roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
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]());
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]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     }
2827     
2828     
2829     
2830 });
2831
2832
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * navbar
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.NavSimplebar
2845  * @extends Roo.bootstrap.Navbar
2846  * Bootstrap Sidebar class
2847  *
2848  * @cfg {Boolean} inverse is inverted color
2849  * 
2850  * @cfg {String} type (nav | pills | tabs)
2851  * @cfg {Boolean} arrangement stacked | justified
2852  * @cfg {String} align (left | right) alignment
2853  * 
2854  * @cfg {Boolean} main (true|false) main nav bar? default false
2855  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2856  * 
2857  * @cfg {String} tag (header|footer|nav|div) default is nav 
2858
2859  * 
2860  * 
2861  * 
2862  * @constructor
2863  * Create a new Sidebar
2864  * @param {Object} config The config object
2865  */
2866
2867
2868 Roo.bootstrap.NavSimplebar = function(config){
2869     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2870 };
2871
2872 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2873     
2874     inverse: false,
2875     
2876     type: false,
2877     arrangement: '',
2878     align : false,
2879     
2880     
2881     
2882     main : false,
2883     
2884     
2885     tag : false,
2886     
2887     
2888     getAutoCreate : function(){
2889         
2890         
2891         var cfg = {
2892             tag : this.tag || 'div',
2893             cls : 'navbar'
2894         };
2895           
2896         
2897         cfg.cn = [
2898             {
2899                 cls: 'nav',
2900                 tag : 'ul'
2901             }
2902         ];
2903         
2904          
2905         this.type = this.type || 'nav';
2906         if (['tabs','pills'].indexOf(this.type)!==-1) {
2907             cfg.cn[0].cls += ' nav-' + this.type
2908         
2909         
2910         } else {
2911             if (this.type!=='nav') {
2912                 Roo.log('nav type must be nav/tabs/pills')
2913             }
2914             cfg.cn[0].cls += ' navbar-nav'
2915         }
2916         
2917         
2918         
2919         
2920         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2921             cfg.cn[0].cls += ' nav-' + this.arrangement;
2922         }
2923         
2924         
2925         if (this.align === 'right') {
2926             cfg.cn[0].cls += ' navbar-right';
2927         }
2928         
2929         if (this.inverse) {
2930             cfg.cls += ' navbar-inverse';
2931             
2932         }
2933         
2934         
2935         return cfg;
2936     
2937         
2938     }
2939     
2940     
2941     
2942 });
2943
2944
2945
2946  
2947
2948  
2949        /*
2950  * - LGPL
2951  *
2952  * navbar
2953  * 
2954  */
2955
2956 /**
2957  * @class Roo.bootstrap.NavHeaderbar
2958  * @extends Roo.bootstrap.NavSimplebar
2959  * Bootstrap Sidebar class
2960  *
2961  * @cfg {String} brand what is brand
2962  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2963  * @cfg {String} brand_href href of the brand
2964  * 
2965  * @constructor
2966  * Create a new Sidebar
2967  * @param {Object} config The config object
2968  */
2969
2970
2971 Roo.bootstrap.NavHeaderbar = function(config){
2972     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2973 };
2974
2975 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2976     
2977     position: '',
2978     brand: '',
2979     brand_href: false,
2980     
2981     
2982     getAutoCreate : function(){
2983         
2984         
2985         
2986         var   cfg = {
2987             tag: this.nav || 'nav',
2988             cls: 'navbar',
2989             role: 'navigation',
2990             cn: [
2991                 {
2992                     tag: 'div',
2993                     cls: 'navbar-header',
2994                     cn: [
2995                         {
2996                         tag: 'button',
2997                         type: 'button',
2998                         cls: 'navbar-toggle',
2999                         'data-toggle': 'collapse',
3000                         cn: [
3001                             {
3002                                 tag: 'span',
3003                                 cls: 'sr-only',
3004                                 html: 'Toggle navigation'
3005                             },
3006                             {
3007                                 tag: 'span',
3008                                 cls: 'icon-bar'
3009                             },
3010                             {
3011                                 tag: 'span',
3012                                 cls: 'icon-bar'
3013                             },
3014                             {
3015                                 tag: 'span',
3016                                 cls: 'icon-bar'
3017                             }
3018                         ]
3019                         }
3020                     ]
3021                 },
3022                 {
3023                 tag: 'div',
3024                 cls: 'collapse navbar-collapse'
3025                 }
3026             ]
3027         };
3028         
3029         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3030         
3031         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3032             cfg.cls += ' navbar-' + this.position;
3033             
3034             // tag can override this..
3035             
3036             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3037         }
3038         
3039         if (this.brand !== '') {
3040             cfg.cn[0].cn.push({
3041                 tag: 'a',
3042                 href: this.brand_href ? this.brand_href : '#',
3043                 cls: 'navbar-brand',
3044                 cn: [
3045                 this.brand
3046                 ]
3047             });
3048         }
3049         
3050         if(this.main){
3051             cfg.cls += ' main-nav';
3052         }
3053         
3054         
3055         return cfg;
3056
3057         
3058     }
3059     
3060     
3061     
3062 });
3063
3064
3065
3066  
3067
3068  /*
3069  * - LGPL
3070  *
3071  * navbar
3072  * 
3073  */
3074
3075 /**
3076  * @class Roo.bootstrap.NavSidebar
3077  * @extends Roo.bootstrap.Navbar
3078  * Bootstrap Sidebar class
3079  * 
3080  * @constructor
3081  * Create a new Sidebar
3082  * @param {Object} config The config object
3083  */
3084
3085
3086 Roo.bootstrap.NavSidebar = function(config){
3087     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3088 };
3089
3090 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3091     
3092     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3093     
3094     getAutoCreate : function(){
3095         
3096         
3097         return  {
3098             tag: 'div',
3099             cls: 'sidebar sidebar-nav'
3100         };
3101     
3102         
3103     }
3104     
3105     
3106     
3107 });
3108
3109
3110
3111  
3112
3113  /*
3114  * - LGPL
3115  *
3116  * nav group
3117  * 
3118  */
3119
3120 /**
3121  * @class Roo.bootstrap.NavGroup
3122  * @extends Roo.bootstrap.Component
3123  * Bootstrap NavGroup class
3124  * @cfg {String} align left | right
3125  * @cfg {Boolean} inverse false | true
3126  * @cfg {String} type (nav|pills|tab) default nav
3127  * @cfg {String} navId - reference Id for navbar.
3128
3129  * 
3130  * @constructor
3131  * Create a new nav group
3132  * @param {Object} config The config object
3133  */
3134
3135 Roo.bootstrap.NavGroup = function(config){
3136     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3137     this.navItems = [];
3138     Roo.bootstrap.NavGroup.register(this);
3139      this.addEvents({
3140         /**
3141              * @event changed
3142              * Fires when the active item changes
3143              * @param {Roo.bootstrap.NavGroup} this
3144              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3145              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3146          */
3147         'changed': true
3148      });
3149     
3150 };
3151
3152 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3153     
3154     align: '',
3155     inverse: false,
3156     form: false,
3157     type: 'nav',
3158     navId : '',
3159     // private
3160     
3161     navItems : false,
3162     
3163     getAutoCreate : function()
3164     {
3165         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3166         
3167         cfg = {
3168             tag : 'ul',
3169             cls: 'nav' 
3170         }
3171         
3172         if (['tabs','pills'].indexOf(this.type)!==-1) {
3173             cfg.cls += ' nav-' + this.type
3174         } else {
3175             if (this.type!=='nav') {
3176                 Roo.log('nav type must be nav/tabs/pills')
3177             }
3178             cfg.cls += ' navbar-nav'
3179         }
3180         
3181         if (this.parent().sidebar) {
3182             cfg = {
3183                 tag: 'ul',
3184                 cls: 'dashboard-menu sidebar-menu'
3185             }
3186             
3187             return cfg;
3188         }
3189         
3190         if (this.form === true) {
3191             cfg = {
3192                 tag: 'form',
3193                 cls: 'navbar-form'
3194             }
3195             
3196             if (this.align === 'right') {
3197                 cfg.cls += ' navbar-right';
3198             } else {
3199                 cfg.cls += ' navbar-left';
3200             }
3201         }
3202         
3203         if (this.align === 'right') {
3204             cfg.cls += ' navbar-right';
3205         }
3206         
3207         if (this.inverse) {
3208             cfg.cls += ' navbar-inverse';
3209             
3210         }
3211         
3212         
3213         return cfg;
3214     },
3215     
3216     setActiveItem : function(item)
3217     {
3218         var prev = false;
3219         Roo.each(this.navItems, function(v){
3220             if (v == item) {
3221                 return ;
3222             }
3223             if (v.isActive()) {
3224                 v.setActive(false, true);
3225                 prev = v;
3226                 
3227             }
3228             
3229         });
3230
3231         item.setActive(true, true);
3232         this.fireEvent('changed', this, item, prev);
3233         
3234         
3235     },
3236     
3237     
3238     register : function(item)
3239     {
3240         this.navItems.push( item);
3241         item.navId = this.navId;
3242     
3243     },
3244     getNavItem: function(tabId)
3245     {
3246         var ret = false;
3247         Roo.each(this.navItems, function(e) {
3248             if (e.tabId == tabId) {
3249                ret =  e;
3250                return false;
3251             }
3252             return true;
3253             
3254         });
3255         return ret;
3256     }
3257 });
3258
3259  
3260 Roo.apply(Roo.bootstrap.NavGroup, {
3261     
3262     groups: {},
3263     
3264     register : function(navgrp)
3265     {
3266         this.groups[navgrp.navId] = navgrp;
3267         
3268     },
3269     get: function(navId) {
3270         return this.groups[navId];
3271     }
3272     
3273     
3274     
3275 });
3276
3277  /*
3278  * - LGPL
3279  *
3280  * row
3281  * 
3282  */
3283
3284 /**
3285  * @class Roo.bootstrap.Navbar.Item
3286  * @extends Roo.bootstrap.Component
3287  * Bootstrap Navbar.Button class
3288  * @cfg {String} href  link to
3289  * @cfg {String} html content of button
3290  * @cfg {String} badge text inside badge
3291  * @cfg {String} glyphicon name of glyphicon
3292  * @cfg {String} icon name of font awesome icon
3293  * @cfg {Boolean} active Is item active
3294  * @cfg {Boolean} preventDefault (true | false) default false
3295  * @cfg {String} tabId the tab that this item activates.
3296   
3297  * @constructor
3298  * Create a new Navbar Button
3299  * @param {Object} config The config object
3300  */
3301 Roo.bootstrap.Navbar.Item = function(config){
3302     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3303     this.addEvents({
3304         // raw events
3305         /**
3306          * @event click
3307          * The raw click event for the entire grid.
3308          * @param {Roo.EventObject} e
3309          */
3310         "click" : true,
3311          /**
3312             * @event changed
3313             * Fires when the active item active state changes
3314             * @param {Roo.bootstrap.Navbar.Item} this
3315             * @param {boolean} state the new state
3316              
3317          */
3318         'changed': true
3319     });
3320    
3321 };
3322
3323 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3324     
3325     href: false,
3326     html: '',
3327     badge: '',
3328     icon: false,
3329     glyphicon: false,
3330     active: false,
3331     preventDefault : false,
3332     tabId : false,
3333     
3334     getAutoCreate : function(){
3335         
3336         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3337         
3338         if (this.parent().parent().sidebar === true) {
3339             cfg = {
3340                 tag: 'li',
3341                 cls: '',
3342                 cn: [
3343                     {
3344                     tag: 'p',
3345                     cls: ''
3346                     }
3347                 ]
3348             }
3349             
3350             if (this.html) {
3351                 cfg.cn[0].html = this.html;
3352             }
3353             
3354             if (this.active) {
3355                 this.cls += ' active';
3356             }
3357             
3358             if (this.menu) {
3359                 cfg.cn[0].cls += ' dropdown-toggle';
3360                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3361             }
3362             
3363             if (this.href) {
3364                 cfg.cn[0].tag = 'a',
3365                 cfg.cn[0].href = this.href;
3366             }
3367             
3368             if (this.glyphicon) {
3369                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3370             }
3371                 
3372             if (this.icon) {
3373                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3374             }
3375             
3376             return cfg;
3377         }
3378         
3379         cfg = {
3380             tag: 'li',
3381                 cls: 'nav-item'
3382         }
3383             
3384         if (this.active) {
3385             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3386         }
3387             
3388         cfg.cn = [
3389             {
3390                 tag: 'p',
3391                 html: 'Text'
3392             }
3393         ];
3394         
3395         if (this.glyphicon) {
3396             if(cfg.html){cfg.html = ' ' + this.html};
3397             cfg.cn=[
3398                 {
3399                     tag: 'span',
3400                     cls: 'glyphicon glyphicon-' + this.glyphicon
3401                 }
3402             ];
3403         }
3404         
3405         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3406         
3407         if (this.menu) {
3408             cfg.cn[0].tag='a';
3409             cfg.cn[0].href='#';
3410             cfg.cn[0].html += " <span class='caret'></span>";
3411         //}else if (!this.href) {
3412         //    cfg.cn[0].tag='p';
3413         //    cfg.cn[0].cls='navbar-text';
3414         } else {
3415             cfg.cn[0].tag='a';
3416             cfg.cn[0].href=this.href||'#';
3417             cfg.cn[0].html=this.html;
3418         }
3419         
3420         if (this.badge !== '') {
3421             
3422             cfg.cn[0].cn=[
3423             cfg.cn[0].html + ' ',
3424             {
3425                 tag: 'span',
3426                 cls: 'badge',
3427                 html: this.badge
3428             }
3429             ];
3430             cfg.cn[0].html=''
3431         }
3432          
3433         if (this.icon) {
3434             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3435         }
3436         
3437         return cfg;
3438     },
3439     initEvents: function() {
3440        // Roo.log('init events?');
3441        // Roo.log(this.el.dom);
3442         this.el.select('a',true).on('click', this.onClick, this);
3443         // at this point parent should be available..
3444         this.parent().register(this);
3445     },
3446     
3447     onClick : function(e)
3448     {
3449         if(this.preventDefault){
3450             e.preventDefault();
3451         }
3452         
3453         if (typeof (this.menu) != 'undefined') {
3454             this.menu.parentType = this.xtype;
3455             this.menu.triggerEl = this.el;
3456             this.addxtype(Roo.apply({}, this.menu));
3457         }
3458         
3459         if(this.fireEvent('click', this, e) === false){
3460             return;
3461         };
3462         
3463         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3464              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3465                 this.parent().setActiveItem(this);
3466             }
3467             
3468         } 
3469     },
3470     
3471     isActive: function () {
3472         return this.active
3473     },
3474     setActive : function(state, fire)
3475     {
3476         this.active = state;
3477         if (!state ) {
3478             this.el.removeClass('active');
3479         } else if (!this.el.hasClass('active')) {
3480             this.el.addClass('active');
3481         }
3482         if (fire) {
3483             this.fireEvent('changed', this, state);
3484         }
3485         
3486         
3487     }
3488      // this should not be here...
3489  
3490 });
3491  
3492
3493  /*
3494  * - LGPL
3495  *
3496  * row
3497  * 
3498  */
3499
3500 /**
3501  * @class Roo.bootstrap.NavItem
3502  * @extends Roo.bootstrap.Component
3503  * Bootstrap Navbar.NavItem class
3504  * @cfg {String} href  link to
3505  * @cfg {String} html content of button
3506  * @cfg {String} badge text inside badge
3507  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3508  * @cfg {String} glyphicon name of glyphicon
3509  * @cfg {String} icon name of font awesome icon
3510  * @cfg {Boolean} active Is item active
3511  * @cfg {Boolean} preventDefault (true | false) default false
3512  * @cfg {String} tabId the tab that this item activates.
3513   
3514  * @constructor
3515  * Create a new Navbar Item
3516  * @param {Object} config The config object
3517  */
3518 Roo.bootstrap.NavItem = function(config){
3519     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3520     this.addEvents({
3521         // raw events
3522         /**
3523          * @event click
3524          * The raw click event for the entire grid.
3525          * @param {Roo.EventObject} e
3526          */
3527         "click" : true,
3528          /**
3529             * @event changed
3530             * Fires when the active item active state changes
3531             * @param {Roo.bootstrap.NavItem} this
3532             * @param {boolean} state the new state
3533              
3534          */
3535         'changed': true
3536     });
3537    
3538 };
3539
3540 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3541     
3542     href: false,
3543     html: '',
3544     badge: '',
3545     icon: false,
3546     glyphicon: false,
3547     active: false,
3548     preventDefault : false,
3549     tabId : false,
3550     
3551     getAutoCreate : function(){
3552          
3553         var cfg = {
3554             tag: 'li',
3555             cls: 'nav-item',
3556             cn : [
3557                 {
3558                     tag: 'a',
3559                     href : this.href || "#",
3560                     html: this.html || ''
3561                 }
3562             ]
3563         }
3564             
3565         if (this.active) {
3566             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3567         }
3568             
3569         // glyphicon and icon go before content..
3570         if (this.glyphicon || this.icon) {
3571              if (this.icon) {
3572                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3573             } else {
3574                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>'  + cfg.cn[0].html;
3575             }
3576         }
3577         
3578         
3579         
3580         if (this.menu) {
3581             
3582             cfg.cn[0].html += " <span class='caret'></span>";
3583          
3584         }
3585         
3586         if (this.badge !== '') {
3587              
3588             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3589         }
3590         
3591         
3592         
3593         return cfg;
3594     },
3595     initEvents: function() {
3596        // Roo.log('init events?');
3597        // Roo.log(this.el.dom);
3598        if (typeof (this.menu) != 'undefined') {
3599             this.menu.parentType = this.xtype;
3600             this.menu.triggerEl = this.el;
3601             this.addxtype(Roo.apply({}, this.menu));
3602         }
3603
3604        
3605         this.el.select('a',true).on('click', this.onClick, this);
3606         // at this point parent should be available..
3607         this.parent().register(this);
3608     },
3609     
3610     onClick : function(e)
3611     {
3612         if(this.preventDefault){
3613             e.preventDefault();
3614         }
3615         
3616         if(this.fireEvent('click', this, e) === false){
3617             return;
3618         };
3619         
3620         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3621              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3622                 this.parent().setActiveItem(this);
3623             }
3624             
3625             
3626             
3627         } 
3628     },
3629     
3630     isActive: function () {
3631         return this.active
3632     },
3633     setActive : function(state, fire)
3634     {
3635         this.active = state;
3636         if (!state ) {
3637             this.el.removeClass('active');
3638         } else if (!this.el.hasClass('active')) {
3639             this.el.addClass('active');
3640         }
3641         if (fire) {
3642             this.fireEvent('changed', this, state);
3643         }
3644         
3645         
3646     }
3647      // this should not be here...
3648  
3649 });
3650  
3651
3652  /*
3653  * - LGPL
3654  *
3655  * sidebar item
3656  *
3657  *  li
3658  *    <span> icon </span>
3659  *    <span> text </span>
3660  *    <span>badge </span>
3661  */
3662
3663 /**
3664  * @class Roo.bootstrap.NavSidebarItem
3665  * @extends Roo.bootstrap.NavItem
3666  * Bootstrap Navbar.NavSidebarItem class
3667  * @constructor
3668  * Create a new Navbar Button
3669  * @param {Object} config The config object
3670  */
3671 Roo.bootstrap.NavSidebarItem = function(config){
3672     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3673     this.addEvents({
3674         // raw events
3675         /**
3676          * @event click
3677          * The raw click event for the entire grid.
3678          * @param {Roo.EventObject} e
3679          */
3680         "click" : true,
3681          /**
3682             * @event changed
3683             * Fires when the active item active state changes
3684             * @param {Roo.bootstrap.NavSidebarItem} this
3685             * @param {boolean} state the new state
3686              
3687          */
3688         'changed': true
3689     });
3690    
3691 };
3692
3693 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3694     
3695     
3696     getAutoCreate : function(){
3697         
3698         
3699         var a = {
3700                 tag: 'a',
3701                 href : this.href || '#',
3702                 cls: '',
3703                 html : '',
3704                 cn : []
3705         };
3706         var cfg = {
3707             tag: 'li',
3708             cls: '',
3709             cn: [ a ]
3710         }
3711         var span = {
3712             tag: 'span',
3713             html : this.html || ''
3714         }
3715         
3716         
3717         if (this.active) {
3718             cfg.cls += ' active';
3719         }
3720         
3721         // left icon..
3722         if (this.glyphicon || this.icon) {
3723             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3724             a.cn.push({ tag : 'i', cls : c }) ;
3725         }
3726         // html..
3727         a.cn.push(span);
3728         // then badge..
3729         if (this.badge !== '') {
3730             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3731         }
3732         // fi
3733         if (this.menu) {
3734             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3735             a.cls += 'dropdown-toggle treeview' ;
3736             
3737         }
3738         
3739         
3740         
3741         return cfg;
3742          
3743            
3744     }
3745    
3746      
3747  
3748 });
3749  
3750
3751  /*
3752  * - LGPL
3753  *
3754  * row
3755  * 
3756  */
3757
3758 /**
3759  * @class Roo.bootstrap.Row
3760  * @extends Roo.bootstrap.Component
3761  * Bootstrap Row class (contains columns...)
3762  * 
3763  * @constructor
3764  * Create a new Row
3765  * @param {Object} config The config object
3766  */
3767
3768 Roo.bootstrap.Row = function(config){
3769     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3770 };
3771
3772 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3773     
3774     getAutoCreate : function(){
3775        return {
3776             cls: 'row clearfix'
3777        };
3778     }
3779     
3780     
3781 });
3782
3783  
3784
3785  /*
3786  * - LGPL
3787  *
3788  * element
3789  * 
3790  */
3791
3792 /**
3793  * @class Roo.bootstrap.Element
3794  * @extends Roo.bootstrap.Component
3795  * Bootstrap Element class
3796  * @cfg {String} html contents of the element
3797  * @cfg {String} tag tag of the element
3798  * @cfg {String} cls class of the element
3799  * 
3800  * @constructor
3801  * Create a new Element
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Element = function(config){
3806     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3810     
3811     tag: 'div',
3812     cls: '',
3813     html: '',
3814      
3815     
3816     getAutoCreate : function(){
3817         
3818         var cfg = {
3819             tag: this.tag,
3820             cls: this.cls,
3821             html: this.html
3822         }
3823         
3824         
3825         
3826         return cfg;
3827     }
3828    
3829 });
3830
3831  
3832
3833  /*
3834  * - LGPL
3835  *
3836  * pagination
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Pagination
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Pagination class
3844  * @cfg {String} size xs | sm | md | lg
3845  * @cfg {Boolean} inverse false | true
3846  * 
3847  * @constructor
3848  * Create a new Pagination
3849  * @param {Object} config The config object
3850  */
3851
3852 Roo.bootstrap.Pagination = function(config){
3853     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3857     
3858     cls: false,
3859     size: false,
3860     inverse: false,
3861     
3862     getAutoCreate : function(){
3863         var cfg = {
3864             tag: 'ul',
3865                 cls: 'pagination'
3866         };
3867         if (this.inverse) {
3868             cfg.cls += ' inverse';
3869         }
3870         if (this.html) {
3871             cfg.html=this.html;
3872         }
3873         if (this.cls) {
3874             cfg.cls += " " + this.cls;
3875         }
3876         return cfg;
3877     }
3878    
3879 });
3880
3881  
3882
3883  /*
3884  * - LGPL
3885  *
3886  * Pagination item
3887  * 
3888  */
3889
3890
3891 /**
3892  * @class Roo.bootstrap.PaginationItem
3893  * @extends Roo.bootstrap.Component
3894  * Bootstrap PaginationItem class
3895  * @cfg {String} html text
3896  * @cfg {String} href the link
3897  * @cfg {Boolean} preventDefault (true | false) default true
3898  * @cfg {Boolean} active (true | false) default false
3899  * 
3900  * 
3901  * @constructor
3902  * Create a new PaginationItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.PaginationItem = function(config){
3908     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3909     this.addEvents({
3910         // raw events
3911         /**
3912          * @event click
3913          * The raw click event for the entire grid.
3914          * @param {Roo.EventObject} e
3915          */
3916         "click" : true
3917     });
3918 };
3919
3920 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3921     
3922     href : false,
3923     html : false,
3924     preventDefault: true,
3925     active : false,
3926     cls : false,
3927     
3928     getAutoCreate : function(){
3929         var cfg= {
3930             tag: 'li',
3931             cn: [
3932                 {
3933                     tag : 'a',
3934                     href : this.href ? this.href : '#',
3935                     html : this.html ? this.html : ''
3936                 }
3937             ]
3938         };
3939         
3940         if(this.cls){
3941             cfg.cls = this.cls;
3942         }
3943         
3944         if(this.active){
3945             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3946         }
3947         
3948         return cfg;
3949     },
3950     
3951     initEvents: function() {
3952         
3953         this.el.on('click', this.onClick, this);
3954         
3955     },
3956     onClick : function(e)
3957     {
3958         Roo.log('PaginationItem on click ');
3959         if(this.preventDefault){
3960             e.preventDefault();
3961         }
3962         
3963         this.fireEvent('click', this, e);
3964     }
3965    
3966 });
3967
3968  
3969
3970  /*
3971  * - LGPL
3972  *
3973  * slider
3974  * 
3975  */
3976
3977
3978 /**
3979  * @class Roo.bootstrap.Slider
3980  * @extends Roo.bootstrap.Component
3981  * Bootstrap Slider class
3982  *    
3983  * @constructor
3984  * Create a new Slider
3985  * @param {Object} config The config object
3986  */
3987
3988 Roo.bootstrap.Slider = function(config){
3989     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3993     
3994     getAutoCreate : function(){
3995         
3996         var cfg = {
3997             tag: 'div',
3998             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3999             cn: [
4000                 {
4001                     tag: 'a',
4002                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4003                 }
4004             ]
4005         }
4006         
4007         return cfg;
4008     }
4009    
4010 });
4011
4012  /*
4013  * - LGPL
4014  *
4015  * table
4016  * 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.Table
4021  * @extends Roo.bootstrap.Component
4022  * Bootstrap Table class
4023  * @cfg {String} cls table class
4024  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4025  * @cfg {String} bgcolor Specifies the background color for a table
4026  * @cfg {Number} border Specifies whether the table cells should have borders or not
4027  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4028  * @cfg {Number} cellspacing Specifies the space between cells
4029  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4030  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4031  * @cfg {String} sortable Specifies that the table should be sortable
4032  * @cfg {String} summary Specifies a summary of the content of a table
4033  * @cfg {Number} width Specifies the width of a table
4034  * 
4035  * @cfg {boolean} striped Should the rows be alternative striped
4036  * @cfg {boolean} bordered Add borders to the table
4037  * @cfg {boolean} hover Add hover highlighting
4038  * @cfg {boolean} condensed Format condensed
4039  * @cfg {boolean} responsive Format condensed
4040  *
4041  
4042  
4043  * 
4044  * @constructor
4045  * Create a new Table
4046  * @param {Object} config The config object
4047  */
4048
4049 Roo.bootstrap.Table = function(config){
4050     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4051     
4052     if (this.sm) {
4053         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4054         this.sm = this.selModel;
4055         this.sm.xmodule = this.xmodule || false;
4056     }
4057     if (this.cm && typeof(this.cm.config) == 'undefined') {
4058         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4059         this.cm = this.colModel;
4060         this.cm.xmodule = this.xmodule || false;
4061     }
4062     if (this.store) {
4063         this.store= Roo.factory(this.store, Roo.data);
4064         this.ds = this.store;
4065         this.ds.xmodule = this.xmodule || false;
4066          
4067     }
4068 };
4069
4070 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4071     
4072     cls: false,
4073     align: false,
4074     bgcolor: false,
4075     border: false,
4076     cellpadding: false,
4077     cellspacing: false,
4078     frame: false,
4079     rules: false,
4080     sortable: false,
4081     summary: false,
4082     width: false,
4083     striped : false,
4084     bordered: false,
4085     hover:  false,
4086     condensed : false,
4087     responsive : false,
4088     sm : false,
4089     cm : false,
4090     store : false,
4091     
4092     getAutoCreate : function(){
4093         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4094         
4095         cfg = {
4096             tag: 'table',
4097             cls : 'table',
4098             cn : []
4099         }
4100             
4101         if (this.striped) {
4102             cfg.cls += ' table-striped';
4103         }
4104         if (this.hover) {
4105             cfg.cls += ' table-hover';
4106         }
4107         if (this.bordered) {
4108             cfg.cls += ' table-bordered';
4109         }
4110         if (this.condensed) {
4111             cfg.cls += ' table-condensed';
4112         }
4113         if (this.responsive) {
4114             cfg.cls += ' table-responsive';
4115         }
4116         
4117           
4118         
4119         
4120         if (this.cls) {
4121             cfg.cls+=  ' ' +this.cls;
4122         }
4123         
4124         // this lot should be simplifed...
4125         
4126         if (this.align) {
4127             cfg.align=this.align;
4128         }
4129         if (this.bgcolor) {
4130             cfg.bgcolor=this.bgcolor;
4131         }
4132         if (this.border) {
4133             cfg.border=this.border;
4134         }
4135         if (this.cellpadding) {
4136             cfg.cellpadding=this.cellpadding;
4137         }
4138         if (this.cellspacing) {
4139             cfg.cellspacing=this.cellspacing;
4140         }
4141         if (this.frame) {
4142             cfg.frame=this.frame;
4143         }
4144         if (this.rules) {
4145             cfg.rules=this.rules;
4146         }
4147         if (this.sortable) {
4148             cfg.sortable=this.sortable;
4149         }
4150         if (this.summary) {
4151             cfg.summary=this.summary;
4152         }
4153         if (this.width) {
4154             cfg.width=this.width;
4155         }
4156         
4157         if(this.store || this.cm){
4158             cfg.cn.push(this.renderHeader());
4159             cfg.cn.push(this.renderBody());
4160             cfg.cn.push(this.renderFooter());
4161             
4162             cfg.cls+=  ' TableGrid';
4163         }
4164         
4165         return cfg;
4166     },
4167 //    
4168 //    initTableGrid : function()
4169 //    {
4170 //        var cfg = {};
4171 //        
4172 //        var header = {
4173 //            tag: 'thead',
4174 //            cn : []
4175 //        };
4176 //        
4177 //        var cm = this.cm;
4178 //        
4179 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4180 //            header.cn.push({
4181 //                tag: 'th',
4182 //                html: cm.getColumnHeader(i)
4183 //            })
4184 //        }
4185 //        
4186 //        cfg.push(header);
4187 //        
4188 //        return cfg;
4189 //        
4190 //        
4191 //    },
4192     
4193     initEvents : function()
4194     {   
4195         if(!this.store || !this.cm){
4196             return;
4197         }
4198         
4199         Roo.log('initEvents with ds!!!!');
4200         
4201         var _this = this;
4202         
4203         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4204             e.on('click', _this.sort, _this);
4205         });
4206 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4207 //        this.maskEl.enableDisplayMode("block");
4208 //        this.maskEl.show();
4209         
4210         this.store.on('load', this.onLoad, this);
4211         this.store.on('beforeload', this.onBeforeLoad, this);
4212         
4213         this.store.load();
4214         
4215         
4216         
4217     },
4218     
4219     sort : function(e,el)
4220     {
4221         var col = Roo.get(el)
4222         
4223         if(!col.hasClass('sortable')){
4224             return;
4225         }
4226         
4227         var sort = col.attr('sort');
4228         var dir = 'ASC';
4229         
4230         if(col.hasClass('glyphicon-arrow-up')){
4231             dir = 'DESC';
4232         }
4233         
4234         this.store.sortInfo = {field : sort, direction : dir};
4235         
4236         this.store.load();
4237     },
4238     
4239     renderHeader : function()
4240     {
4241         var header = {
4242             tag: 'thead',
4243             cn : []
4244         };
4245         
4246         var cm = this.cm;
4247         
4248         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4249             
4250             var config = cm.config[i];
4251             
4252             var c = {
4253                 tag: 'th',
4254                 html: cm.getColumnHeader(i)
4255             };
4256             
4257             if(typeof(config.dataIndex) != 'undefined'){
4258                 c.sort = config.dataIndex;
4259             }
4260             
4261             if(typeof(config.sortable) != 'undefined' && config.sortable){
4262                 c.cls = 'sortable';
4263             }
4264             
4265             if(typeof(config.width) != 'undefined'){
4266                 c.style = 'width:' + config.width + 'px';
4267             }
4268             
4269             header.cn.push(c)
4270         }
4271         
4272         return header;
4273     },
4274     
4275     renderBody : function()
4276     {
4277         var body = {
4278             tag: 'tbody',
4279             cn : []
4280         };
4281         
4282         return body;
4283     },
4284     
4285     renderFooter : function()
4286     {
4287         var footer = {
4288             tag: 'tfoot',
4289             cn : []
4290         };
4291         
4292         return footer;
4293     },
4294     
4295     onLoad : function()
4296     {
4297         Roo.log('ds onload');
4298         
4299         var _this = this;
4300         var cm = this.cm;
4301         
4302         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4303             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4304             
4305             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4306                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4307             }
4308             
4309             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4310                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4311             }
4312         });
4313         
4314         var tbody = this.el.select('tbody', true).first();
4315         
4316         var renders = [];
4317         
4318         if(this.store.getCount() > 0){
4319             this.store.data.each(function(d){
4320                 var row = {
4321                     tag : 'tr',
4322                     cn : []
4323                 };
4324                 
4325                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4326                     var renderer = cm.getRenderer(i);
4327                     var config = cm.config[i];
4328                     var value = '';
4329                     var id = Roo.id();
4330                     
4331                     if(typeof(renderer) !== 'undefined'){
4332                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4333                     }
4334                     
4335                     if(typeof(value) === 'object'){
4336                         renders.push({
4337                             id : id,
4338                             cfg : value 
4339                         })
4340                     }
4341                     
4342                     var td = {
4343                         tag: 'td',
4344                         id: id,
4345                         html: (typeof(value) === 'object') ? '' : value
4346                     };
4347                     
4348                     if(typeof(config.width) != 'undefined'){
4349                         td.style = 'width:' +  config.width + 'px';
4350                     }
4351                     
4352                     row.cn.push(td);
4353                    
4354                 }
4355                 
4356                 tbody.createChild(row);
4357                 
4358             });
4359         }
4360         
4361         
4362         if(renders.length){
4363             var _this = this;
4364             Roo.each(renders, function(r){
4365                 _this.renderColumn(r);
4366             })
4367         }
4368 //        
4369 //        if(this.loadMask){
4370 //            this.maskEl.hide();
4371 //        }
4372     },
4373     
4374     onBeforeLoad : function()
4375     {
4376         Roo.log('ds onBeforeLoad');
4377         
4378         this.clear();
4379         
4380 //        if(this.loadMask){
4381 //            this.maskEl.show();
4382 //        }
4383     },
4384     
4385     clear : function()
4386     {
4387         this.el.select('tbody', true).first().dom.innerHTML = '';
4388     },
4389     
4390     getSelectionModel : function(){
4391         if(!this.selModel){
4392             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4393         }
4394         return this.selModel;
4395     },
4396     
4397     renderColumn : function(r)
4398     {
4399         var _this = this;
4400         r.cfg.render(Roo.get(r.id));
4401         
4402         if(r.cfg.cn){
4403             Roo.each(r.cfg.cn, function(c){
4404                 var child = {
4405                     id: r.id,
4406                     cfg: c
4407                 }
4408                 _this.renderColumn(child);
4409             })
4410         }
4411     }
4412    
4413 });
4414
4415  
4416
4417  /*
4418  * - LGPL
4419  *
4420  * table cell
4421  * 
4422  */
4423
4424 /**
4425  * @class Roo.bootstrap.TableCell
4426  * @extends Roo.bootstrap.Component
4427  * Bootstrap TableCell class
4428  * @cfg {String} html cell contain text
4429  * @cfg {String} cls cell class
4430  * @cfg {String} tag cell tag (td|th) default td
4431  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4432  * @cfg {String} align Aligns the content in a cell
4433  * @cfg {String} axis Categorizes cells
4434  * @cfg {String} bgcolor Specifies the background color of a cell
4435  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4436  * @cfg {Number} colspan Specifies the number of columns a cell should span
4437  * @cfg {String} headers Specifies one or more header cells a cell is related to
4438  * @cfg {Number} height Sets the height of a cell
4439  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4440  * @cfg {Number} rowspan Sets the number of rows a cell should span
4441  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4442  * @cfg {String} valign Vertical aligns the content in a cell
4443  * @cfg {Number} width Specifies the width of a cell
4444  * 
4445  * @constructor
4446  * Create a new TableCell
4447  * @param {Object} config The config object
4448  */
4449
4450 Roo.bootstrap.TableCell = function(config){
4451     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4452 };
4453
4454 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4455     
4456     html: false,
4457     cls: false,
4458     tag: false,
4459     abbr: false,
4460     align: false,
4461     axis: false,
4462     bgcolor: false,
4463     charoff: false,
4464     colspan: false,
4465     headers: false,
4466     height: false,
4467     nowrap: false,
4468     rowspan: false,
4469     scope: false,
4470     valign: false,
4471     width: false,
4472     
4473     
4474     getAutoCreate : function(){
4475         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4476         
4477         cfg = {
4478             tag: 'td'
4479         }
4480         
4481         if(this.tag){
4482             cfg.tag = this.tag;
4483         }
4484         
4485         if (this.html) {
4486             cfg.html=this.html
4487         }
4488         if (this.cls) {
4489             cfg.cls=this.cls
4490         }
4491         if (this.abbr) {
4492             cfg.abbr=this.abbr
4493         }
4494         if (this.align) {
4495             cfg.align=this.align
4496         }
4497         if (this.axis) {
4498             cfg.axis=this.axis
4499         }
4500         if (this.bgcolor) {
4501             cfg.bgcolor=this.bgcolor
4502         }
4503         if (this.charoff) {
4504             cfg.charoff=this.charoff
4505         }
4506         if (this.colspan) {
4507             cfg.colspan=this.colspan
4508         }
4509         if (this.headers) {
4510             cfg.headers=this.headers
4511         }
4512         if (this.height) {
4513             cfg.height=this.height
4514         }
4515         if (this.nowrap) {
4516             cfg.nowrap=this.nowrap
4517         }
4518         if (this.rowspan) {
4519             cfg.rowspan=this.rowspan
4520         }
4521         if (this.scope) {
4522             cfg.scope=this.scope
4523         }
4524         if (this.valign) {
4525             cfg.valign=this.valign
4526         }
4527         if (this.width) {
4528             cfg.width=this.width
4529         }
4530         
4531         
4532         return cfg;
4533     }
4534    
4535 });
4536
4537  
4538
4539  /*
4540  * - LGPL
4541  *
4542  * table row
4543  * 
4544  */
4545
4546 /**
4547  * @class Roo.bootstrap.TableRow
4548  * @extends Roo.bootstrap.Component
4549  * Bootstrap TableRow class
4550  * @cfg {String} cls row class
4551  * @cfg {String} align Aligns the content in a table row
4552  * @cfg {String} bgcolor Specifies a background color for a table row
4553  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4554  * @cfg {String} valign Vertical aligns the content in a table row
4555  * 
4556  * @constructor
4557  * Create a new TableRow
4558  * @param {Object} config The config object
4559  */
4560
4561 Roo.bootstrap.TableRow = function(config){
4562     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4563 };
4564
4565 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4566     
4567     cls: false,
4568     align: false,
4569     bgcolor: false,
4570     charoff: false,
4571     valign: false,
4572     
4573     getAutoCreate : function(){
4574         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4575         
4576         cfg = {
4577             tag: 'tr'
4578         }
4579             
4580         if(this.cls){
4581             cfg.cls = this.cls;
4582         }
4583         if(this.align){
4584             cfg.align = this.align;
4585         }
4586         if(this.bgcolor){
4587             cfg.bgcolor = this.bgcolor;
4588         }
4589         if(this.charoff){
4590             cfg.charoff = this.charoff;
4591         }
4592         if(this.valign){
4593             cfg.valign = this.valign;
4594         }
4595         
4596         return cfg;
4597     }
4598    
4599 });
4600
4601  
4602
4603  /*
4604  * - LGPL
4605  *
4606  * table body
4607  * 
4608  */
4609
4610 /**
4611  * @class Roo.bootstrap.TableBody
4612  * @extends Roo.bootstrap.Component
4613  * Bootstrap TableBody class
4614  * @cfg {String} cls element class
4615  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4616  * @cfg {String} align Aligns the content inside the element
4617  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4618  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4619  * 
4620  * @constructor
4621  * Create a new TableBody
4622  * @param {Object} config The config object
4623  */
4624
4625 Roo.bootstrap.TableBody = function(config){
4626     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4627 };
4628
4629 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4630     
4631     cls: false,
4632     tag: false,
4633     align: false,
4634     charoff: false,
4635     valign: false,
4636     
4637     getAutoCreate : function(){
4638         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4639         
4640         cfg = {
4641             tag: 'tbody'
4642         }
4643             
4644         if (this.cls) {
4645             cfg.cls=this.cls
4646         }
4647         if(this.tag){
4648             cfg.tag = this.tag;
4649         }
4650         
4651         if(this.align){
4652             cfg.align = this.align;
4653         }
4654         if(this.charoff){
4655             cfg.charoff = this.charoff;
4656         }
4657         if(this.valign){
4658             cfg.valign = this.valign;
4659         }
4660         
4661         return cfg;
4662     }
4663     
4664     
4665 //    initEvents : function()
4666 //    {
4667 //        
4668 //        if(!this.store){
4669 //            return;
4670 //        }
4671 //        
4672 //        this.store = Roo.factory(this.store, Roo.data);
4673 //        this.store.on('load', this.onLoad, this);
4674 //        
4675 //        this.store.load();
4676 //        
4677 //    },
4678 //    
4679 //    onLoad: function () 
4680 //    {   
4681 //        this.fireEvent('load', this);
4682 //    }
4683 //    
4684 //   
4685 });
4686
4687  
4688
4689  /*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700 // as we use this in bootstrap.
4701 Roo.namespace('Roo.form');
4702  /**
4703  * @class Roo.form.Action
4704  * Internal Class used to handle form actions
4705  * @constructor
4706  * @param {Roo.form.BasicForm} el The form element or its id
4707  * @param {Object} config Configuration options
4708  */
4709
4710  
4711  
4712 // define the action interface
4713 Roo.form.Action = function(form, options){
4714     this.form = form;
4715     this.options = options || {};
4716 };
4717 /**
4718  * Client Validation Failed
4719  * @const 
4720  */
4721 Roo.form.Action.CLIENT_INVALID = 'client';
4722 /**
4723  * Server Validation Failed
4724  * @const 
4725  */
4726 Roo.form.Action.SERVER_INVALID = 'server';
4727  /**
4728  * Connect to Server Failed
4729  * @const 
4730  */
4731 Roo.form.Action.CONNECT_FAILURE = 'connect';
4732 /**
4733  * Reading Data from Server Failed
4734  * @const 
4735  */
4736 Roo.form.Action.LOAD_FAILURE = 'load';
4737
4738 Roo.form.Action.prototype = {
4739     type : 'default',
4740     failureType : undefined,
4741     response : undefined,
4742     result : undefined,
4743
4744     // interface method
4745     run : function(options){
4746
4747     },
4748
4749     // interface method
4750     success : function(response){
4751
4752     },
4753
4754     // interface method
4755     handleResponse : function(response){
4756
4757     },
4758
4759     // default connection failure
4760     failure : function(response){
4761         
4762         this.response = response;
4763         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4764         this.form.afterAction(this, false);
4765     },
4766
4767     processResponse : function(response){
4768         this.response = response;
4769         if(!response.responseText){
4770             return true;
4771         }
4772         this.result = this.handleResponse(response);
4773         return this.result;
4774     },
4775
4776     // utility functions used internally
4777     getUrl : function(appendParams){
4778         var url = this.options.url || this.form.url || this.form.el.dom.action;
4779         if(appendParams){
4780             var p = this.getParams();
4781             if(p){
4782                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4783             }
4784         }
4785         return url;
4786     },
4787
4788     getMethod : function(){
4789         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4790     },
4791
4792     getParams : function(){
4793         var bp = this.form.baseParams;
4794         var p = this.options.params;
4795         if(p){
4796             if(typeof p == "object"){
4797                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4798             }else if(typeof p == 'string' && bp){
4799                 p += '&' + Roo.urlEncode(bp);
4800             }
4801         }else if(bp){
4802             p = Roo.urlEncode(bp);
4803         }
4804         return p;
4805     },
4806
4807     createCallback : function(){
4808         return {
4809             success: this.success,
4810             failure: this.failure,
4811             scope: this,
4812             timeout: (this.form.timeout*1000),
4813             upload: this.form.fileUpload ? this.success : undefined
4814         };
4815     }
4816 };
4817
4818 Roo.form.Action.Submit = function(form, options){
4819     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4820 };
4821
4822 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4823     type : 'submit',
4824
4825     haveProgress : false,
4826     uploadComplete : false,
4827     
4828     // uploadProgress indicator.
4829     uploadProgress : function()
4830     {
4831         if (!this.form.progressUrl) {
4832             return;
4833         }
4834         
4835         if (!this.haveProgress) {
4836             Roo.MessageBox.progress("Uploading", "Uploading");
4837         }
4838         if (this.uploadComplete) {
4839            Roo.MessageBox.hide();
4840            return;
4841         }
4842         
4843         this.haveProgress = true;
4844    
4845         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4846         
4847         var c = new Roo.data.Connection();
4848         c.request({
4849             url : this.form.progressUrl,
4850             params: {
4851                 id : uid
4852             },
4853             method: 'GET',
4854             success : function(req){
4855                //console.log(data);
4856                 var rdata = false;
4857                 var edata;
4858                 try  {
4859                    rdata = Roo.decode(req.responseText)
4860                 } catch (e) {
4861                     Roo.log("Invalid data from server..");
4862                     Roo.log(edata);
4863                     return;
4864                 }
4865                 if (!rdata || !rdata.success) {
4866                     Roo.log(rdata);
4867                     Roo.MessageBox.alert(Roo.encode(rdata));
4868                     return;
4869                 }
4870                 var data = rdata.data;
4871                 
4872                 if (this.uploadComplete) {
4873                    Roo.MessageBox.hide();
4874                    return;
4875                 }
4876                    
4877                 if (data){
4878                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4879                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4880                     );
4881                 }
4882                 this.uploadProgress.defer(2000,this);
4883             },
4884        
4885             failure: function(data) {
4886                 Roo.log('progress url failed ');
4887                 Roo.log(data);
4888             },
4889             scope : this
4890         });
4891            
4892     },
4893     
4894     
4895     run : function()
4896     {
4897         // run get Values on the form, so it syncs any secondary forms.
4898         this.form.getValues();
4899         
4900         var o = this.options;
4901         var method = this.getMethod();
4902         var isPost = method == 'POST';
4903         if(o.clientValidation === false || this.form.isValid()){
4904             
4905             if (this.form.progressUrl) {
4906                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4907                     (new Date() * 1) + '' + Math.random());
4908                     
4909             } 
4910             
4911             
4912             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4913                 form:this.form.el.dom,
4914                 url:this.getUrl(!isPost),
4915                 method: method,
4916                 params:isPost ? this.getParams() : null,
4917                 isUpload: this.form.fileUpload
4918             }));
4919             
4920             this.uploadProgress();
4921
4922         }else if (o.clientValidation !== false){ // client validation failed
4923             this.failureType = Roo.form.Action.CLIENT_INVALID;
4924             this.form.afterAction(this, false);
4925         }
4926     },
4927
4928     success : function(response)
4929     {
4930         this.uploadComplete= true;
4931         if (this.haveProgress) {
4932             Roo.MessageBox.hide();
4933         }
4934         
4935         
4936         var result = this.processResponse(response);
4937         if(result === true || result.success){
4938             this.form.afterAction(this, true);
4939             return;
4940         }
4941         if(result.errors){
4942             this.form.markInvalid(result.errors);
4943             this.failureType = Roo.form.Action.SERVER_INVALID;
4944         }
4945         this.form.afterAction(this, false);
4946     },
4947     failure : function(response)
4948     {
4949         this.uploadComplete= true;
4950         if (this.haveProgress) {
4951             Roo.MessageBox.hide();
4952         }
4953         
4954         this.response = response;
4955         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4956         this.form.afterAction(this, false);
4957     },
4958     
4959     handleResponse : function(response){
4960         if(this.form.errorReader){
4961             var rs = this.form.errorReader.read(response);
4962             var errors = [];
4963             if(rs.records){
4964                 for(var i = 0, len = rs.records.length; i < len; i++) {
4965                     var r = rs.records[i];
4966                     errors[i] = r.data;
4967                 }
4968             }
4969             if(errors.length < 1){
4970                 errors = null;
4971             }
4972             return {
4973                 success : rs.success,
4974                 errors : errors
4975             };
4976         }
4977         var ret = false;
4978         try {
4979             ret = Roo.decode(response.responseText);
4980         } catch (e) {
4981             ret = {
4982                 success: false,
4983                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4984                 errors : []
4985             };
4986         }
4987         return ret;
4988         
4989     }
4990 });
4991
4992
4993 Roo.form.Action.Load = function(form, options){
4994     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4995     this.reader = this.form.reader;
4996 };
4997
4998 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4999     type : 'load',
5000
5001     run : function(){
5002         
5003         Roo.Ajax.request(Roo.apply(
5004                 this.createCallback(), {
5005                     method:this.getMethod(),
5006                     url:this.getUrl(false),
5007                     params:this.getParams()
5008         }));
5009     },
5010
5011     success : function(response){
5012         
5013         var result = this.processResponse(response);
5014         if(result === true || !result.success || !result.data){
5015             this.failureType = Roo.form.Action.LOAD_FAILURE;
5016             this.form.afterAction(this, false);
5017             return;
5018         }
5019         this.form.clearInvalid();
5020         this.form.setValues(result.data);
5021         this.form.afterAction(this, true);
5022     },
5023
5024     handleResponse : function(response){
5025         if(this.form.reader){
5026             var rs = this.form.reader.read(response);
5027             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5028             return {
5029                 success : rs.success,
5030                 data : data
5031             };
5032         }
5033         return Roo.decode(response.responseText);
5034     }
5035 });
5036
5037 Roo.form.Action.ACTION_TYPES = {
5038     'load' : Roo.form.Action.Load,
5039     'submit' : Roo.form.Action.Submit
5040 };/*
5041  * - LGPL
5042  *
5043  * form
5044  * 
5045  */
5046
5047 /**
5048  * @class Roo.bootstrap.Form
5049  * @extends Roo.bootstrap.Component
5050  * Bootstrap Form class
5051  * @cfg {String} method  GET | POST (default POST)
5052  * @cfg {String} labelAlign top | left (default top)
5053   * @cfg {String} align left  | right - for navbars
5054
5055  * 
5056  * @constructor
5057  * Create a new Form
5058  * @param {Object} config The config object
5059  */
5060
5061
5062 Roo.bootstrap.Form = function(config){
5063     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5064     this.addEvents({
5065         /**
5066          * @event clientvalidation
5067          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5068          * @param {Form} this
5069          * @param {Boolean} valid true if the form has passed client-side validation
5070          */
5071         clientvalidation: true,
5072         /**
5073          * @event beforeaction
5074          * Fires before any action is performed. Return false to cancel the action.
5075          * @param {Form} this
5076          * @param {Action} action The action to be performed
5077          */
5078         beforeaction: true,
5079         /**
5080          * @event actionfailed
5081          * Fires when an action fails.
5082          * @param {Form} this
5083          * @param {Action} action The action that failed
5084          */
5085         actionfailed : true,
5086         /**
5087          * @event actioncomplete
5088          * Fires when an action is completed.
5089          * @param {Form} this
5090          * @param {Action} action The action that completed
5091          */
5092         actioncomplete : true
5093     });
5094     
5095 };
5096
5097 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5098       
5099      /**
5100      * @cfg {String} method
5101      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5102      */
5103     method : 'POST',
5104     /**
5105      * @cfg {String} url
5106      * The URL to use for form actions if one isn't supplied in the action options.
5107      */
5108     /**
5109      * @cfg {Boolean} fileUpload
5110      * Set to true if this form is a file upload.
5111      */
5112      
5113     /**
5114      * @cfg {Object} baseParams
5115      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5116      */
5117       
5118     /**
5119      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5120      */
5121     timeout: 30,
5122     /**
5123      * @cfg {Sting} align (left|right) for navbar forms
5124      */
5125     align : 'left',
5126
5127     // private
5128     activeAction : null,
5129  
5130     /**
5131      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5132      * element by passing it or its id or mask the form itself by passing in true.
5133      * @type Mixed
5134      */
5135     waitMsgTarget : false,
5136     
5137      
5138     
5139     /**
5140      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5141      * element by passing it or its id or mask the form itself by passing in true.
5142      * @type Mixed
5143      */
5144     
5145     getAutoCreate : function(){
5146         
5147         var cfg = {
5148             tag: 'form',
5149             method : this.method || 'POST',
5150             id : this.id || Roo.id(),
5151             cls : ''
5152         }
5153         if (this.parent().xtype.match(/^Nav/)) {
5154             cfg.cls = 'navbar-form navbar-' + this.align;
5155             
5156         }
5157         
5158         if (this.labelAlign == 'left' ) {
5159             cfg.cls += ' form-horizontal';
5160         }
5161         
5162         
5163         return cfg;
5164     },
5165     initEvents : function()
5166     {
5167         this.el.on('submit', this.onSubmit, this);
5168         
5169         
5170     },
5171     // private
5172     onSubmit : function(e){
5173         e.stopEvent();
5174     },
5175     
5176      /**
5177      * Returns true if client-side validation on the form is successful.
5178      * @return Boolean
5179      */
5180     isValid : function(){
5181         var items = this.getItems();
5182         var valid = true;
5183         items.each(function(f){
5184            if(!f.validate()){
5185                valid = false;
5186                
5187            }
5188         });
5189         return valid;
5190     },
5191     /**
5192      * Returns true if any fields in this form have changed since their original load.
5193      * @return Boolean
5194      */
5195     isDirty : function(){
5196         var dirty = false;
5197         var items = this.getItems();
5198         items.each(function(f){
5199            if(f.isDirty()){
5200                dirty = true;
5201                return false;
5202            }
5203            return true;
5204         });
5205         return dirty;
5206     },
5207      /**
5208      * Performs a predefined action (submit or load) or custom actions you define on this form.
5209      * @param {String} actionName The name of the action type
5210      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5211      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5212      * accept other config options):
5213      * <pre>
5214 Property          Type             Description
5215 ----------------  ---------------  ----------------------------------------------------------------------------------
5216 url               String           The url for the action (defaults to the form's url)
5217 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5218 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5219 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5220                                    validate the form on the client (defaults to false)
5221      * </pre>
5222      * @return {BasicForm} this
5223      */
5224     doAction : function(action, options){
5225         if(typeof action == 'string'){
5226             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5227         }
5228         if(this.fireEvent('beforeaction', this, action) !== false){
5229             this.beforeAction(action);
5230             action.run.defer(100, action);
5231         }
5232         return this;
5233     },
5234     
5235     // private
5236     beforeAction : function(action){
5237         var o = action.options;
5238         
5239         // not really supported yet.. ??
5240         
5241         //if(this.waitMsgTarget === true){
5242             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5243         //}else if(this.waitMsgTarget){
5244         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5245         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5246         //}else {
5247         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5248        // }
5249          
5250     },
5251
5252     // private
5253     afterAction : function(action, success){
5254         this.activeAction = null;
5255         var o = action.options;
5256         
5257         //if(this.waitMsgTarget === true){
5258             this.el.unmask();
5259         //}else if(this.waitMsgTarget){
5260         //    this.waitMsgTarget.unmask();
5261         //}else{
5262         //    Roo.MessageBox.updateProgress(1);
5263         //    Roo.MessageBox.hide();
5264        // }
5265         // 
5266         if(success){
5267             if(o.reset){
5268                 this.reset();
5269             }
5270             Roo.callback(o.success, o.scope, [this, action]);
5271             this.fireEvent('actioncomplete', this, action);
5272             
5273         }else{
5274             
5275             // failure condition..
5276             // we have a scenario where updates need confirming.
5277             // eg. if a locking scenario exists..
5278             // we look for { errors : { needs_confirm : true }} in the response.
5279             if (
5280                 (typeof(action.result) != 'undefined')  &&
5281                 (typeof(action.result.errors) != 'undefined')  &&
5282                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5283            ){
5284                 var _t = this;
5285                 Roo.log("not supported yet");
5286                  /*
5287                 
5288                 Roo.MessageBox.confirm(
5289                     "Change requires confirmation",
5290                     action.result.errorMsg,
5291                     function(r) {
5292                         if (r != 'yes') {
5293                             return;
5294                         }
5295                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5296                     }
5297                     
5298                 );
5299                 */
5300                 
5301                 
5302                 return;
5303             }
5304             
5305             Roo.callback(o.failure, o.scope, [this, action]);
5306             // show an error message if no failed handler is set..
5307             if (!this.hasListener('actionfailed')) {
5308                 Roo.log("need to add dialog support");
5309                 /*
5310                 Roo.MessageBox.alert("Error",
5311                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5312                         action.result.errorMsg :
5313                         "Saving Failed, please check your entries or try again"
5314                 );
5315                 */
5316             }
5317             
5318             this.fireEvent('actionfailed', this, action);
5319         }
5320         
5321     },
5322     /**
5323      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5324      * @param {String} id The value to search for
5325      * @return Field
5326      */
5327     findField : function(id){
5328         var items = this.getItems();
5329         var field = items.get(id);
5330         if(!field){
5331              items.each(function(f){
5332                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5333                     field = f;
5334                     return false;
5335                 }
5336                 return true;
5337             });
5338         }
5339         return field || null;
5340     },
5341      /**
5342      * Mark fields in this form invalid in bulk.
5343      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5344      * @return {BasicForm} this
5345      */
5346     markInvalid : function(errors){
5347         if(errors instanceof Array){
5348             for(var i = 0, len = errors.length; i < len; i++){
5349                 var fieldError = errors[i];
5350                 var f = this.findField(fieldError.id);
5351                 if(f){
5352                     f.markInvalid(fieldError.msg);
5353                 }
5354             }
5355         }else{
5356             var field, id;
5357             for(id in errors){
5358                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5359                     field.markInvalid(errors[id]);
5360                 }
5361             }
5362         }
5363         //Roo.each(this.childForms || [], function (f) {
5364         //    f.markInvalid(errors);
5365         //});
5366         
5367         return this;
5368     },
5369
5370     /**
5371      * Set values for fields in this form in bulk.
5372      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5373      * @return {BasicForm} this
5374      */
5375     setValues : function(values){
5376         if(values instanceof Array){ // array of objects
5377             for(var i = 0, len = values.length; i < len; i++){
5378                 var v = values[i];
5379                 var f = this.findField(v.id);
5380                 if(f){
5381                     f.setValue(v.value);
5382                     if(this.trackResetOnLoad){
5383                         f.originalValue = f.getValue();
5384                     }
5385                 }
5386             }
5387         }else{ // object hash
5388             var field, id;
5389             for(id in values){
5390                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5391                     
5392                     if (field.setFromData && 
5393                         field.valueField && 
5394                         field.displayField &&
5395                         // combos' with local stores can 
5396                         // be queried via setValue()
5397                         // to set their value..
5398                         (field.store && !field.store.isLocal)
5399                         ) {
5400                         // it's a combo
5401                         var sd = { };
5402                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5403                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5404                         field.setFromData(sd);
5405                         
5406                     } else {
5407                         field.setValue(values[id]);
5408                     }
5409                     
5410                     
5411                     if(this.trackResetOnLoad){
5412                         field.originalValue = field.getValue();
5413                     }
5414                 }
5415             }
5416         }
5417          
5418         //Roo.each(this.childForms || [], function (f) {
5419         //    f.setValues(values);
5420         //});
5421                 
5422         return this;
5423     },
5424
5425     /**
5426      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5427      * they are returned as an array.
5428      * @param {Boolean} asString
5429      * @return {Object}
5430      */
5431     getValues : function(asString){
5432         //if (this.childForms) {
5433             // copy values from the child forms
5434         //    Roo.each(this.childForms, function (f) {
5435         //        this.setValues(f.getValues());
5436         //    }, this);
5437         //}
5438         
5439         
5440         
5441         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5442         if(asString === true){
5443             return fs;
5444         }
5445         return Roo.urlDecode(fs);
5446     },
5447     
5448     /**
5449      * Returns the fields in this form as an object with key/value pairs. 
5450      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5451      * @return {Object}
5452      */
5453     getFieldValues : function(with_hidden)
5454     {
5455         var items = this.getItems();
5456         var ret = {};
5457         items.each(function(f){
5458             if (!f.getName()) {
5459                 return;
5460             }
5461             var v = f.getValue();
5462             if (f.inputType =='radio') {
5463                 if (typeof(ret[f.getName()]) == 'undefined') {
5464                     ret[f.getName()] = ''; // empty..
5465                 }
5466                 
5467                 if (!f.el.dom.checked) {
5468                     return;
5469                     
5470                 }
5471                 v = f.el.dom.value;
5472                 
5473             }
5474             
5475             // not sure if this supported any more..
5476             if ((typeof(v) == 'object') && f.getRawValue) {
5477                 v = f.getRawValue() ; // dates..
5478             }
5479             // combo boxes where name != hiddenName...
5480             if (f.name != f.getName()) {
5481                 ret[f.name] = f.getRawValue();
5482             }
5483             ret[f.getName()] = v;
5484         });
5485         
5486         return ret;
5487     },
5488
5489     /**
5490      * Clears all invalid messages in this form.
5491      * @return {BasicForm} this
5492      */
5493     clearInvalid : function(){
5494         var items = this.getItems();
5495         
5496         items.each(function(f){
5497            f.clearInvalid();
5498         });
5499         
5500         
5501         
5502         return this;
5503     },
5504
5505     /**
5506      * Resets this form.
5507      * @return {BasicForm} this
5508      */
5509     reset : function(){
5510         var items = this.getItems();
5511         items.each(function(f){
5512             f.reset();
5513         });
5514         
5515         Roo.each(this.childForms || [], function (f) {
5516             f.reset();
5517         });
5518        
5519         
5520         return this;
5521     },
5522     getItems : function()
5523     {
5524         var r=new Roo.util.MixedCollection(false, function(o){
5525             return o.id || (o.id = Roo.id());
5526         });
5527         var iter = function(el) {
5528             if (el.inputEl) {
5529                 r.add(el);
5530             }
5531             if (!el.items) {
5532                 return;
5533             }
5534             Roo.each(el.items,function(e) {
5535                 iter(e);
5536             });
5537             
5538             
5539         };
5540         iter(this);
5541         return r;
5542         
5543         
5544         
5545         
5546     }
5547     
5548 });
5549
5550  
5551 /*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561 /**
5562  * @class Roo.form.VTypes
5563  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5564  * @singleton
5565  */
5566 Roo.form.VTypes = function(){
5567     // closure these in so they are only created once.
5568     var alpha = /^[a-zA-Z_]+$/;
5569     var alphanum = /^[a-zA-Z0-9_]+$/;
5570     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5571     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5572
5573     // All these messages and functions are configurable
5574     return {
5575         /**
5576          * The function used to validate email addresses
5577          * @param {String} value The email address
5578          */
5579         'email' : function(v){
5580             return email.test(v);
5581         },
5582         /**
5583          * The error text to display when the email validation function returns false
5584          * @type String
5585          */
5586         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5587         /**
5588          * The keystroke filter mask to be applied on email input
5589          * @type RegExp
5590          */
5591         'emailMask' : /[a-z0-9_\.\-@]/i,
5592
5593         /**
5594          * The function used to validate URLs
5595          * @param {String} value The URL
5596          */
5597         'url' : function(v){
5598             return url.test(v);
5599         },
5600         /**
5601          * The error text to display when the url validation function returns false
5602          * @type String
5603          */
5604         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5605         
5606         /**
5607          * The function used to validate alpha values
5608          * @param {String} value The value
5609          */
5610         'alpha' : function(v){
5611             return alpha.test(v);
5612         },
5613         /**
5614          * The error text to display when the alpha validation function returns false
5615          * @type String
5616          */
5617         'alphaText' : 'This field should only contain letters and _',
5618         /**
5619          * The keystroke filter mask to be applied on alpha input
5620          * @type RegExp
5621          */
5622         'alphaMask' : /[a-z_]/i,
5623
5624         /**
5625          * The function used to validate alphanumeric values
5626          * @param {String} value The value
5627          */
5628         'alphanum' : function(v){
5629             return alphanum.test(v);
5630         },
5631         /**
5632          * The error text to display when the alphanumeric validation function returns false
5633          * @type String
5634          */
5635         'alphanumText' : 'This field should only contain letters, numbers and _',
5636         /**
5637          * The keystroke filter mask to be applied on alphanumeric input
5638          * @type RegExp
5639          */
5640         'alphanumMask' : /[a-z0-9_]/i
5641     };
5642 }();/*
5643  * - LGPL
5644  *
5645  * Input
5646  * 
5647  */
5648
5649 /**
5650  * @class Roo.bootstrap.Input
5651  * @extends Roo.bootstrap.Component
5652  * Bootstrap Input class
5653  * @cfg {Boolean} disabled is it disabled
5654  * @cfg {String} fieldLabel - the label associated
5655  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5656  * @cfg {String} name name of the input
5657  * @cfg {string} fieldLabel - the label associated
5658  * @cfg {string}  inputType - input / file submit ...
5659  * @cfg {string} placeholder - placeholder to put in text.
5660  * @cfg {string}  before - input group add on before
5661  * @cfg {string} after - input group add on after
5662  * @cfg {string} size - (lg|sm) or leave empty..
5663  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5664  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5665  * @cfg {Number} md colspan out of 12 for computer-sized screens
5666  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5667  * @cfg {string} value default value of the input
5668  * @cfg {Number} labelWidth set the width of label (0-12)
5669  * @cfg {String} labelAlign (top|left)
5670  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5671  * 
5672  * 
5673  * @constructor
5674  * Create a new Input
5675  * @param {Object} config The config object
5676  */
5677
5678 Roo.bootstrap.Input = function(config){
5679     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5680    
5681         this.addEvents({
5682             /**
5683              * @event focus
5684              * Fires when this field receives input focus.
5685              * @param {Roo.form.Field} this
5686              */
5687             focus : true,
5688             /**
5689              * @event blur
5690              * Fires when this field loses input focus.
5691              * @param {Roo.form.Field} this
5692              */
5693             blur : true,
5694             /**
5695              * @event specialkey
5696              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5697              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5698              * @param {Roo.form.Field} this
5699              * @param {Roo.EventObject} e The event object
5700              */
5701             specialkey : true,
5702             /**
5703              * @event change
5704              * Fires just before the field blurs if the field value has changed.
5705              * @param {Roo.form.Field} this
5706              * @param {Mixed} newValue The new value
5707              * @param {Mixed} oldValue The original value
5708              */
5709             change : true,
5710             /**
5711              * @event invalid
5712              * Fires after the field has been marked as invalid.
5713              * @param {Roo.form.Field} this
5714              * @param {String} msg The validation message
5715              */
5716             invalid : true,
5717             /**
5718              * @event valid
5719              * Fires after the field has been validated with no errors.
5720              * @param {Roo.form.Field} this
5721              */
5722             valid : true,
5723              /**
5724              * @event keyup
5725              * Fires after the key up
5726              * @param {Roo.form.Field} this
5727              * @param {Roo.EventObject}  e The event Object
5728              */
5729             keyup : true
5730         });
5731 };
5732
5733 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5734      /**
5735      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5736       automatic validation (defaults to "keyup").
5737      */
5738     validationEvent : "keyup",
5739      /**
5740      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5741      */
5742     validateOnBlur : true,
5743     /**
5744      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5745      */
5746     validationDelay : 250,
5747      /**
5748      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5749      */
5750     focusClass : "x-form-focus",  // not needed???
5751     
5752        
5753     /**
5754      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5755      */
5756     invalidClass : "has-error",
5757     
5758     /**
5759      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5760      */
5761     selectOnFocus : false,
5762     
5763      /**
5764      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5765      */
5766     maskRe : null,
5767        /**
5768      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5769      */
5770     vtype : null,
5771     
5772       /**
5773      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5774      */
5775     disableKeyFilter : false,
5776     
5777        /**
5778      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5779      */
5780     disabled : false,
5781      /**
5782      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5783      */
5784     allowBlank : true,
5785     /**
5786      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5787      */
5788     blankText : "This field is required",
5789     
5790      /**
5791      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5792      */
5793     minLength : 0,
5794     /**
5795      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5796      */
5797     maxLength : Number.MAX_VALUE,
5798     /**
5799      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5800      */
5801     minLengthText : "The minimum length for this field is {0}",
5802     /**
5803      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5804      */
5805     maxLengthText : "The maximum length for this field is {0}",
5806   
5807     
5808     /**
5809      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5810      * If available, this function will be called only after the basic validators all return true, and will be passed the
5811      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5812      */
5813     validator : null,
5814     /**
5815      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5816      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5817      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5818      */
5819     regex : null,
5820     /**
5821      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5822      */
5823     regexText : "",
5824     
5825     
5826     
5827     fieldLabel : '',
5828     inputType : 'text',
5829     
5830     name : false,
5831     placeholder: false,
5832     before : false,
5833     after : false,
5834     size : false,
5835     // private
5836     hasFocus : false,
5837     preventMark: false,
5838     isFormField : true,
5839     value : '',
5840     labelWidth : 2,
5841     labelAlign : false,
5842     readOnly : false,
5843     
5844     parentLabelAlign : function()
5845     {
5846         var parent = this;
5847         while (parent.parent()) {
5848             parent = parent.parent();
5849             if (typeof(parent.labelAlign) !='undefined') {
5850                 return parent.labelAlign;
5851             }
5852         }
5853         return 'left';
5854         
5855     },
5856     
5857     getAutoCreate : function(){
5858         
5859         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5860         
5861         var id = Roo.id();
5862         
5863         var cfg = {};
5864         
5865         if(this.inputType != 'hidden'){
5866             cfg.cls = 'form-group' //input-group
5867         }
5868         
5869         var input =  {
5870             tag: 'input',
5871             id : id,
5872             type : this.inputType,
5873             value : this.value,
5874             cls : 'form-control',
5875             placeholder : this.placeholder || ''
5876             
5877         };
5878         
5879         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5880             input.maxLength = this.maxLength;
5881         }
5882         
5883         if (this.disabled) {
5884             input.disabled=true;
5885         }
5886         
5887         if (this.readOnly) {
5888             input.readonly=true;
5889         }
5890         
5891         if (this.name) {
5892             input.name = this.name;
5893         }
5894         if (this.size) {
5895             input.cls += ' input-' + this.size;
5896         }
5897         var settings=this;
5898         ['xs','sm','md','lg'].map(function(size){
5899             if (settings[size]) {
5900                 cfg.cls += ' col-' + size + '-' + settings[size];
5901             }
5902         });
5903         
5904         var inputblock = input;
5905         
5906         if (this.before || this.after) {
5907             
5908             inputblock = {
5909                 cls : 'input-group',
5910                 cn :  [] 
5911             };
5912             if (this.before && typeof(this.before) == 'string') {
5913                 
5914                 inputblock.cn.push({
5915                     tag :'span',
5916                     cls : 'roo-input-before input-group-addon',
5917                     html : this.before
5918                 });
5919             }
5920             if (this.before && typeof(this.before) == 'object') {
5921                 this.before = Roo.factory(this.before);
5922                 Roo.log(this.before);
5923                 inputblock.cn.push({
5924                     tag :'span',
5925                     cls : 'roo-input-before input-group-' +
5926                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5927                 });
5928             }
5929             
5930             inputblock.cn.push(input);
5931             
5932             if (this.after && typeof(this.after) == 'string') {
5933                 inputblock.cn.push({
5934                     tag :'span',
5935                     cls : 'roo-input-after input-group-addon',
5936                     html : this.after
5937                 });
5938             }
5939             if (this.after && typeof(this.after) == 'object') {
5940                 this.after = Roo.factory(this.after);
5941                 Roo.log(this.after);
5942                 inputblock.cn.push({
5943                     tag :'span',
5944                     cls : 'roo-input-after input-group-' +
5945                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5946                 });
5947             }
5948         };
5949         
5950         if (align ==='left' && this.fieldLabel.length) {
5951                 Roo.log("left and has label");
5952                 cfg.cn = [
5953                     
5954                     {
5955                         tag: 'label',
5956                         'for' :  id,
5957                         cls : 'control-label col-sm-' + this.labelWidth,
5958                         html : this.fieldLabel
5959                         
5960                     },
5961                     {
5962                         cls : "col-sm-" + (12 - this.labelWidth), 
5963                         cn: [
5964                             inputblock
5965                         ]
5966                     }
5967                     
5968                 ];
5969         } else if ( this.fieldLabel.length) {
5970                 Roo.log(" label");
5971                  cfg.cn = [
5972                    
5973                     {
5974                         tag: 'label',
5975                         //cls : 'input-group-addon',
5976                         html : this.fieldLabel
5977                         
5978                     },
5979                     
5980                     inputblock
5981                     
5982                 ];
5983
5984         } else {
5985             
5986                 Roo.log(" no label && no align");
5987                 cfg.cn = [
5988                     
5989                         inputblock
5990                     
5991                 ];
5992                 
5993                 
5994         };
5995         Roo.log('input-parentType: ' + this.parentType);
5996         
5997         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5998            cfg.cls += ' navbar-form';
5999            Roo.log(cfg);
6000         }
6001         
6002         return cfg;
6003         
6004     },
6005     /**
6006      * return the real input element.
6007      */
6008     inputEl: function ()
6009     {
6010         return this.el.select('input.form-control',true).first();
6011     },
6012     setDisabled : function(v)
6013     {
6014         var i  = this.inputEl().dom;
6015         if (!v) {
6016             i.removeAttribute('disabled');
6017             return;
6018             
6019         }
6020         i.setAttribute('disabled','true');
6021     },
6022     initEvents : function()
6023     {
6024         
6025         this.inputEl().on("keydown" , this.fireKey,  this);
6026         this.inputEl().on("focus", this.onFocus,  this);
6027         this.inputEl().on("blur", this.onBlur,  this);
6028         
6029         this.inputEl().relayEvent('keyup', this);
6030
6031         // reference to original value for reset
6032         this.originalValue = this.getValue();
6033         //Roo.form.TextField.superclass.initEvents.call(this);
6034         if(this.validationEvent == 'keyup'){
6035             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6036             this.inputEl().on('keyup', this.filterValidation, this);
6037         }
6038         else if(this.validationEvent !== false){
6039             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6040         }
6041         
6042         if(this.selectOnFocus){
6043             this.on("focus", this.preFocus, this);
6044             
6045         }
6046         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6047             this.inputEl().on("keypress", this.filterKeys, this);
6048         }
6049        /* if(this.grow){
6050             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6051             this.el.on("click", this.autoSize,  this);
6052         }
6053         */
6054         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6055             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6056         }
6057         
6058         if (typeof(this.before) == 'object') {
6059             this.before.render(this.el.select('.roo-input-before',true).first());
6060         }
6061         if (typeof(this.after) == 'object') {
6062             this.after.render(this.el.select('.roo-input-after',true).first());
6063         }
6064         
6065         
6066     },
6067     filterValidation : function(e){
6068         if(!e.isNavKeyPress()){
6069             this.validationTask.delay(this.validationDelay);
6070         }
6071     },
6072      /**
6073      * Validates the field value
6074      * @return {Boolean} True if the value is valid, else false
6075      */
6076     validate : function(){
6077         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6078         if(this.disabled || this.validateValue(this.getRawValue())){
6079             this.clearInvalid();
6080             return true;
6081         }
6082         return false;
6083     },
6084     
6085     
6086     /**
6087      * Validates a value according to the field's validation rules and marks the field as invalid
6088      * if the validation fails
6089      * @param {Mixed} value The value to validate
6090      * @return {Boolean} True if the value is valid, else false
6091      */
6092     validateValue : function(value){
6093         if(value.length < 1)  { // if it's blank
6094              if(this.allowBlank){
6095                 this.clearInvalid();
6096                 return true;
6097              }else{
6098                 this.markInvalid(this.blankText);
6099                 return false;
6100              }
6101         }
6102         if(value.length < this.minLength){
6103             this.markInvalid(String.format(this.minLengthText, this.minLength));
6104             return false;
6105         }
6106         if(value.length > this.maxLength){
6107             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6108             return false;
6109         }
6110         if(this.vtype){
6111             var vt = Roo.form.VTypes;
6112             if(!vt[this.vtype](value, this)){
6113                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6114                 return false;
6115             }
6116         }
6117         if(typeof this.validator == "function"){
6118             var msg = this.validator(value);
6119             if(msg !== true){
6120                 this.markInvalid(msg);
6121                 return false;
6122             }
6123         }
6124         if(this.regex && !this.regex.test(value)){
6125             this.markInvalid(this.regexText);
6126             return false;
6127         }
6128         return true;
6129     },
6130
6131     
6132     
6133      // private
6134     fireKey : function(e){
6135         //Roo.log('field ' + e.getKey());
6136         if(e.isNavKeyPress()){
6137             this.fireEvent("specialkey", this, e);
6138         }
6139     },
6140     focus : function (selectText){
6141         if(this.rendered){
6142             this.inputEl().focus();
6143             if(selectText === true){
6144                 this.inputEl().dom.select();
6145             }
6146         }
6147         return this;
6148     } ,
6149     
6150     onFocus : function(){
6151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6152            // this.el.addClass(this.focusClass);
6153         }
6154         if(!this.hasFocus){
6155             this.hasFocus = true;
6156             this.startValue = this.getValue();
6157             this.fireEvent("focus", this);
6158         }
6159     },
6160     
6161     beforeBlur : Roo.emptyFn,
6162
6163     
6164     // private
6165     onBlur : function(){
6166         this.beforeBlur();
6167         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6168             //this.el.removeClass(this.focusClass);
6169         }
6170         this.hasFocus = false;
6171         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6172             this.validate();
6173         }
6174         var v = this.getValue();
6175         if(String(v) !== String(this.startValue)){
6176             this.fireEvent('change', this, v, this.startValue);
6177         }
6178         this.fireEvent("blur", this);
6179     },
6180     
6181     /**
6182      * Resets the current field value to the originally loaded value and clears any validation messages
6183      */
6184     reset : function(){
6185         this.setValue(this.originalValue);
6186         this.clearInvalid();
6187     },
6188      /**
6189      * Returns the name of the field
6190      * @return {Mixed} name The name field
6191      */
6192     getName: function(){
6193         return this.name;
6194     },
6195      /**
6196      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6197      * @return {Mixed} value The field value
6198      */
6199     getValue : function(){
6200         return this.inputEl().getValue();
6201     },
6202     /**
6203      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6204      * @return {Mixed} value The field value
6205      */
6206     getRawValue : function(){
6207         var v = this.inputEl().getValue();
6208         
6209         return v;
6210     },
6211     
6212     /**
6213      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6214      * @param {Mixed} value The value to set
6215      */
6216     setRawValue : function(v){
6217         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6218     },
6219     
6220     selectText : function(start, end){
6221         var v = this.getRawValue();
6222         if(v.length > 0){
6223             start = start === undefined ? 0 : start;
6224             end = end === undefined ? v.length : end;
6225             var d = this.inputEl().dom;
6226             if(d.setSelectionRange){
6227                 d.setSelectionRange(start, end);
6228             }else if(d.createTextRange){
6229                 var range = d.createTextRange();
6230                 range.moveStart("character", start);
6231                 range.moveEnd("character", v.length-end);
6232                 range.select();
6233             }
6234         }
6235     },
6236     
6237     /**
6238      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6239      * @param {Mixed} value The value to set
6240      */
6241     setValue : function(v){
6242         this.value = v;
6243         if(this.rendered){
6244             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6245             this.validate();
6246         }
6247     },
6248     
6249     /*
6250     processValue : function(value){
6251         if(this.stripCharsRe){
6252             var newValue = value.replace(this.stripCharsRe, '');
6253             if(newValue !== value){
6254                 this.setRawValue(newValue);
6255                 return newValue;
6256             }
6257         }
6258         return value;
6259     },
6260   */
6261     preFocus : function(){
6262         
6263         if(this.selectOnFocus){
6264             this.inputEl().dom.select();
6265         }
6266     },
6267     filterKeys : function(e){
6268         var k = e.getKey();
6269         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6270             return;
6271         }
6272         var c = e.getCharCode(), cc = String.fromCharCode(c);
6273         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6274             return;
6275         }
6276         if(!this.maskRe.test(cc)){
6277             e.stopEvent();
6278         }
6279     },
6280      /**
6281      * Clear any invalid styles/messages for this field
6282      */
6283     clearInvalid : function(){
6284         
6285         if(!this.el || this.preventMark){ // not rendered
6286             return;
6287         }
6288         this.el.removeClass(this.invalidClass);
6289         /*
6290         switch(this.msgTarget){
6291             case 'qtip':
6292                 this.el.dom.qtip = '';
6293                 break;
6294             case 'title':
6295                 this.el.dom.title = '';
6296                 break;
6297             case 'under':
6298                 if(this.errorEl){
6299                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6300                 }
6301                 break;
6302             case 'side':
6303                 if(this.errorIcon){
6304                     this.errorIcon.dom.qtip = '';
6305                     this.errorIcon.hide();
6306                     this.un('resize', this.alignErrorIcon, this);
6307                 }
6308                 break;
6309             default:
6310                 var t = Roo.getDom(this.msgTarget);
6311                 t.innerHTML = '';
6312                 t.style.display = 'none';
6313                 break;
6314         }
6315         */
6316         this.fireEvent('valid', this);
6317     },
6318      /**
6319      * Mark this field as invalid
6320      * @param {String} msg The validation message
6321      */
6322     markInvalid : function(msg){
6323         if(!this.el  || this.preventMark){ // not rendered
6324             return;
6325         }
6326         this.el.addClass(this.invalidClass);
6327         /*
6328         msg = msg || this.invalidText;
6329         switch(this.msgTarget){
6330             case 'qtip':
6331                 this.el.dom.qtip = msg;
6332                 this.el.dom.qclass = 'x-form-invalid-tip';
6333                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6334                     Roo.QuickTips.enable();
6335                 }
6336                 break;
6337             case 'title':
6338                 this.el.dom.title = msg;
6339                 break;
6340             case 'under':
6341                 if(!this.errorEl){
6342                     var elp = this.el.findParent('.x-form-element', 5, true);
6343                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6344                     this.errorEl.setWidth(elp.getWidth(true)-20);
6345                 }
6346                 this.errorEl.update(msg);
6347                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6348                 break;
6349             case 'side':
6350                 if(!this.errorIcon){
6351                     var elp = this.el.findParent('.x-form-element', 5, true);
6352                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6353                 }
6354                 this.alignErrorIcon();
6355                 this.errorIcon.dom.qtip = msg;
6356                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6357                 this.errorIcon.show();
6358                 this.on('resize', this.alignErrorIcon, this);
6359                 break;
6360             default:
6361                 var t = Roo.getDom(this.msgTarget);
6362                 t.innerHTML = msg;
6363                 t.style.display = this.msgDisplay;
6364                 break;
6365         }
6366         */
6367         this.fireEvent('invalid', this, msg);
6368     },
6369     // private
6370     SafariOnKeyDown : function(event)
6371     {
6372         // this is a workaround for a password hang bug on chrome/ webkit.
6373         
6374         var isSelectAll = false;
6375         
6376         if(this.inputEl().dom.selectionEnd > 0){
6377             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6378         }
6379         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6380             event.preventDefault();
6381             this.setValue('');
6382             return;
6383         }
6384         
6385         if(isSelectAll){ // backspace and delete key
6386             
6387             event.preventDefault();
6388             // this is very hacky as keydown always get's upper case.
6389             //
6390             var cc = String.fromCharCode(event.getCharCode());
6391             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6392             
6393         }
6394     },
6395     adjustWidth : function(tag, w){
6396         tag = tag.toLowerCase();
6397         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6398             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6399                 if(tag == 'input'){
6400                     return w + 2;
6401                 }
6402                 if(tag == 'textarea'){
6403                     return w-2;
6404                 }
6405             }else if(Roo.isOpera){
6406                 if(tag == 'input'){
6407                     return w + 2;
6408                 }
6409                 if(tag == 'textarea'){
6410                     return w-2;
6411                 }
6412             }
6413         }
6414         return w;
6415     }
6416     
6417 });
6418
6419  
6420 /*
6421  * - LGPL
6422  *
6423  * Input
6424  * 
6425  */
6426
6427 /**
6428  * @class Roo.bootstrap.TextArea
6429  * @extends Roo.bootstrap.Input
6430  * Bootstrap TextArea class
6431  * @cfg {Number} cols Specifies the visible width of a text area
6432  * @cfg {Number} rows Specifies the visible number of lines in a text area
6433  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6434  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6435  * @cfg {string} html text
6436  * 
6437  * @constructor
6438  * Create a new TextArea
6439  * @param {Object} config The config object
6440  */
6441
6442 Roo.bootstrap.TextArea = function(config){
6443     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6444    
6445 };
6446
6447 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6448      
6449     cols : false,
6450     rows : 5,
6451     readOnly : false,
6452     warp : 'soft',
6453     resize : false,
6454     value: false,
6455     html: false,
6456     
6457     getAutoCreate : function(){
6458         
6459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6460         
6461         var id = Roo.id();
6462         
6463         var cfg = {};
6464         
6465         var input =  {
6466             tag: 'textarea',
6467             id : id,
6468             warp : this.warp,
6469             rows : this.rows,
6470             value : this.value || '',
6471             html: this.html || '',
6472             cls : 'form-control',
6473             placeholder : this.placeholder || '' 
6474             
6475         };
6476         
6477         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6478             input.maxLength = this.maxLength;
6479         }
6480         
6481         if(this.resize){
6482             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6483         }
6484         
6485         if(this.cols){
6486             input.cols = this.cols;
6487         }
6488         
6489         if (this.readOnly) {
6490             input.readonly = true;
6491         }
6492         
6493         if (this.name) {
6494             input.name = this.name;
6495         }
6496         
6497         if (this.size) {
6498             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6499         }
6500         
6501         var settings=this;
6502         ['xs','sm','md','lg'].map(function(size){
6503             if (settings[size]) {
6504                 cfg.cls += ' col-' + size + '-' + settings[size];
6505             }
6506         });
6507         
6508         var inputblock = input;
6509         
6510         if (this.before || this.after) {
6511             
6512             inputblock = {
6513                 cls : 'input-group',
6514                 cn :  [] 
6515             };
6516             if (this.before) {
6517                 inputblock.cn.push({
6518                     tag :'span',
6519                     cls : 'input-group-addon',
6520                     html : this.before
6521                 });
6522             }
6523             inputblock.cn.push(input);
6524             if (this.after) {
6525                 inputblock.cn.push({
6526                     tag :'span',
6527                     cls : 'input-group-addon',
6528                     html : this.after
6529                 });
6530             }
6531             
6532         }
6533         
6534         if (align ==='left' && this.fieldLabel.length) {
6535                 Roo.log("left and has label");
6536                 cfg.cn = [
6537                     
6538                     {
6539                         tag: 'label',
6540                         'for' :  id,
6541                         cls : 'control-label col-sm-' + this.labelWidth,
6542                         html : this.fieldLabel
6543                         
6544                     },
6545                     {
6546                         cls : "col-sm-" + (12 - this.labelWidth), 
6547                         cn: [
6548                             inputblock
6549                         ]
6550                     }
6551                     
6552                 ];
6553         } else if ( this.fieldLabel.length) {
6554                 Roo.log(" label");
6555                  cfg.cn = [
6556                    
6557                     {
6558                         tag: 'label',
6559                         //cls : 'input-group-addon',
6560                         html : this.fieldLabel
6561                         
6562                     },
6563                     
6564                     inputblock
6565                     
6566                 ];
6567
6568         } else {
6569             
6570                    Roo.log(" no label && no align");
6571                 cfg.cn = [
6572                     
6573                         inputblock
6574                     
6575                 ];
6576                 
6577                 
6578         }
6579         
6580         if (this.disabled) {
6581             input.disabled=true;
6582         }
6583         
6584         return cfg;
6585         
6586     },
6587     /**
6588      * return the real textarea element.
6589      */
6590     inputEl: function ()
6591     {
6592         return this.el.select('textarea.form-control',true).first();
6593     }
6594 });
6595
6596  
6597 /*
6598  * - LGPL
6599  *
6600  * trigger field - base class for combo..
6601  * 
6602  */
6603  
6604 /**
6605  * @class Roo.bootstrap.TriggerField
6606  * @extends Roo.bootstrap.Input
6607  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6608  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6609  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6610  * for which you can provide a custom implementation.  For example:
6611  * <pre><code>
6612 var trigger = new Roo.bootstrap.TriggerField();
6613 trigger.onTriggerClick = myTriggerFn;
6614 trigger.applyTo('my-field');
6615 </code></pre>
6616  *
6617  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6618  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6619  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6620  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6621  * @constructor
6622  * Create a new TriggerField.
6623  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6624  * to the base TextField)
6625  */
6626 Roo.bootstrap.TriggerField = function(config){
6627     this.mimicing = false;
6628     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6629 };
6630
6631 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6632     /**
6633      * @cfg {String} triggerClass A CSS class to apply to the trigger
6634      */
6635      /**
6636      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6637      */
6638     hideTrigger:false,
6639
6640     /** @cfg {Boolean} grow @hide */
6641     /** @cfg {Number} growMin @hide */
6642     /** @cfg {Number} growMax @hide */
6643
6644     /**
6645      * @hide 
6646      * @method
6647      */
6648     autoSize: Roo.emptyFn,
6649     // private
6650     monitorTab : true,
6651     // private
6652     deferHeight : true,
6653
6654     
6655     actionMode : 'wrap',
6656     
6657     
6658     
6659     getAutoCreate : function(){
6660        
6661         var parent = this.parent();
6662         
6663         var align = this.labelAlign || this.parentLabelAlign();
6664         
6665         var id = Roo.id();
6666         
6667         var cfg = {
6668             cls: 'form-group' //input-group
6669         };
6670         
6671         
6672         var input =  {
6673             tag: 'input',
6674             id : id,
6675             type : this.inputType,
6676             cls : 'form-control',
6677             autocomplete: 'off',
6678             placeholder : this.placeholder || '' 
6679             
6680         };
6681         if (this.name) {
6682             input.name = this.name;
6683         }
6684         if (this.size) {
6685             input.cls += ' input-' + this.size;
6686         }
6687         
6688         if (this.disabled) {
6689             input.disabled=true;
6690         }
6691         
6692         var inputblock = input;
6693         
6694         if (this.before || this.after) {
6695             
6696             inputblock = {
6697                 cls : 'input-group',
6698                 cn :  [] 
6699             };
6700             if (this.before) {
6701                 inputblock.cn.push({
6702                     tag :'span',
6703                     cls : 'input-group-addon',
6704                     html : this.before
6705                 });
6706             }
6707             inputblock.cn.push(input);
6708             if (this.after) {
6709                 inputblock.cn.push({
6710                     tag :'span',
6711                     cls : 'input-group-addon',
6712                     html : this.after
6713                 });
6714             }
6715             
6716         };
6717         
6718         var box = {
6719             tag: 'div',
6720             cn: [
6721                 {
6722                     tag: 'input',
6723                     type : 'hidden',
6724                     cls: 'form-hidden-field'
6725                 },
6726                 inputblock
6727             ]
6728             
6729         };
6730         
6731         if(this.multiple){
6732             Roo.log('multiple');
6733             
6734             box = {
6735                 tag: 'div',
6736                 cn: [
6737                     {
6738                         tag: 'input',
6739                         type : 'hidden',
6740                         cls: 'form-hidden-field'
6741                     },
6742                     {
6743                         tag: 'ul',
6744                         cls: 'select2-choices',
6745                         cn:[
6746                             {
6747                                 tag: 'li',
6748                                 cls: 'select2-search-field',
6749                                 cn: [
6750
6751                                     inputblock
6752                                 ]
6753                             }
6754                         ]
6755                     }
6756                 ]
6757             }
6758         };
6759         
6760         var combobox = {
6761             cls: 'select2-container input-group',
6762             cn: [
6763                 box,
6764                 {
6765                     tag: 'ul',
6766                     cls: 'typeahead typeahead-long dropdown-menu',
6767                     style: 'display:none'
6768                 }
6769             ]
6770         };
6771         
6772         if(!this.multiple){
6773             combobox.cn.push({
6774                 tag :'span',
6775                 cls : 'input-group-addon btn dropdown-toggle',
6776                 cn : [
6777                     {
6778                         tag: 'span',
6779                         cls: 'caret'
6780                     },
6781                     {
6782                         tag: 'span',
6783                         cls: 'combobox-clear',
6784                         cn  : [
6785                             {
6786                                 tag : 'i',
6787                                 cls: 'icon-remove'
6788                             }
6789                         ]
6790                     }
6791                 ]
6792
6793             })
6794         }
6795         
6796         if(this.multiple){
6797             combobox.cls += ' select2-container-multi';
6798         }
6799         
6800         if (align ==='left' && this.fieldLabel.length) {
6801             
6802                 Roo.log("left and has label");
6803                 cfg.cn = [
6804                     
6805                     {
6806                         tag: 'label',
6807                         'for' :  id,
6808                         cls : 'control-label col-sm-' + this.labelWidth,
6809                         html : this.fieldLabel
6810                         
6811                     },
6812                     {
6813                         cls : "col-sm-" + (12 - this.labelWidth), 
6814                         cn: [
6815                             combobox
6816                         ]
6817                     }
6818                     
6819                 ];
6820         } else if ( this.fieldLabel.length) {
6821                 Roo.log(" label");
6822                  cfg.cn = [
6823                    
6824                     {
6825                         tag: 'label',
6826                         //cls : 'input-group-addon',
6827                         html : this.fieldLabel
6828                         
6829                     },
6830                     
6831                     combobox
6832                     
6833                 ];
6834
6835         } else {
6836             
6837                 Roo.log(" no label && no align");
6838                 cfg = combobox
6839                      
6840                 
6841         }
6842          
6843         var settings=this;
6844         ['xs','sm','md','lg'].map(function(size){
6845             if (settings[size]) {
6846                 cfg.cls += ' col-' + size + '-' + settings[size];
6847             }
6848         });
6849         
6850         return cfg;
6851         
6852     },
6853     
6854     
6855     
6856     // private
6857     onResize : function(w, h){
6858 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6859 //        if(typeof w == 'number'){
6860 //            var x = w - this.trigger.getWidth();
6861 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6862 //            this.trigger.setStyle('left', x+'px');
6863 //        }
6864     },
6865
6866     // private
6867     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6868
6869     // private
6870     getResizeEl : function(){
6871         return this.inputEl();
6872     },
6873
6874     // private
6875     getPositionEl : function(){
6876         return this.inputEl();
6877     },
6878
6879     // private
6880     alignErrorIcon : function(){
6881         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6882     },
6883
6884     // private
6885     initEvents : function(){
6886         
6887         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6888         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6889         if(!this.multiple){
6890             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6891             if(this.hideTrigger){
6892                 this.trigger.setDisplayed(false);
6893             }
6894             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6895         }
6896         
6897         if(this.multiple){
6898             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6899         }
6900         
6901         //this.trigger.addClassOnOver('x-form-trigger-over');
6902         //this.trigger.addClassOnClick('x-form-trigger-click');
6903         
6904         //if(!this.width){
6905         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6906         //}
6907     },
6908
6909     // private
6910     initTrigger : function(){
6911        
6912     },
6913
6914     // private
6915     onDestroy : function(){
6916         if(this.trigger){
6917             this.trigger.removeAllListeners();
6918           //  this.trigger.remove();
6919         }
6920         //if(this.wrap){
6921         //    this.wrap.remove();
6922         //}
6923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6924     },
6925
6926     // private
6927     onFocus : function(){
6928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6929         /*
6930         if(!this.mimicing){
6931             this.wrap.addClass('x-trigger-wrap-focus');
6932             this.mimicing = true;
6933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6934             if(this.monitorTab){
6935                 this.el.on("keydown", this.checkTab, this);
6936             }
6937         }
6938         */
6939     },
6940
6941     // private
6942     checkTab : function(e){
6943         if(e.getKey() == e.TAB){
6944             this.triggerBlur();
6945         }
6946     },
6947
6948     // private
6949     onBlur : function(){
6950         // do nothing
6951     },
6952
6953     // private
6954     mimicBlur : function(e, t){
6955         /*
6956         if(!this.wrap.contains(t) && this.validateBlur()){
6957             this.triggerBlur();
6958         }
6959         */
6960     },
6961
6962     // private
6963     triggerBlur : function(){
6964         this.mimicing = false;
6965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6966         if(this.monitorTab){
6967             this.el.un("keydown", this.checkTab, this);
6968         }
6969         //this.wrap.removeClass('x-trigger-wrap-focus');
6970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6971     },
6972
6973     // private
6974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6975     validateBlur : function(e, t){
6976         return true;
6977     },
6978
6979     // private
6980     onDisable : function(){
6981         this.inputEl().dom.disabled = true;
6982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6983         //if(this.wrap){
6984         //    this.wrap.addClass('x-item-disabled');
6985         //}
6986     },
6987
6988     // private
6989     onEnable : function(){
6990         this.inputEl().dom.disabled = false;
6991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6992         //if(this.wrap){
6993         //    this.el.removeClass('x-item-disabled');
6994         //}
6995     },
6996
6997     // private
6998     onShow : function(){
6999         var ae = this.getActionEl();
7000         
7001         if(ae){
7002             ae.dom.style.display = '';
7003             ae.dom.style.visibility = 'visible';
7004         }
7005     },
7006
7007     // private
7008     
7009     onHide : function(){
7010         var ae = this.getActionEl();
7011         ae.dom.style.display = 'none';
7012     },
7013
7014     /**
7015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7016      * by an implementing function.
7017      * @method
7018      * @param {EventObject} e
7019      */
7020     onTriggerClick : Roo.emptyFn
7021 });
7022  /*
7023  * Based on:
7024  * Ext JS Library 1.1.1
7025  * Copyright(c) 2006-2007, Ext JS, LLC.
7026  *
7027  * Originally Released Under LGPL - original licence link has changed is not relivant.
7028  *
7029  * Fork - LGPL
7030  * <script type="text/javascript">
7031  */
7032
7033
7034 /**
7035  * @class Roo.data.SortTypes
7036  * @singleton
7037  * Defines the default sorting (casting?) comparison functions used when sorting data.
7038  */
7039 Roo.data.SortTypes = {
7040     /**
7041      * Default sort that does nothing
7042      * @param {Mixed} s The value being converted
7043      * @return {Mixed} The comparison value
7044      */
7045     none : function(s){
7046         return s;
7047     },
7048     
7049     /**
7050      * The regular expression used to strip tags
7051      * @type {RegExp}
7052      * @property
7053      */
7054     stripTagsRE : /<\/?[^>]+>/gi,
7055     
7056     /**
7057      * Strips all HTML tags to sort on text only
7058      * @param {Mixed} s The value being converted
7059      * @return {String} The comparison value
7060      */
7061     asText : function(s){
7062         return String(s).replace(this.stripTagsRE, "");
7063     },
7064     
7065     /**
7066      * Strips all HTML tags to sort on text only - Case insensitive
7067      * @param {Mixed} s The value being converted
7068      * @return {String} The comparison value
7069      */
7070     asUCText : function(s){
7071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7072     },
7073     
7074     /**
7075      * Case insensitive string
7076      * @param {Mixed} s The value being converted
7077      * @return {String} The comparison value
7078      */
7079     asUCString : function(s) {
7080         return String(s).toUpperCase();
7081     },
7082     
7083     /**
7084      * Date sorting
7085      * @param {Mixed} s The value being converted
7086      * @return {Number} The comparison value
7087      */
7088     asDate : function(s) {
7089         if(!s){
7090             return 0;
7091         }
7092         if(s instanceof Date){
7093             return s.getTime();
7094         }
7095         return Date.parse(String(s));
7096     },
7097     
7098     /**
7099      * Float sorting
7100      * @param {Mixed} s The value being converted
7101      * @return {Float} The comparison value
7102      */
7103     asFloat : function(s) {
7104         var val = parseFloat(String(s).replace(/,/g, ""));
7105         if(isNaN(val)) val = 0;
7106         return val;
7107     },
7108     
7109     /**
7110      * Integer sorting
7111      * @param {Mixed} s The value being converted
7112      * @return {Number} The comparison value
7113      */
7114     asInt : function(s) {
7115         var val = parseInt(String(s).replace(/,/g, ""));
7116         if(isNaN(val)) val = 0;
7117         return val;
7118     }
7119 };/*
7120  * Based on:
7121  * Ext JS Library 1.1.1
7122  * Copyright(c) 2006-2007, Ext JS, LLC.
7123  *
7124  * Originally Released Under LGPL - original licence link has changed is not relivant.
7125  *
7126  * Fork - LGPL
7127  * <script type="text/javascript">
7128  */
7129
7130 /**
7131 * @class Roo.data.Record
7132  * Instances of this class encapsulate both record <em>definition</em> information, and record
7133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7134  * to access Records cached in an {@link Roo.data.Store} object.<br>
7135  * <p>
7136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7138  * objects.<br>
7139  * <p>
7140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7141  * @constructor
7142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7143  * {@link #create}. The parameters are the same.
7144  * @param {Array} data An associative Array of data values keyed by the field name.
7145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7147  * not specified an integer id is generated.
7148  */
7149 Roo.data.Record = function(data, id){
7150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7151     this.data = data;
7152 };
7153
7154 /**
7155  * Generate a constructor for a specific record layout.
7156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7158  * Each field definition object may contain the following properties: <ul>
7159  * <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,
7160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7166  * this may be omitted.</p></li>
7167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7168  * <ul><li>auto (Default, implies no conversion)</li>
7169  * <li>string</li>
7170  * <li>int</li>
7171  * <li>float</li>
7172  * <li>boolean</li>
7173  * <li>date</li></ul></p></li>
7174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7177  * by the Reader into an object that will be stored in the Record. It is passed the
7178  * following parameters:<ul>
7179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7180  * </ul></p></li>
7181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7182  * </ul>
7183  * <br>usage:<br><pre><code>
7184 var TopicRecord = Roo.data.Record.create(
7185     {name: 'title', mapping: 'topic_title'},
7186     {name: 'author', mapping: 'username'},
7187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7189     {name: 'lastPoster', mapping: 'user2'},
7190     {name: 'excerpt', mapping: 'post_text'}
7191 );
7192
7193 var myNewRecord = new TopicRecord({
7194     title: 'Do my job please',
7195     author: 'noobie',
7196     totalPosts: 1,
7197     lastPost: new Date(),
7198     lastPoster: 'Animal',
7199     excerpt: 'No way dude!'
7200 });
7201 myStore.add(myNewRecord);
7202 </code></pre>
7203  * @method create
7204  * @static
7205  */
7206 Roo.data.Record.create = function(o){
7207     var f = function(){
7208         f.superclass.constructor.apply(this, arguments);
7209     };
7210     Roo.extend(f, Roo.data.Record);
7211     var p = f.prototype;
7212     p.fields = new Roo.util.MixedCollection(false, function(field){
7213         return field.name;
7214     });
7215     for(var i = 0, len = o.length; i < len; i++){
7216         p.fields.add(new Roo.data.Field(o[i]));
7217     }
7218     f.getField = function(name){
7219         return p.fields.get(name);  
7220     };
7221     return f;
7222 };
7223
7224 Roo.data.Record.AUTO_ID = 1000;
7225 Roo.data.Record.EDIT = 'edit';
7226 Roo.data.Record.REJECT = 'reject';
7227 Roo.data.Record.COMMIT = 'commit';
7228
7229 Roo.data.Record.prototype = {
7230     /**
7231      * Readonly flag - true if this record has been modified.
7232      * @type Boolean
7233      */
7234     dirty : false,
7235     editing : false,
7236     error: null,
7237     modified: null,
7238
7239     // private
7240     join : function(store){
7241         this.store = store;
7242     },
7243
7244     /**
7245      * Set the named field to the specified value.
7246      * @param {String} name The name of the field to set.
7247      * @param {Object} value The value to set the field to.
7248      */
7249     set : function(name, value){
7250         if(this.data[name] == value){
7251             return;
7252         }
7253         this.dirty = true;
7254         if(!this.modified){
7255             this.modified = {};
7256         }
7257         if(typeof this.modified[name] == 'undefined'){
7258             this.modified[name] = this.data[name];
7259         }
7260         this.data[name] = value;
7261         if(!this.editing && this.store){
7262             this.store.afterEdit(this);
7263         }       
7264     },
7265
7266     /**
7267      * Get the value of the named field.
7268      * @param {String} name The name of the field to get the value of.
7269      * @return {Object} The value of the field.
7270      */
7271     get : function(name){
7272         return this.data[name]; 
7273     },
7274
7275     // private
7276     beginEdit : function(){
7277         this.editing = true;
7278         this.modified = {}; 
7279     },
7280
7281     // private
7282     cancelEdit : function(){
7283         this.editing = false;
7284         delete this.modified;
7285     },
7286
7287     // private
7288     endEdit : function(){
7289         this.editing = false;
7290         if(this.dirty && this.store){
7291             this.store.afterEdit(this);
7292         }
7293     },
7294
7295     /**
7296      * Usually called by the {@link Roo.data.Store} which owns the Record.
7297      * Rejects all changes made to the Record since either creation, or the last commit operation.
7298      * Modified fields are reverted to their original values.
7299      * <p>
7300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7301      * of reject operations.
7302      */
7303     reject : function(){
7304         var m = this.modified;
7305         for(var n in m){
7306             if(typeof m[n] != "function"){
7307                 this.data[n] = m[n];
7308             }
7309         }
7310         this.dirty = false;
7311         delete this.modified;
7312         this.editing = false;
7313         if(this.store){
7314             this.store.afterReject(this);
7315         }
7316     },
7317
7318     /**
7319      * Usually called by the {@link Roo.data.Store} which owns the Record.
7320      * Commits all changes made to the Record since either creation, or the last commit operation.
7321      * <p>
7322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7323      * of commit operations.
7324      */
7325     commit : function(){
7326         this.dirty = false;
7327         delete this.modified;
7328         this.editing = false;
7329         if(this.store){
7330             this.store.afterCommit(this);
7331         }
7332     },
7333
7334     // private
7335     hasError : function(){
7336         return this.error != null;
7337     },
7338
7339     // private
7340     clearError : function(){
7341         this.error = null;
7342     },
7343
7344     /**
7345      * Creates a copy of this record.
7346      * @param {String} id (optional) A new record id if you don't want to use this record's id
7347      * @return {Record}
7348      */
7349     copy : function(newId) {
7350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7351     }
7352 };/*
7353  * Based on:
7354  * Ext JS Library 1.1.1
7355  * Copyright(c) 2006-2007, Ext JS, LLC.
7356  *
7357  * Originally Released Under LGPL - original licence link has changed is not relivant.
7358  *
7359  * Fork - LGPL
7360  * <script type="text/javascript">
7361  */
7362
7363
7364
7365 /**
7366  * @class Roo.data.Store
7367  * @extends Roo.util.Observable
7368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7370  * <p>
7371  * 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
7372  * has no knowledge of the format of the data returned by the Proxy.<br>
7373  * <p>
7374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7375  * instances from the data object. These records are cached and made available through accessor functions.
7376  * @constructor
7377  * Creates a new Store.
7378  * @param {Object} config A config object containing the objects needed for the Store to access data,
7379  * and read the data into Records.
7380  */
7381 Roo.data.Store = function(config){
7382     this.data = new Roo.util.MixedCollection(false);
7383     this.data.getKey = function(o){
7384         return o.id;
7385     };
7386     this.baseParams = {};
7387     // private
7388     this.paramNames = {
7389         "start" : "start",
7390         "limit" : "limit",
7391         "sort" : "sort",
7392         "dir" : "dir",
7393         "multisort" : "_multisort"
7394     };
7395
7396     if(config && config.data){
7397         this.inlineData = config.data;
7398         delete config.data;
7399     }
7400
7401     Roo.apply(this, config);
7402     
7403     if(this.reader){ // reader passed
7404         this.reader = Roo.factory(this.reader, Roo.data);
7405         this.reader.xmodule = this.xmodule || false;
7406         if(!this.recordType){
7407             this.recordType = this.reader.recordType;
7408         }
7409         if(this.reader.onMetaChange){
7410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7411         }
7412     }
7413
7414     if(this.recordType){
7415         this.fields = this.recordType.prototype.fields;
7416     }
7417     this.modified = [];
7418
7419     this.addEvents({
7420         /**
7421          * @event datachanged
7422          * Fires when the data cache has changed, and a widget which is using this Store
7423          * as a Record cache should refresh its view.
7424          * @param {Store} this
7425          */
7426         datachanged : true,
7427         /**
7428          * @event metachange
7429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7430          * @param {Store} this
7431          * @param {Object} meta The JSON metadata
7432          */
7433         metachange : true,
7434         /**
7435          * @event add
7436          * Fires when Records have been added to the Store
7437          * @param {Store} this
7438          * @param {Roo.data.Record[]} records The array of Records added
7439          * @param {Number} index The index at which the record(s) were added
7440          */
7441         add : true,
7442         /**
7443          * @event remove
7444          * Fires when a Record has been removed from the Store
7445          * @param {Store} this
7446          * @param {Roo.data.Record} record The Record that was removed
7447          * @param {Number} index The index at which the record was removed
7448          */
7449         remove : true,
7450         /**
7451          * @event update
7452          * Fires when a Record has been updated
7453          * @param {Store} this
7454          * @param {Roo.data.Record} record The Record that was updated
7455          * @param {String} operation The update operation being performed.  Value may be one of:
7456          * <pre><code>
7457  Roo.data.Record.EDIT
7458  Roo.data.Record.REJECT
7459  Roo.data.Record.COMMIT
7460          * </code></pre>
7461          */
7462         update : true,
7463         /**
7464          * @event clear
7465          * Fires when the data cache has been cleared.
7466          * @param {Store} this
7467          */
7468         clear : true,
7469         /**
7470          * @event beforeload
7471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7472          * the load action will be canceled.
7473          * @param {Store} this
7474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7475          */
7476         beforeload : true,
7477         /**
7478          * @event beforeloadadd
7479          * Fires after a new set of Records has been loaded.
7480          * @param {Store} this
7481          * @param {Roo.data.Record[]} records The Records that were loaded
7482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7483          */
7484         beforeloadadd : true,
7485         /**
7486          * @event load
7487          * Fires after a new set of Records has been loaded, before they are added to the store.
7488          * @param {Store} this
7489          * @param {Roo.data.Record[]} records The Records that were loaded
7490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7491          * @params {Object} return from reader
7492          */
7493         load : true,
7494         /**
7495          * @event loadexception
7496          * Fires if an exception occurs in the Proxy during loading.
7497          * Called with the signature of the Proxy's "loadexception" event.
7498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7499          * 
7500          * @param {Proxy} 
7501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7502          * @param {Object} load options 
7503          * @param {Object} jsonData from your request (normally this contains the Exception)
7504          */
7505         loadexception : true
7506     });
7507     
7508     if(this.proxy){
7509         this.proxy = Roo.factory(this.proxy, Roo.data);
7510         this.proxy.xmodule = this.xmodule || false;
7511         this.relayEvents(this.proxy,  ["loadexception"]);
7512     }
7513     this.sortToggle = {};
7514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7515
7516     Roo.data.Store.superclass.constructor.call(this);
7517
7518     if(this.inlineData){
7519         this.loadData(this.inlineData);
7520         delete this.inlineData;
7521     }
7522 };
7523
7524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7525      /**
7526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7527     * without a remote query - used by combo/forms at present.
7528     */
7529     
7530     /**
7531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7532     */
7533     /**
7534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7535     */
7536     /**
7537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7539     */
7540     /**
7541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7542     * on any HTTP request
7543     */
7544     /**
7545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7546     */
7547     /**
7548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7549     */
7550     multiSort: false,
7551     /**
7552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7554     */
7555     remoteSort : false,
7556
7557     /**
7558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7559      * loaded or when a record is removed. (defaults to false).
7560     */
7561     pruneModifiedRecords : false,
7562
7563     // private
7564     lastOptions : null,
7565
7566     /**
7567      * Add Records to the Store and fires the add event.
7568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7569      */
7570     add : function(records){
7571         records = [].concat(records);
7572         for(var i = 0, len = records.length; i < len; i++){
7573             records[i].join(this);
7574         }
7575         var index = this.data.length;
7576         this.data.addAll(records);
7577         this.fireEvent("add", this, records, index);
7578     },
7579
7580     /**
7581      * Remove a Record from the Store and fires the remove event.
7582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7583      */
7584     remove : function(record){
7585         var index = this.data.indexOf(record);
7586         this.data.removeAt(index);
7587         if(this.pruneModifiedRecords){
7588             this.modified.remove(record);
7589         }
7590         this.fireEvent("remove", this, record, index);
7591     },
7592
7593     /**
7594      * Remove all Records from the Store and fires the clear event.
7595      */
7596     removeAll : function(){
7597         this.data.clear();
7598         if(this.pruneModifiedRecords){
7599             this.modified = [];
7600         }
7601         this.fireEvent("clear", this);
7602     },
7603
7604     /**
7605      * Inserts Records to the Store at the given index and fires the add event.
7606      * @param {Number} index The start index at which to insert the passed Records.
7607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7608      */
7609     insert : function(index, records){
7610         records = [].concat(records);
7611         for(var i = 0, len = records.length; i < len; i++){
7612             this.data.insert(index, records[i]);
7613             records[i].join(this);
7614         }
7615         this.fireEvent("add", this, records, index);
7616     },
7617
7618     /**
7619      * Get the index within the cache of the passed Record.
7620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7621      * @return {Number} The index of the passed Record. Returns -1 if not found.
7622      */
7623     indexOf : function(record){
7624         return this.data.indexOf(record);
7625     },
7626
7627     /**
7628      * Get the index within the cache of the Record with the passed id.
7629      * @param {String} id The id of the Record to find.
7630      * @return {Number} The index of the Record. Returns -1 if not found.
7631      */
7632     indexOfId : function(id){
7633         return this.data.indexOfKey(id);
7634     },
7635
7636     /**
7637      * Get the Record with the specified id.
7638      * @param {String} id The id of the Record to find.
7639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7640      */
7641     getById : function(id){
7642         return this.data.key(id);
7643     },
7644
7645     /**
7646      * Get the Record at the specified index.
7647      * @param {Number} index The index of the Record to find.
7648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7649      */
7650     getAt : function(index){
7651         return this.data.itemAt(index);
7652     },
7653
7654     /**
7655      * Returns a range of Records between specified indices.
7656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7658      * @return {Roo.data.Record[]} An array of Records
7659      */
7660     getRange : function(start, end){
7661         return this.data.getRange(start, end);
7662     },
7663
7664     // private
7665     storeOptions : function(o){
7666         o = Roo.apply({}, o);
7667         delete o.callback;
7668         delete o.scope;
7669         this.lastOptions = o;
7670     },
7671
7672     /**
7673      * Loads the Record cache from the configured Proxy using the configured Reader.
7674      * <p>
7675      * If using remote paging, then the first load call must specify the <em>start</em>
7676      * and <em>limit</em> properties in the options.params property to establish the initial
7677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7678      * <p>
7679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7680      * and this call will return before the new data has been loaded. Perform any post-processing
7681      * in a callback function, or in a "load" event handler.</strong>
7682      * <p>
7683      * @param {Object} options An object containing properties which control loading options:<ul>
7684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7686      * passed the following arguments:<ul>
7687      * <li>r : Roo.data.Record[]</li>
7688      * <li>options: Options object from the load call</li>
7689      * <li>success: Boolean success indicator</li></ul></li>
7690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7692      * </ul>
7693      */
7694     load : function(options){
7695         options = options || {};
7696         if(this.fireEvent("beforeload", this, options) !== false){
7697             this.storeOptions(options);
7698             var p = Roo.apply(options.params || {}, this.baseParams);
7699             // if meta was not loaded from remote source.. try requesting it.
7700             if (!this.reader.metaFromRemote) {
7701                 p._requestMeta = 1;
7702             }
7703             if(this.sortInfo && this.remoteSort){
7704                 var pn = this.paramNames;
7705                 p[pn["sort"]] = this.sortInfo.field;
7706                 p[pn["dir"]] = this.sortInfo.direction;
7707             }
7708             if (this.multiSort) {
7709                 var pn = this.paramNames;
7710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7711             }
7712             
7713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7714         }
7715     },
7716
7717     /**
7718      * Reloads the Record cache from the configured Proxy using the configured Reader and
7719      * the options from the last load operation performed.
7720      * @param {Object} options (optional) An object containing properties which may override the options
7721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7722      * the most recently used options are reused).
7723      */
7724     reload : function(options){
7725         this.load(Roo.applyIf(options||{}, this.lastOptions));
7726     },
7727
7728     // private
7729     // Called as a callback by the Reader during a load operation.
7730     loadRecords : function(o, options, success){
7731         if(!o || success === false){
7732             if(success !== false){
7733                 this.fireEvent("load", this, [], options, o);
7734             }
7735             if(options.callback){
7736                 options.callback.call(options.scope || this, [], options, false);
7737             }
7738             return;
7739         }
7740         // if data returned failure - throw an exception.
7741         if (o.success === false) {
7742             // show a message if no listener is registered.
7743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7745             }
7746             // loadmask wil be hooked into this..
7747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7748             return;
7749         }
7750         var r = o.records, t = o.totalRecords || r.length;
7751         
7752         this.fireEvent("beforeloadadd", this, r, options, o);
7753         
7754         if(!options || options.add !== true){
7755             if(this.pruneModifiedRecords){
7756                 this.modified = [];
7757             }
7758             for(var i = 0, len = r.length; i < len; i++){
7759                 r[i].join(this);
7760             }
7761             if(this.snapshot){
7762                 this.data = this.snapshot;
7763                 delete this.snapshot;
7764             }
7765             this.data.clear();
7766             this.data.addAll(r);
7767             this.totalLength = t;
7768             this.applySort();
7769             this.fireEvent("datachanged", this);
7770         }else{
7771             this.totalLength = Math.max(t, this.data.length+r.length);
7772             this.add(r);
7773         }
7774         this.fireEvent("load", this, r, options, o);
7775         if(options.callback){
7776             options.callback.call(options.scope || this, r, options, true);
7777         }
7778     },
7779
7780
7781     /**
7782      * Loads data from a passed data block. A Reader which understands the format of the data
7783      * must have been configured in the constructor.
7784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7787      */
7788     loadData : function(o, append){
7789         var r = this.reader.readRecords(o);
7790         this.loadRecords(r, {add: append}, true);
7791     },
7792
7793     /**
7794      * Gets the number of cached records.
7795      * <p>
7796      * <em>If using paging, this may not be the total size of the dataset. If the data object
7797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7798      * the data set size</em>
7799      */
7800     getCount : function(){
7801         return this.data.length || 0;
7802     },
7803
7804     /**
7805      * Gets the total number of records in the dataset as returned by the server.
7806      * <p>
7807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7808      * the dataset size</em>
7809      */
7810     getTotalCount : function(){
7811         return this.totalLength || 0;
7812     },
7813
7814     /**
7815      * Returns the sort state of the Store as an object with two properties:
7816      * <pre><code>
7817  field {String} The name of the field by which the Records are sorted
7818  direction {String} The sort order, "ASC" or "DESC"
7819      * </code></pre>
7820      */
7821     getSortState : function(){
7822         return this.sortInfo;
7823     },
7824
7825     // private
7826     applySort : function(){
7827         if(this.sortInfo && !this.remoteSort){
7828             var s = this.sortInfo, f = s.field;
7829             var st = this.fields.get(f).sortType;
7830             var fn = function(r1, r2){
7831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7833             };
7834             this.data.sort(s.direction, fn);
7835             if(this.snapshot && this.snapshot != this.data){
7836                 this.snapshot.sort(s.direction, fn);
7837             }
7838         }
7839     },
7840
7841     /**
7842      * Sets the default sort column and order to be used by the next load operation.
7843      * @param {String} fieldName The name of the field to sort by.
7844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7845      */
7846     setDefaultSort : function(field, dir){
7847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7848     },
7849
7850     /**
7851      * Sort the Records.
7852      * If remote sorting is used, the sort is performed on the server, and the cache is
7853      * reloaded. If local sorting is used, the cache is sorted internally.
7854      * @param {String} fieldName The name of the field to sort by.
7855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7856      */
7857     sort : function(fieldName, dir){
7858         var f = this.fields.get(fieldName);
7859         if(!dir){
7860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7861             
7862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7864             }else{
7865                 dir = f.sortDir;
7866             }
7867         }
7868         this.sortToggle[f.name] = dir;
7869         this.sortInfo = {field: f.name, direction: dir};
7870         if(!this.remoteSort){
7871             this.applySort();
7872             this.fireEvent("datachanged", this);
7873         }else{
7874             this.load(this.lastOptions);
7875         }
7876     },
7877
7878     /**
7879      * Calls the specified function for each of the Records in the cache.
7880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7881      * Returning <em>false</em> aborts and exits the iteration.
7882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7883      */
7884     each : function(fn, scope){
7885         this.data.each(fn, scope);
7886     },
7887
7888     /**
7889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7890      * (e.g., during paging).
7891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7892      */
7893     getModifiedRecords : function(){
7894         return this.modified;
7895     },
7896
7897     // private
7898     createFilterFn : function(property, value, anyMatch){
7899         if(!value.exec){ // not a regex
7900             value = String(value);
7901             if(value.length == 0){
7902                 return false;
7903             }
7904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7905         }
7906         return function(r){
7907             return value.test(r.data[property]);
7908         };
7909     },
7910
7911     /**
7912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7913      * @param {String} property A field on your records
7914      * @param {Number} start The record index to start at (defaults to 0)
7915      * @param {Number} end The last record index to include (defaults to length - 1)
7916      * @return {Number} The sum
7917      */
7918     sum : function(property, start, end){
7919         var rs = this.data.items, v = 0;
7920         start = start || 0;
7921         end = (end || end === 0) ? end : rs.length-1;
7922
7923         for(var i = start; i <= end; i++){
7924             v += (rs[i].data[property] || 0);
7925         }
7926         return v;
7927     },
7928
7929     /**
7930      * Filter the records by a specified property.
7931      * @param {String} field A field on your records
7932      * @param {String/RegExp} value Either a string that the field
7933      * should start with or a RegExp to test against the field
7934      * @param {Boolean} anyMatch True to match any part not just the beginning
7935      */
7936     filter : function(property, value, anyMatch){
7937         var fn = this.createFilterFn(property, value, anyMatch);
7938         return fn ? this.filterBy(fn) : this.clearFilter();
7939     },
7940
7941     /**
7942      * Filter by a function. The specified function will be called with each
7943      * record in this data source. If the function returns true the record is included,
7944      * otherwise it is filtered.
7945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7946      * @param {Object} scope (optional) The scope of the function (defaults to this)
7947      */
7948     filterBy : function(fn, scope){
7949         this.snapshot = this.snapshot || this.data;
7950         this.data = this.queryBy(fn, scope||this);
7951         this.fireEvent("datachanged", this);
7952     },
7953
7954     /**
7955      * Query the records by a specified property.
7956      * @param {String} field A field on your records
7957      * @param {String/RegExp} value Either a string that the field
7958      * should start with or a RegExp to test against the field
7959      * @param {Boolean} anyMatch True to match any part not just the beginning
7960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7961      */
7962     query : function(property, value, anyMatch){
7963         var fn = this.createFilterFn(property, value, anyMatch);
7964         return fn ? this.queryBy(fn) : this.data.clone();
7965     },
7966
7967     /**
7968      * Query by a function. The specified function will be called with each
7969      * record in this data source. If the function returns true the record is included
7970      * in the results.
7971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7972      * @param {Object} scope (optional) The scope of the function (defaults to this)
7973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7974      **/
7975     queryBy : function(fn, scope){
7976         var data = this.snapshot || this.data;
7977         return data.filterBy(fn, scope||this);
7978     },
7979
7980     /**
7981      * Collects unique values for a particular dataIndex from this store.
7982      * @param {String} dataIndex The property to collect
7983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7985      * @return {Array} An array of the unique values
7986      **/
7987     collect : function(dataIndex, allowNull, bypassFilter){
7988         var d = (bypassFilter === true && this.snapshot) ?
7989                 this.snapshot.items : this.data.items;
7990         var v, sv, r = [], l = {};
7991         for(var i = 0, len = d.length; i < len; i++){
7992             v = d[i].data[dataIndex];
7993             sv = String(v);
7994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7995                 l[sv] = true;
7996                 r[r.length] = v;
7997             }
7998         }
7999         return r;
8000     },
8001
8002     /**
8003      * Revert to a view of the Record cache with no filtering applied.
8004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8005      */
8006     clearFilter : function(suppressEvent){
8007         if(this.snapshot && this.snapshot != this.data){
8008             this.data = this.snapshot;
8009             delete this.snapshot;
8010             if(suppressEvent !== true){
8011                 this.fireEvent("datachanged", this);
8012             }
8013         }
8014     },
8015
8016     // private
8017     afterEdit : function(record){
8018         if(this.modified.indexOf(record) == -1){
8019             this.modified.push(record);
8020         }
8021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8022     },
8023     
8024     // private
8025     afterReject : function(record){
8026         this.modified.remove(record);
8027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8028     },
8029
8030     // private
8031     afterCommit : function(record){
8032         this.modified.remove(record);
8033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8034     },
8035
8036     /**
8037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8039      */
8040     commitChanges : function(){
8041         var m = this.modified.slice(0);
8042         this.modified = [];
8043         for(var i = 0, len = m.length; i < len; i++){
8044             m[i].commit();
8045         }
8046     },
8047
8048     /**
8049      * Cancel outstanding changes on all changed records.
8050      */
8051     rejectChanges : function(){
8052         var m = this.modified.slice(0);
8053         this.modified = [];
8054         for(var i = 0, len = m.length; i < len; i++){
8055             m[i].reject();
8056         }
8057     },
8058
8059     onMetaChange : function(meta, rtype, o){
8060         this.recordType = rtype;
8061         this.fields = rtype.prototype.fields;
8062         delete this.snapshot;
8063         this.sortInfo = meta.sortInfo || this.sortInfo;
8064         this.modified = [];
8065         this.fireEvent('metachange', this, this.reader.meta);
8066     },
8067     
8068     moveIndex : function(data, type)
8069     {
8070         var index = this.indexOf(data);
8071         
8072         var newIndex = index + type;
8073         
8074         this.remove(data);
8075         
8076         this.insert(newIndex, data);
8077         
8078     }
8079 });/*
8080  * Based on:
8081  * Ext JS Library 1.1.1
8082  * Copyright(c) 2006-2007, Ext JS, LLC.
8083  *
8084  * Originally Released Under LGPL - original licence link has changed is not relivant.
8085  *
8086  * Fork - LGPL
8087  * <script type="text/javascript">
8088  */
8089
8090 /**
8091  * @class Roo.data.SimpleStore
8092  * @extends Roo.data.Store
8093  * Small helper class to make creating Stores from Array data easier.
8094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8095  * @cfg {Array} fields An array of field definition objects, or field name strings.
8096  * @cfg {Array} data The multi-dimensional array of data
8097  * @constructor
8098  * @param {Object} config
8099  */
8100 Roo.data.SimpleStore = function(config){
8101     Roo.data.SimpleStore.superclass.constructor.call(this, {
8102         isLocal : true,
8103         reader: new Roo.data.ArrayReader({
8104                 id: config.id
8105             },
8106             Roo.data.Record.create(config.fields)
8107         ),
8108         proxy : new Roo.data.MemoryProxy(config.data)
8109     });
8110     this.load();
8111 };
8112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8113  * Based on:
8114  * Ext JS Library 1.1.1
8115  * Copyright(c) 2006-2007, Ext JS, LLC.
8116  *
8117  * Originally Released Under LGPL - original licence link has changed is not relivant.
8118  *
8119  * Fork - LGPL
8120  * <script type="text/javascript">
8121  */
8122
8123 /**
8124 /**
8125  * @extends Roo.data.Store
8126  * @class Roo.data.JsonStore
8127  * Small helper class to make creating Stores for JSON data easier. <br/>
8128 <pre><code>
8129 var store = new Roo.data.JsonStore({
8130     url: 'get-images.php',
8131     root: 'images',
8132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8133 });
8134 </code></pre>
8135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8136  * JsonReader and HttpProxy (unless inline data is provided).</b>
8137  * @cfg {Array} fields An array of field definition objects, or field name strings.
8138  * @constructor
8139  * @param {Object} config
8140  */
8141 Roo.data.JsonStore = function(c){
8142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8144         reader: new Roo.data.JsonReader(c, c.fields)
8145     }));
8146 };
8147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158  
8159 Roo.data.Field = function(config){
8160     if(typeof config == "string"){
8161         config = {name: config};
8162     }
8163     Roo.apply(this, config);
8164     
8165     if(!this.type){
8166         this.type = "auto";
8167     }
8168     
8169     var st = Roo.data.SortTypes;
8170     // named sortTypes are supported, here we look them up
8171     if(typeof this.sortType == "string"){
8172         this.sortType = st[this.sortType];
8173     }
8174     
8175     // set default sortType for strings and dates
8176     if(!this.sortType){
8177         switch(this.type){
8178             case "string":
8179                 this.sortType = st.asUCString;
8180                 break;
8181             case "date":
8182                 this.sortType = st.asDate;
8183                 break;
8184             default:
8185                 this.sortType = st.none;
8186         }
8187     }
8188
8189     // define once
8190     var stripRe = /[\$,%]/g;
8191
8192     // prebuilt conversion function for this field, instead of
8193     // switching every time we're reading a value
8194     if(!this.convert){
8195         var cv, dateFormat = this.dateFormat;
8196         switch(this.type){
8197             case "":
8198             case "auto":
8199             case undefined:
8200                 cv = function(v){ return v; };
8201                 break;
8202             case "string":
8203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8204                 break;
8205             case "int":
8206                 cv = function(v){
8207                     return v !== undefined && v !== null && v !== '' ?
8208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8209                     };
8210                 break;
8211             case "float":
8212                 cv = function(v){
8213                     return v !== undefined && v !== null && v !== '' ?
8214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8215                     };
8216                 break;
8217             case "bool":
8218             case "boolean":
8219                 cv = function(v){ return v === true || v === "true" || v == 1; };
8220                 break;
8221             case "date":
8222                 cv = function(v){
8223                     if(!v){
8224                         return '';
8225                     }
8226                     if(v instanceof Date){
8227                         return v;
8228                     }
8229                     if(dateFormat){
8230                         if(dateFormat == "timestamp"){
8231                             return new Date(v*1000);
8232                         }
8233                         return Date.parseDate(v, dateFormat);
8234                     }
8235                     var parsed = Date.parse(v);
8236                     return parsed ? new Date(parsed) : null;
8237                 };
8238              break;
8239             
8240         }
8241         this.convert = cv;
8242     }
8243 };
8244
8245 Roo.data.Field.prototype = {
8246     dateFormat: null,
8247     defaultValue: "",
8248     mapping: null,
8249     sortType : null,
8250     sortDir : "ASC"
8251 };/*
8252  * Based on:
8253  * Ext JS Library 1.1.1
8254  * Copyright(c) 2006-2007, Ext JS, LLC.
8255  *
8256  * Originally Released Under LGPL - original licence link has changed is not relivant.
8257  *
8258  * Fork - LGPL
8259  * <script type="text/javascript">
8260  */
8261  
8262 // Base class for reading structured data from a data source.  This class is intended to be
8263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8264
8265 /**
8266  * @class Roo.data.DataReader
8267  * Base class for reading structured data from a data source.  This class is intended to be
8268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8269  */
8270
8271 Roo.data.DataReader = function(meta, recordType){
8272     
8273     this.meta = meta;
8274     
8275     this.recordType = recordType instanceof Array ? 
8276         Roo.data.Record.create(recordType) : recordType;
8277 };
8278
8279 Roo.data.DataReader.prototype = {
8280      /**
8281      * Create an empty record
8282      * @param {Object} data (optional) - overlay some values
8283      * @return {Roo.data.Record} record created.
8284      */
8285     newRow :  function(d) {
8286         var da =  {};
8287         this.recordType.prototype.fields.each(function(c) {
8288             switch( c.type) {
8289                 case 'int' : da[c.name] = 0; break;
8290                 case 'date' : da[c.name] = new Date(); break;
8291                 case 'float' : da[c.name] = 0.0; break;
8292                 case 'boolean' : da[c.name] = false; break;
8293                 default : da[c.name] = ""; break;
8294             }
8295             
8296         });
8297         return new this.recordType(Roo.apply(da, d));
8298     }
8299     
8300 };/*
8301  * Based on:
8302  * Ext JS Library 1.1.1
8303  * Copyright(c) 2006-2007, Ext JS, LLC.
8304  *
8305  * Originally Released Under LGPL - original licence link has changed is not relivant.
8306  *
8307  * Fork - LGPL
8308  * <script type="text/javascript">
8309  */
8310
8311 /**
8312  * @class Roo.data.DataProxy
8313  * @extends Roo.data.Observable
8314  * This class is an abstract base class for implementations which provide retrieval of
8315  * unformatted data objects.<br>
8316  * <p>
8317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8318  * (of the appropriate type which knows how to parse the data object) to provide a block of
8319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8320  * <p>
8321  * Custom implementations must implement the load method as described in
8322  * {@link Roo.data.HttpProxy#load}.
8323  */
8324 Roo.data.DataProxy = function(){
8325     this.addEvents({
8326         /**
8327          * @event beforeload
8328          * Fires before a network request is made to retrieve a data object.
8329          * @param {Object} This DataProxy object.
8330          * @param {Object} params The params parameter to the load function.
8331          */
8332         beforeload : true,
8333         /**
8334          * @event load
8335          * Fires before the load method's callback is called.
8336          * @param {Object} This DataProxy object.
8337          * @param {Object} o The data object.
8338          * @param {Object} arg The callback argument object passed to the load function.
8339          */
8340         load : true,
8341         /**
8342          * @event loadexception
8343          * Fires if an Exception occurs during data retrieval.
8344          * @param {Object} This DataProxy object.
8345          * @param {Object} o The data object.
8346          * @param {Object} arg The callback argument object passed to the load function.
8347          * @param {Object} e The Exception.
8348          */
8349         loadexception : true
8350     });
8351     Roo.data.DataProxy.superclass.constructor.call(this);
8352 };
8353
8354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8355
8356     /**
8357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8358      */
8359 /*
8360  * Based on:
8361  * Ext JS Library 1.1.1
8362  * Copyright(c) 2006-2007, Ext JS, LLC.
8363  *
8364  * Originally Released Under LGPL - original licence link has changed is not relivant.
8365  *
8366  * Fork - LGPL
8367  * <script type="text/javascript">
8368  */
8369 /**
8370  * @class Roo.data.MemoryProxy
8371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8372  * to the Reader when its load method is called.
8373  * @constructor
8374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8375  */
8376 Roo.data.MemoryProxy = function(data){
8377     if (data.data) {
8378         data = data.data;
8379     }
8380     Roo.data.MemoryProxy.superclass.constructor.call(this);
8381     this.data = data;
8382 };
8383
8384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8385     /**
8386      * Load data from the requested source (in this case an in-memory
8387      * data object passed to the constructor), read the data object into
8388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8389      * process that block using the passed callback.
8390      * @param {Object} params This parameter is not used by the MemoryProxy class.
8391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8392      * object into a block of Roo.data.Records.
8393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8394      * The function must be passed <ul>
8395      * <li>The Record block object</li>
8396      * <li>The "arg" argument from the load function</li>
8397      * <li>A boolean success indicator</li>
8398      * </ul>
8399      * @param {Object} scope The scope in which to call the callback
8400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8401      */
8402     load : function(params, reader, callback, scope, arg){
8403         params = params || {};
8404         var result;
8405         try {
8406             result = reader.readRecords(this.data);
8407         }catch(e){
8408             this.fireEvent("loadexception", this, arg, null, e);
8409             callback.call(scope, null, arg, false);
8410             return;
8411         }
8412         callback.call(scope, result, arg, true);
8413     },
8414     
8415     // private
8416     update : function(params, records){
8417         
8418     }
8419 });/*
8420  * Based on:
8421  * Ext JS Library 1.1.1
8422  * Copyright(c) 2006-2007, Ext JS, LLC.
8423  *
8424  * Originally Released Under LGPL - original licence link has changed is not relivant.
8425  *
8426  * Fork - LGPL
8427  * <script type="text/javascript">
8428  */
8429 /**
8430  * @class Roo.data.HttpProxy
8431  * @extends Roo.data.DataProxy
8432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8433  * configured to reference a certain URL.<br><br>
8434  * <p>
8435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8436  * from which the running page was served.<br><br>
8437  * <p>
8438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8439  * <p>
8440  * Be aware that to enable the browser to parse an XML document, the server must set
8441  * the Content-Type header in the HTTP response to "text/xml".
8442  * @constructor
8443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8445  * will be used to make the request.
8446  */
8447 Roo.data.HttpProxy = function(conn){
8448     Roo.data.HttpProxy.superclass.constructor.call(this);
8449     // is conn a conn config or a real conn?
8450     this.conn = conn;
8451     this.useAjax = !conn || !conn.events;
8452   
8453 };
8454
8455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8456     // thse are take from connection...
8457     
8458     /**
8459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8460      */
8461     /**
8462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8463      * extra parameters to each request made by this object. (defaults to undefined)
8464      */
8465     /**
8466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8467      *  to each request made by this object. (defaults to undefined)
8468      */
8469     /**
8470      * @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)
8471      */
8472     /**
8473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8474      */
8475      /**
8476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8477      * @type Boolean
8478      */
8479   
8480
8481     /**
8482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8483      * @type Boolean
8484      */
8485     /**
8486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8488      * a finer-grained basis than the DataProxy events.
8489      */
8490     getConnection : function(){
8491         return this.useAjax ? Roo.Ajax : this.conn;
8492     },
8493
8494     /**
8495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8497      * process that block using the passed callback.
8498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8499      * for the request to the remote server.
8500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8501      * object into a block of Roo.data.Records.
8502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8503      * The function must be passed <ul>
8504      * <li>The Record block object</li>
8505      * <li>The "arg" argument from the load function</li>
8506      * <li>A boolean success indicator</li>
8507      * </ul>
8508      * @param {Object} scope The scope in which to call the callback
8509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8510      */
8511     load : function(params, reader, callback, scope, arg){
8512         if(this.fireEvent("beforeload", this, params) !== false){
8513             var  o = {
8514                 params : params || {},
8515                 request: {
8516                     callback : callback,
8517                     scope : scope,
8518                     arg : arg
8519                 },
8520                 reader: reader,
8521                 callback : this.loadResponse,
8522                 scope: this
8523             };
8524             if(this.useAjax){
8525                 Roo.applyIf(o, this.conn);
8526                 if(this.activeRequest){
8527                     Roo.Ajax.abort(this.activeRequest);
8528                 }
8529                 this.activeRequest = Roo.Ajax.request(o);
8530             }else{
8531                 this.conn.request(o);
8532             }
8533         }else{
8534             callback.call(scope||this, null, arg, false);
8535         }
8536     },
8537
8538     // private
8539     loadResponse : function(o, success, response){
8540         delete this.activeRequest;
8541         if(!success){
8542             this.fireEvent("loadexception", this, o, response);
8543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8544             return;
8545         }
8546         var result;
8547         try {
8548             result = o.reader.read(response);
8549         }catch(e){
8550             this.fireEvent("loadexception", this, o, response, e);
8551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8552             return;
8553         }
8554         
8555         this.fireEvent("load", this, o, o.request.arg);
8556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8557     },
8558
8559     // private
8560     update : function(dataSet){
8561
8562     },
8563
8564     // private
8565     updateResponse : function(dataSet){
8566
8567     }
8568 });/*
8569  * Based on:
8570  * Ext JS Library 1.1.1
8571  * Copyright(c) 2006-2007, Ext JS, LLC.
8572  *
8573  * Originally Released Under LGPL - original licence link has changed is not relivant.
8574  *
8575  * Fork - LGPL
8576  * <script type="text/javascript">
8577  */
8578
8579 /**
8580  * @class Roo.data.ScriptTagProxy
8581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8582  * other than the originating domain of the running page.<br><br>
8583  * <p>
8584  * <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
8585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8586  * <p>
8587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8588  * source code that is used as the source inside a &lt;script> tag.<br><br>
8589  * <p>
8590  * In order for the browser to process the returned data, the server must wrap the data object
8591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8593  * depending on whether the callback name was passed:
8594  * <p>
8595  * <pre><code>
8596 boolean scriptTag = false;
8597 String cb = request.getParameter("callback");
8598 if (cb != null) {
8599     scriptTag = true;
8600     response.setContentType("text/javascript");
8601 } else {
8602     response.setContentType("application/x-json");
8603 }
8604 Writer out = response.getWriter();
8605 if (scriptTag) {
8606     out.write(cb + "(");
8607 }
8608 out.print(dataBlock.toJsonString());
8609 if (scriptTag) {
8610     out.write(");");
8611 }
8612 </pre></code>
8613  *
8614  * @constructor
8615  * @param {Object} config A configuration object.
8616  */
8617 Roo.data.ScriptTagProxy = function(config){
8618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8619     Roo.apply(this, config);
8620     this.head = document.getElementsByTagName("head")[0];
8621 };
8622
8623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8624
8625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8626     /**
8627      * @cfg {String} url The URL from which to request the data object.
8628      */
8629     /**
8630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8631      */
8632     timeout : 30000,
8633     /**
8634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8635      * the server the name of the callback function set up by the load call to process the returned data object.
8636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8637      * javascript output which calls this named function passing the data object as its only parameter.
8638      */
8639     callbackParam : "callback",
8640     /**
8641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8642      * name to the request.
8643      */
8644     nocache : true,
8645
8646     /**
8647      * Load data from the configured URL, read the data object into
8648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8649      * process that block using the passed callback.
8650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8651      * for the request to the remote server.
8652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8653      * object into a block of Roo.data.Records.
8654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8655      * The function must be passed <ul>
8656      * <li>The Record block object</li>
8657      * <li>The "arg" argument from the load function</li>
8658      * <li>A boolean success indicator</li>
8659      * </ul>
8660      * @param {Object} scope The scope in which to call the callback
8661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8662      */
8663     load : function(params, reader, callback, scope, arg){
8664         if(this.fireEvent("beforeload", this, params) !== false){
8665
8666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8667
8668             var url = this.url;
8669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8670             if(this.nocache){
8671                 url += "&_dc=" + (new Date().getTime());
8672             }
8673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8674             var trans = {
8675                 id : transId,
8676                 cb : "stcCallback"+transId,
8677                 scriptId : "stcScript"+transId,
8678                 params : params,
8679                 arg : arg,
8680                 url : url,
8681                 callback : callback,
8682                 scope : scope,
8683                 reader : reader
8684             };
8685             var conn = this;
8686
8687             window[trans.cb] = function(o){
8688                 conn.handleResponse(o, trans);
8689             };
8690
8691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8692
8693             if(this.autoAbort !== false){
8694                 this.abort();
8695             }
8696
8697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8698
8699             var script = document.createElement("script");
8700             script.setAttribute("src", url);
8701             script.setAttribute("type", "text/javascript");
8702             script.setAttribute("id", trans.scriptId);
8703             this.head.appendChild(script);
8704
8705             this.trans = trans;
8706         }else{
8707             callback.call(scope||this, null, arg, false);
8708         }
8709     },
8710
8711     // private
8712     isLoading : function(){
8713         return this.trans ? true : false;
8714     },
8715
8716     /**
8717      * Abort the current server request.
8718      */
8719     abort : function(){
8720         if(this.isLoading()){
8721             this.destroyTrans(this.trans);
8722         }
8723     },
8724
8725     // private
8726     destroyTrans : function(trans, isLoaded){
8727         this.head.removeChild(document.getElementById(trans.scriptId));
8728         clearTimeout(trans.timeoutId);
8729         if(isLoaded){
8730             window[trans.cb] = undefined;
8731             try{
8732                 delete window[trans.cb];
8733             }catch(e){}
8734         }else{
8735             // if hasn't been loaded, wait for load to remove it to prevent script error
8736             window[trans.cb] = function(){
8737                 window[trans.cb] = undefined;
8738                 try{
8739                     delete window[trans.cb];
8740                 }catch(e){}
8741             };
8742         }
8743     },
8744
8745     // private
8746     handleResponse : function(o, trans){
8747         this.trans = false;
8748         this.destroyTrans(trans, true);
8749         var result;
8750         try {
8751             result = trans.reader.readRecords(o);
8752         }catch(e){
8753             this.fireEvent("loadexception", this, o, trans.arg, e);
8754             trans.callback.call(trans.scope||window, null, trans.arg, false);
8755             return;
8756         }
8757         this.fireEvent("load", this, o, trans.arg);
8758         trans.callback.call(trans.scope||window, result, trans.arg, true);
8759     },
8760
8761     // private
8762     handleFailure : function(trans){
8763         this.trans = false;
8764         this.destroyTrans(trans, false);
8765         this.fireEvent("loadexception", this, null, trans.arg);
8766         trans.callback.call(trans.scope||window, null, trans.arg, false);
8767     }
8768 });/*
8769  * Based on:
8770  * Ext JS Library 1.1.1
8771  * Copyright(c) 2006-2007, Ext JS, LLC.
8772  *
8773  * Originally Released Under LGPL - original licence link has changed is not relivant.
8774  *
8775  * Fork - LGPL
8776  * <script type="text/javascript">
8777  */
8778
8779 /**
8780  * @class Roo.data.JsonReader
8781  * @extends Roo.data.DataReader
8782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8783  * based on mappings in a provided Roo.data.Record constructor.
8784  * 
8785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8786  * in the reply previously. 
8787  * 
8788  * <p>
8789  * Example code:
8790  * <pre><code>
8791 var RecordDef = Roo.data.Record.create([
8792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8794 ]);
8795 var myReader = new Roo.data.JsonReader({
8796     totalProperty: "results",    // The property which contains the total dataset size (optional)
8797     root: "rows",                // The property which contains an Array of row objects
8798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8799 }, RecordDef);
8800 </code></pre>
8801  * <p>
8802  * This would consume a JSON file like this:
8803  * <pre><code>
8804 { 'results': 2, 'rows': [
8805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8807 }
8808 </code></pre>
8809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8811  * paged from the remote server.
8812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8813  * @cfg {String} root name of the property which contains the Array of row objects.
8814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8815  * @constructor
8816  * Create a new JsonReader
8817  * @param {Object} meta Metadata configuration options
8818  * @param {Object} recordType Either an Array of field definition objects,
8819  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8820  */
8821 Roo.data.JsonReader = function(meta, recordType){
8822     
8823     meta = meta || {};
8824     // set some defaults:
8825     Roo.applyIf(meta, {
8826         totalProperty: 'total',
8827         successProperty : 'success',
8828         root : 'data',
8829         id : 'id'
8830     });
8831     
8832     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8833 };
8834 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8835     
8836     /**
8837      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8838      * Used by Store query builder to append _requestMeta to params.
8839      * 
8840      */
8841     metaFromRemote : false,
8842     /**
8843      * This method is only used by a DataProxy which has retrieved data from a remote server.
8844      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8845      * @return {Object} data A data block which is used by an Roo.data.Store object as
8846      * a cache of Roo.data.Records.
8847      */
8848     read : function(response){
8849         var json = response.responseText;
8850        
8851         var o = /* eval:var:o */ eval("("+json+")");
8852         if(!o) {
8853             throw {message: "JsonReader.read: Json object not found"};
8854         }
8855         
8856         if(o.metaData){
8857             
8858             delete this.ef;
8859             this.metaFromRemote = true;
8860             this.meta = o.metaData;
8861             this.recordType = Roo.data.Record.create(o.metaData.fields);
8862             this.onMetaChange(this.meta, this.recordType, o);
8863         }
8864         return this.readRecords(o);
8865     },
8866
8867     // private function a store will implement
8868     onMetaChange : function(meta, recordType, o){
8869
8870     },
8871
8872     /**
8873          * @ignore
8874          */
8875     simpleAccess: function(obj, subsc) {
8876         return obj[subsc];
8877     },
8878
8879         /**
8880          * @ignore
8881          */
8882     getJsonAccessor: function(){
8883         var re = /[\[\.]/;
8884         return function(expr) {
8885             try {
8886                 return(re.test(expr))
8887                     ? new Function("obj", "return obj." + expr)
8888                     : function(obj){
8889                         return obj[expr];
8890                     };
8891             } catch(e){}
8892             return Roo.emptyFn;
8893         };
8894     }(),
8895
8896     /**
8897      * Create a data block containing Roo.data.Records from an XML document.
8898      * @param {Object} o An object which contains an Array of row objects in the property specified
8899      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8900      * which contains the total size of the dataset.
8901      * @return {Object} data A data block which is used by an Roo.data.Store object as
8902      * a cache of Roo.data.Records.
8903      */
8904     readRecords : function(o){
8905         /**
8906          * After any data loads, the raw JSON data is available for further custom processing.
8907          * @type Object
8908          */
8909         this.o = o;
8910         var s = this.meta, Record = this.recordType,
8911             f = Record.prototype.fields, fi = f.items, fl = f.length;
8912
8913 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8914         if (!this.ef) {
8915             if(s.totalProperty) {
8916                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8917                 }
8918                 if(s.successProperty) {
8919                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8920                 }
8921                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8922                 if (s.id) {
8923                         var g = this.getJsonAccessor(s.id);
8924                         this.getId = function(rec) {
8925                                 var r = g(rec);
8926                                 return (r === undefined || r === "") ? null : r;
8927                         };
8928                 } else {
8929                         this.getId = function(){return null;};
8930                 }
8931             this.ef = [];
8932             for(var jj = 0; jj < fl; jj++){
8933                 f = fi[jj];
8934                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8935                 this.ef[jj] = this.getJsonAccessor(map);
8936             }
8937         }
8938
8939         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8940         if(s.totalProperty){
8941             var vt = parseInt(this.getTotal(o), 10);
8942             if(!isNaN(vt)){
8943                 totalRecords = vt;
8944             }
8945         }
8946         if(s.successProperty){
8947             var vs = this.getSuccess(o);
8948             if(vs === false || vs === 'false'){
8949                 success = false;
8950             }
8951         }
8952         var records = [];
8953             for(var i = 0; i < c; i++){
8954                     var n = root[i];
8955                 var values = {};
8956                 var id = this.getId(n);
8957                 for(var j = 0; j < fl; j++){
8958                     f = fi[j];
8959                 var v = this.ef[j](n);
8960                 if (!f.convert) {
8961                     Roo.log('missing convert for ' + f.name);
8962                     Roo.log(f);
8963                     continue;
8964                 }
8965                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8966                 }
8967                 var record = new Record(values, id);
8968                 record.json = n;
8969                 records[i] = record;
8970             }
8971             return {
8972             raw : o,
8973                 success : success,
8974                 records : records,
8975                 totalRecords : totalRecords
8976             };
8977     }
8978 });/*
8979  * Based on:
8980  * Ext JS Library 1.1.1
8981  * Copyright(c) 2006-2007, Ext JS, LLC.
8982  *
8983  * Originally Released Under LGPL - original licence link has changed is not relivant.
8984  *
8985  * Fork - LGPL
8986  * <script type="text/javascript">
8987  */
8988
8989 /**
8990  * @class Roo.data.ArrayReader
8991  * @extends Roo.data.DataReader
8992  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8993  * Each element of that Array represents a row of data fields. The
8994  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8995  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8996  * <p>
8997  * Example code:.
8998  * <pre><code>
8999 var RecordDef = Roo.data.Record.create([
9000     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9001     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9002 ]);
9003 var myReader = new Roo.data.ArrayReader({
9004     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9005 }, RecordDef);
9006 </code></pre>
9007  * <p>
9008  * This would consume an Array like this:
9009  * <pre><code>
9010 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9011   </code></pre>
9012  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9013  * @constructor
9014  * Create a new JsonReader
9015  * @param {Object} meta Metadata configuration options.
9016  * @param {Object} recordType Either an Array of field definition objects
9017  * as specified to {@link Roo.data.Record#create},
9018  * or an {@link Roo.data.Record} object
9019  * created using {@link Roo.data.Record#create}.
9020  */
9021 Roo.data.ArrayReader = function(meta, recordType){
9022     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9023 };
9024
9025 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9026     /**
9027      * Create a data block containing Roo.data.Records from an XML document.
9028      * @param {Object} o An Array of row objects which represents the dataset.
9029      * @return {Object} data A data block which is used by an Roo.data.Store object as
9030      * a cache of Roo.data.Records.
9031      */
9032     readRecords : function(o){
9033         var sid = this.meta ? this.meta.id : null;
9034         var recordType = this.recordType, fields = recordType.prototype.fields;
9035         var records = [];
9036         var root = o;
9037             for(var i = 0; i < root.length; i++){
9038                     var n = root[i];
9039                 var values = {};
9040                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9041                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9042                 var f = fields.items[j];
9043                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9044                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9045                 v = f.convert(v);
9046                 values[f.name] = v;
9047             }
9048                 var record = new recordType(values, id);
9049                 record.json = n;
9050                 records[records.length] = record;
9051             }
9052             return {
9053                 records : records,
9054                 totalRecords : records.length
9055             };
9056     }
9057 });/*
9058  * - LGPL
9059  * * 
9060  */
9061
9062 /**
9063  * @class Roo.bootstrap.ComboBox
9064  * @extends Roo.bootstrap.TriggerField
9065  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9066  * @cfg {Boolean} append (true|false) default false
9067  * @constructor
9068  * Create a new ComboBox.
9069  * @param {Object} config Configuration options
9070  */
9071 Roo.bootstrap.ComboBox = function(config){
9072     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9073     this.addEvents({
9074         /**
9075          * @event expand
9076          * Fires when the dropdown list is expanded
9077              * @param {Roo.bootstrap.ComboBox} combo This combo box
9078              */
9079         'expand' : true,
9080         /**
9081          * @event collapse
9082          * Fires when the dropdown list is collapsed
9083              * @param {Roo.bootstrap.ComboBox} combo This combo box
9084              */
9085         'collapse' : true,
9086         /**
9087          * @event beforeselect
9088          * Fires before a list item is selected. Return false to cancel the selection.
9089              * @param {Roo.bootstrap.ComboBox} combo This combo box
9090              * @param {Roo.data.Record} record The data record returned from the underlying store
9091              * @param {Number} index The index of the selected item in the dropdown list
9092              */
9093         'beforeselect' : true,
9094         /**
9095          * @event select
9096          * Fires when a list item is selected
9097              * @param {Roo.bootstrap.ComboBox} combo This combo box
9098              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9099              * @param {Number} index The index of the selected item in the dropdown list
9100              */
9101         'select' : true,
9102         /**
9103          * @event beforequery
9104          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9105          * The event object passed has these properties:
9106              * @param {Roo.bootstrap.ComboBox} combo This combo box
9107              * @param {String} query The query
9108              * @param {Boolean} forceAll true to force "all" query
9109              * @param {Boolean} cancel true to cancel the query
9110              * @param {Object} e The query event object
9111              */
9112         'beforequery': true,
9113          /**
9114          * @event add
9115          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9116              * @param {Roo.bootstrap.ComboBox} combo This combo box
9117              */
9118         'add' : true,
9119         /**
9120          * @event edit
9121          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9122              * @param {Roo.bootstrap.ComboBox} combo This combo box
9123              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9124              */
9125         'edit' : true,
9126         /**
9127          * @event remove
9128          * Fires when the remove value from the combobox array
9129              * @param {Roo.bootstrap.ComboBox} combo This combo box
9130              */
9131         'remove' : true
9132         
9133     });
9134     
9135     
9136     this.selectedIndex = -1;
9137     if(this.mode == 'local'){
9138         if(config.queryDelay === undefined){
9139             this.queryDelay = 10;
9140         }
9141         if(config.minChars === undefined){
9142             this.minChars = 0;
9143         }
9144     }
9145 };
9146
9147 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9148      
9149     /**
9150      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9151      * rendering into an Roo.Editor, defaults to false)
9152      */
9153     /**
9154      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9155      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9156      */
9157     /**
9158      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9159      */
9160     /**
9161      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9162      * the dropdown list (defaults to undefined, with no header element)
9163      */
9164
9165      /**
9166      * @cfg {String/Roo.Template} tpl The template to use to render the output
9167      */
9168      
9169      /**
9170      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9171      */
9172     listWidth: undefined,
9173     /**
9174      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9175      * mode = 'remote' or 'text' if mode = 'local')
9176      */
9177     displayField: undefined,
9178     /**
9179      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9180      * mode = 'remote' or 'value' if mode = 'local'). 
9181      * Note: use of a valueField requires the user make a selection
9182      * in order for a value to be mapped.
9183      */
9184     valueField: undefined,
9185     
9186     
9187     /**
9188      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9189      * field's data value (defaults to the underlying DOM element's name)
9190      */
9191     hiddenName: undefined,
9192     /**
9193      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9194      */
9195     listClass: '',
9196     /**
9197      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9198      */
9199     selectedClass: 'active',
9200     
9201     /**
9202      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9203      */
9204     shadow:'sides',
9205     /**
9206      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9207      * anchor positions (defaults to 'tl-bl')
9208      */
9209     listAlign: 'tl-bl?',
9210     /**
9211      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9212      */
9213     maxHeight: 300,
9214     /**
9215      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9216      * query specified by the allQuery config option (defaults to 'query')
9217      */
9218     triggerAction: 'query',
9219     /**
9220      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9221      * (defaults to 4, does not apply if editable = false)
9222      */
9223     minChars : 4,
9224     /**
9225      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9226      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9227      */
9228     typeAhead: false,
9229     /**
9230      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9231      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9232      */
9233     queryDelay: 500,
9234     /**
9235      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9236      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9237      */
9238     pageSize: 0,
9239     /**
9240      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9241      * when editable = true (defaults to false)
9242      */
9243     selectOnFocus:false,
9244     /**
9245      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9246      */
9247     queryParam: 'query',
9248     /**
9249      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9250      * when mode = 'remote' (defaults to 'Loading...')
9251      */
9252     loadingText: 'Loading...',
9253     /**
9254      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9255      */
9256     resizable: false,
9257     /**
9258      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9259      */
9260     handleHeight : 8,
9261     /**
9262      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9263      * traditional select (defaults to true)
9264      */
9265     editable: true,
9266     /**
9267      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9268      */
9269     allQuery: '',
9270     /**
9271      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9272      */
9273     mode: 'remote',
9274     /**
9275      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9276      * listWidth has a higher value)
9277      */
9278     minListWidth : 70,
9279     /**
9280      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9281      * allow the user to set arbitrary text into the field (defaults to false)
9282      */
9283     forceSelection:false,
9284     /**
9285      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9286      * if typeAhead = true (defaults to 250)
9287      */
9288     typeAheadDelay : 250,
9289     /**
9290      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9291      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9292      */
9293     valueNotFoundText : undefined,
9294     /**
9295      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9296      */
9297     blockFocus : false,
9298     
9299     /**
9300      * @cfg {Boolean} disableClear Disable showing of clear button.
9301      */
9302     disableClear : false,
9303     /**
9304      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9305      */
9306     alwaysQuery : false,
9307     
9308     /**
9309      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9310      */
9311     multiple : false,
9312     
9313     //private
9314     addicon : false,
9315     editicon: false,
9316     
9317     page: 0,
9318     hasQuery: false,
9319     append: false,
9320     loadNext: false,
9321     item: [],
9322     
9323     // element that contains real text value.. (when hidden is used..)
9324      
9325     // private
9326     initEvents: function(){
9327         
9328         if (!this.store) {
9329             throw "can not find store for combo";
9330         }
9331         this.store = Roo.factory(this.store, Roo.data);
9332         
9333         
9334         
9335         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9336         
9337         
9338         if(this.hiddenName){
9339             
9340             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9341             
9342             this.hiddenField.dom.value =
9343                 this.hiddenValue !== undefined ? this.hiddenValue :
9344                 this.value !== undefined ? this.value : '';
9345
9346             // prevent input submission
9347             this.el.dom.removeAttribute('name');
9348             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9349              
9350              
9351         }
9352         //if(Roo.isGecko){
9353         //    this.el.dom.setAttribute('autocomplete', 'off');
9354         //}
9355
9356         var cls = 'x-combo-list';
9357         this.list = this.el.select('ul.dropdown-menu',true).first();
9358
9359         //this.list = new Roo.Layer({
9360         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9361         //});
9362         
9363         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9364         this.list.setWidth(lw);
9365         
9366         this.list.on('mouseover', this.onViewOver, this);
9367         this.list.on('mousemove', this.onViewMove, this);
9368         
9369         this.list.on('scroll', this.onViewScroll, this);
9370         
9371         /*
9372         this.list.swallowEvent('mousewheel');
9373         this.assetHeight = 0;
9374
9375         if(this.title){
9376             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9377             this.assetHeight += this.header.getHeight();
9378         }
9379
9380         this.innerList = this.list.createChild({cls:cls+'-inner'});
9381         this.innerList.on('mouseover', this.onViewOver, this);
9382         this.innerList.on('mousemove', this.onViewMove, this);
9383         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9384         
9385         if(this.allowBlank && !this.pageSize && !this.disableClear){
9386             this.footer = this.list.createChild({cls:cls+'-ft'});
9387             this.pageTb = new Roo.Toolbar(this.footer);
9388            
9389         }
9390         if(this.pageSize){
9391             this.footer = this.list.createChild({cls:cls+'-ft'});
9392             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9393                     {pageSize: this.pageSize});
9394             
9395         }
9396         
9397         if (this.pageTb && this.allowBlank && !this.disableClear) {
9398             var _this = this;
9399             this.pageTb.add(new Roo.Toolbar.Fill(), {
9400                 cls: 'x-btn-icon x-btn-clear',
9401                 text: '&#160;',
9402                 handler: function()
9403                 {
9404                     _this.collapse();
9405                     _this.clearValue();
9406                     _this.onSelect(false, -1);
9407                 }
9408             });
9409         }
9410         if (this.footer) {
9411             this.assetHeight += this.footer.getHeight();
9412         }
9413         */
9414             
9415         if(!this.tpl){
9416             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9417         }
9418
9419         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9420             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9421         });
9422         //this.view.wrapEl.setDisplayed(false);
9423         this.view.on('click', this.onViewClick, this);
9424         
9425         
9426         
9427         this.store.on('beforeload', this.onBeforeLoad, this);
9428         this.store.on('load', this.onLoad, this);
9429         this.store.on('loadexception', this.onLoadException, this);
9430         /*
9431         if(this.resizable){
9432             this.resizer = new Roo.Resizable(this.list,  {
9433                pinned:true, handles:'se'
9434             });
9435             this.resizer.on('resize', function(r, w, h){
9436                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9437                 this.listWidth = w;
9438                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9439                 this.restrictHeight();
9440             }, this);
9441             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9442         }
9443         */
9444         if(!this.editable){
9445             this.editable = true;
9446             this.setEditable(false);
9447         }
9448         
9449         /*
9450         
9451         if (typeof(this.events.add.listeners) != 'undefined') {
9452             
9453             this.addicon = this.wrap.createChild(
9454                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9455        
9456             this.addicon.on('click', function(e) {
9457                 this.fireEvent('add', this);
9458             }, this);
9459         }
9460         if (typeof(this.events.edit.listeners) != 'undefined') {
9461             
9462             this.editicon = this.wrap.createChild(
9463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9464             if (this.addicon) {
9465                 this.editicon.setStyle('margin-left', '40px');
9466             }
9467             this.editicon.on('click', function(e) {
9468                 
9469                 // we fire even  if inothing is selected..
9470                 this.fireEvent('edit', this, this.lastData );
9471                 
9472             }, this);
9473         }
9474         */
9475         
9476         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9477             "up" : function(e){
9478                 this.inKeyMode = true;
9479                 this.selectPrev();
9480             },
9481
9482             "down" : function(e){
9483                 if(!this.isExpanded()){
9484                     this.onTriggerClick();
9485                 }else{
9486                     this.inKeyMode = true;
9487                     this.selectNext();
9488                 }
9489             },
9490
9491             "enter" : function(e){
9492                 this.onViewClick();
9493                 //return true;
9494             },
9495
9496             "esc" : function(e){
9497                 this.collapse();
9498             },
9499
9500             "tab" : function(e){
9501                 this.collapse();
9502                 
9503                 if(this.fireEvent("specialkey", this, e)){
9504                     this.onViewClick(false);
9505                 }
9506                 
9507                 return true;
9508             },
9509
9510             scope : this,
9511
9512             doRelay : function(foo, bar, hname){
9513                 if(hname == 'down' || this.scope.isExpanded()){
9514                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9515                 }
9516                 return true;
9517             },
9518
9519             forceKeyDown: true
9520         });
9521         
9522         
9523         this.queryDelay = Math.max(this.queryDelay || 10,
9524                 this.mode == 'local' ? 10 : 250);
9525         
9526         
9527         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9528         
9529         if(this.typeAhead){
9530             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9531         }
9532         if(this.editable !== false){
9533             this.inputEl().on("keyup", this.onKeyUp, this);
9534         }
9535         if(this.forceSelection){
9536             this.inputEl().on('blur', this.doForce, this);
9537         }
9538         
9539         if(this.multiple){
9540             this.choices = this.el.select('ul.select2-choices', true).first();
9541             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9542         }
9543     },
9544
9545     onDestroy : function(){
9546         if(this.view){
9547             this.view.setStore(null);
9548             this.view.el.removeAllListeners();
9549             this.view.el.remove();
9550             this.view.purgeListeners();
9551         }
9552         if(this.list){
9553             this.list.dom.innerHTML  = '';
9554         }
9555         if(this.store){
9556             this.store.un('beforeload', this.onBeforeLoad, this);
9557             this.store.un('load', this.onLoad, this);
9558             this.store.un('loadexception', this.onLoadException, this);
9559         }
9560         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9561     },
9562
9563     // private
9564     fireKey : function(e){
9565         if(e.isNavKeyPress() && !this.list.isVisible()){
9566             this.fireEvent("specialkey", this, e);
9567         }
9568     },
9569
9570     // private
9571     onResize: function(w, h){
9572 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9573 //        
9574 //        if(typeof w != 'number'){
9575 //            // we do not handle it!?!?
9576 //            return;
9577 //        }
9578 //        var tw = this.trigger.getWidth();
9579 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9580 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9581 //        var x = w - tw;
9582 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9583 //            
9584 //        //this.trigger.setStyle('left', x+'px');
9585 //        
9586 //        if(this.list && this.listWidth === undefined){
9587 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9588 //            this.list.setWidth(lw);
9589 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9590 //        }
9591         
9592     
9593         
9594     },
9595
9596     /**
9597      * Allow or prevent the user from directly editing the field text.  If false is passed,
9598      * the user will only be able to select from the items defined in the dropdown list.  This method
9599      * is the runtime equivalent of setting the 'editable' config option at config time.
9600      * @param {Boolean} value True to allow the user to directly edit the field text
9601      */
9602     setEditable : function(value){
9603         if(value == this.editable){
9604             return;
9605         }
9606         this.editable = value;
9607         if(!value){
9608             this.inputEl().dom.setAttribute('readOnly', true);
9609             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9610             this.inputEl().addClass('x-combo-noedit');
9611         }else{
9612             this.inputEl().dom.setAttribute('readOnly', false);
9613             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9614             this.inputEl().removeClass('x-combo-noedit');
9615         }
9616     },
9617
9618     // private
9619     
9620     onBeforeLoad : function(combo,opts){
9621         if(!this.hasFocus){
9622             return;
9623         }
9624          if (!opts.add) {
9625             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9626          }
9627         this.restrictHeight();
9628         this.selectedIndex = -1;
9629     },
9630
9631     // private
9632     onLoad : function(){
9633         
9634         this.hasQuery = false;
9635         
9636         if(!this.hasFocus){
9637             return;
9638         }
9639         
9640         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9641             this.loading.hide();
9642         }
9643         
9644         if(this.store.getCount() > 0){
9645             this.expand();
9646             this.restrictHeight();
9647             if(this.lastQuery == this.allQuery){
9648                 if(this.editable){
9649                     this.inputEl().dom.select();
9650                 }
9651                 if(!this.selectByValue(this.value, true)){
9652                     this.select(0, true);
9653                 }
9654             }else{
9655                 this.selectNext();
9656                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9657                     this.taTask.delay(this.typeAheadDelay);
9658                 }
9659             }
9660         }else{
9661             this.onEmptyResults();
9662         }
9663         
9664         //this.el.focus();
9665     },
9666     // private
9667     onLoadException : function()
9668     {
9669         this.hasQuery = false;
9670         
9671         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9672             this.loading.hide();
9673         }
9674         
9675         this.collapse();
9676         Roo.log(this.store.reader.jsonData);
9677         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9678             // fixme
9679             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9680         }
9681         
9682         
9683     },
9684     // private
9685     onTypeAhead : function(){
9686         if(this.store.getCount() > 0){
9687             var r = this.store.getAt(0);
9688             var newValue = r.data[this.displayField];
9689             var len = newValue.length;
9690             var selStart = this.getRawValue().length;
9691             
9692             if(selStart != len){
9693                 this.setRawValue(newValue);
9694                 this.selectText(selStart, newValue.length);
9695             }
9696         }
9697     },
9698
9699     // private
9700     onSelect : function(record, index){
9701         
9702         if(this.fireEvent('beforeselect', this, record, index) !== false){
9703         
9704             this.setFromData(index > -1 ? record.data : false);
9705             
9706             this.collapse();
9707             this.fireEvent('select', this, record, index);
9708         }
9709     },
9710
9711     /**
9712      * Returns the currently selected field value or empty string if no value is set.
9713      * @return {String} value The selected value
9714      */
9715     getValue : function(){
9716         
9717         if(this.multiple){
9718             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9719         }
9720         
9721         if(this.valueField){
9722             return typeof this.value != 'undefined' ? this.value : '';
9723         }else{
9724             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9725         }
9726     },
9727
9728     /**
9729      * Clears any text/value currently set in the field
9730      */
9731     clearValue : function(){
9732         if(this.hiddenField){
9733             this.hiddenField.dom.value = '';
9734         }
9735         this.value = '';
9736         this.setRawValue('');
9737         this.lastSelectionText = '';
9738         
9739     },
9740
9741     /**
9742      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9743      * will be displayed in the field.  If the value does not match the data value of an existing item,
9744      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9745      * Otherwise the field will be blank (although the value will still be set).
9746      * @param {String} value The value to match
9747      */
9748     setValue : function(v){
9749         if(this.multiple){
9750             this.syncValue();
9751             return;
9752         }
9753         
9754         var text = v;
9755         if(this.valueField){
9756             var r = this.findRecord(this.valueField, v);
9757             if(r){
9758                 text = r.data[this.displayField];
9759             }else if(this.valueNotFoundText !== undefined){
9760                 text = this.valueNotFoundText;
9761             }
9762         }
9763         this.lastSelectionText = text;
9764         if(this.hiddenField){
9765             this.hiddenField.dom.value = v;
9766         }
9767         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9768         this.value = v;
9769     },
9770     /**
9771      * @property {Object} the last set data for the element
9772      */
9773     
9774     lastData : false,
9775     /**
9776      * Sets the value of the field based on a object which is related to the record format for the store.
9777      * @param {Object} value the value to set as. or false on reset?
9778      */
9779     setFromData : function(o){
9780         
9781         if(this.multiple){
9782             this.addItem(o);
9783             return;
9784         }
9785             
9786         var dv = ''; // display value
9787         var vv = ''; // value value..
9788         this.lastData = o;
9789         if (this.displayField) {
9790             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9791         } else {
9792             // this is an error condition!!!
9793             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9794         }
9795         
9796         if(this.valueField){
9797             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9798         }
9799         
9800         if(this.hiddenField){
9801             this.hiddenField.dom.value = vv;
9802             
9803             this.lastSelectionText = dv;
9804             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9805             this.value = vv;
9806             return;
9807         }
9808         // no hidden field.. - we store the value in 'value', but still display
9809         // display field!!!!
9810         this.lastSelectionText = dv;
9811         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9812         this.value = vv;
9813         
9814         
9815     },
9816     // private
9817     reset : function(){
9818         // overridden so that last data is reset..
9819         this.setValue(this.originalValue);
9820         this.clearInvalid();
9821         this.lastData = false;
9822         if (this.view) {
9823             this.view.clearSelections();
9824         }
9825     },
9826     // private
9827     findRecord : function(prop, value){
9828         var record;
9829         if(this.store.getCount() > 0){
9830             this.store.each(function(r){
9831                 if(r.data[prop] == value){
9832                     record = r;
9833                     return false;
9834                 }
9835                 return true;
9836             });
9837         }
9838         return record;
9839     },
9840     
9841     getName: function()
9842     {
9843         // returns hidden if it's set..
9844         if (!this.rendered) {return ''};
9845         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9846         
9847     },
9848     // private
9849     onViewMove : function(e, t){
9850         this.inKeyMode = false;
9851     },
9852
9853     // private
9854     onViewOver : function(e, t){
9855         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9856             return;
9857         }
9858         var item = this.view.findItemFromChild(t);
9859         if(item){
9860             var index = this.view.indexOf(item);
9861             this.select(index, false);
9862         }
9863     },
9864
9865     // private
9866     onViewClick : function(doFocus)
9867     {
9868         var index = this.view.getSelectedIndexes()[0];
9869         var r = this.store.getAt(index);
9870         if(r){
9871             this.onSelect(r, index);
9872         }
9873         if(doFocus !== false && !this.blockFocus){
9874             this.inputEl().focus();
9875         }
9876     },
9877
9878     // private
9879     restrictHeight : function(){
9880         //this.innerList.dom.style.height = '';
9881         //var inner = this.innerList.dom;
9882         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9883         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9884         //this.list.beginUpdate();
9885         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9886         this.list.alignTo(this.inputEl(), this.listAlign);
9887         //this.list.endUpdate();
9888     },
9889
9890     // private
9891     onEmptyResults : function(){
9892         this.collapse();
9893     },
9894
9895     /**
9896      * Returns true if the dropdown list is expanded, else false.
9897      */
9898     isExpanded : function(){
9899         return this.list.isVisible();
9900     },
9901
9902     /**
9903      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9904      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9905      * @param {String} value The data value of the item to select
9906      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9907      * selected item if it is not currently in view (defaults to true)
9908      * @return {Boolean} True if the value matched an item in the list, else false
9909      */
9910     selectByValue : function(v, scrollIntoView){
9911         if(v !== undefined && v !== null){
9912             var r = this.findRecord(this.valueField || this.displayField, v);
9913             if(r){
9914                 this.select(this.store.indexOf(r), scrollIntoView);
9915                 return true;
9916             }
9917         }
9918         return false;
9919     },
9920
9921     /**
9922      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9923      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9924      * @param {Number} index The zero-based index of the list item to select
9925      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9926      * selected item if it is not currently in view (defaults to true)
9927      */
9928     select : function(index, scrollIntoView){
9929         this.selectedIndex = index;
9930         this.view.select(index);
9931         if(scrollIntoView !== false){
9932             var el = this.view.getNode(index);
9933             if(el){
9934                 //this.innerList.scrollChildIntoView(el, false);
9935                 
9936             }
9937         }
9938     },
9939
9940     // private
9941     selectNext : function(){
9942         var ct = this.store.getCount();
9943         if(ct > 0){
9944             if(this.selectedIndex == -1){
9945                 this.select(0);
9946             }else if(this.selectedIndex < ct-1){
9947                 this.select(this.selectedIndex+1);
9948             }
9949         }
9950     },
9951
9952     // private
9953     selectPrev : function(){
9954         var ct = this.store.getCount();
9955         if(ct > 0){
9956             if(this.selectedIndex == -1){
9957                 this.select(0);
9958             }else if(this.selectedIndex != 0){
9959                 this.select(this.selectedIndex-1);
9960             }
9961         }
9962     },
9963
9964     // private
9965     onKeyUp : function(e){
9966         if(this.editable !== false && !e.isSpecialKey()){
9967             this.lastKey = e.getKey();
9968             this.dqTask.delay(this.queryDelay);
9969         }
9970     },
9971
9972     // private
9973     validateBlur : function(){
9974         return !this.list || !this.list.isVisible();   
9975     },
9976
9977     // private
9978     initQuery : function(){
9979         this.doQuery(this.getRawValue());
9980     },
9981
9982     // private
9983     doForce : function(){
9984         if(this.inputEl().dom.value.length > 0){
9985             this.inputEl().dom.value =
9986                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9987              
9988         }
9989     },
9990
9991     /**
9992      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9993      * query allowing the query action to be canceled if needed.
9994      * @param {String} query The SQL query to execute
9995      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9996      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9997      * saved in the current store (defaults to false)
9998      */
9999     doQuery : function(q, forceAll){
10000         
10001         if(q === undefined || q === null){
10002             q = '';
10003         }
10004         var qe = {
10005             query: q,
10006             forceAll: forceAll,
10007             combo: this,
10008             cancel:false
10009         };
10010         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10011             return false;
10012         }
10013         q = qe.query;
10014         
10015         forceAll = qe.forceAll;
10016         if(forceAll === true || (q.length >= this.minChars)){
10017             
10018             this.hasQuery = true;
10019             
10020             if(this.lastQuery != q || this.alwaysQuery){
10021                 this.lastQuery = q;
10022                 if(this.mode == 'local'){
10023                     this.selectedIndex = -1;
10024                     if(forceAll){
10025                         this.store.clearFilter();
10026                     }else{
10027                         this.store.filter(this.displayField, q);
10028                     }
10029                     this.onLoad();
10030                 }else{
10031                     this.store.baseParams[this.queryParam] = q;
10032                     
10033                     var options = {params : this.getParams(q)};
10034                     
10035                     if(this.loadNext){
10036                         options.add = true;
10037                         options.params.start = this.page * this.pageSize;
10038                     }
10039                     
10040                     this.store.load(options);
10041                     this.expand();
10042                 }
10043             }else{
10044                 this.selectedIndex = -1;
10045                 this.onLoad();   
10046             }
10047         }
10048         
10049         this.loadNext = false;
10050     },
10051
10052     // private
10053     getParams : function(q){
10054         var p = {};
10055         //p[this.queryParam] = q;
10056         
10057         if(this.pageSize){
10058             p.start = 0;
10059             p.limit = this.pageSize;
10060         }
10061         return p;
10062     },
10063
10064     /**
10065      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10066      */
10067     collapse : function(){
10068         if(!this.isExpanded()){
10069             return;
10070         }
10071         
10072         this.list.hide();
10073         Roo.get(document).un('mousedown', this.collapseIf, this);
10074         Roo.get(document).un('mousewheel', this.collapseIf, this);
10075         if (!this.editable) {
10076             Roo.get(document).un('keydown', this.listKeyPress, this);
10077         }
10078         this.fireEvent('collapse', this);
10079     },
10080
10081     // private
10082     collapseIf : function(e){
10083         var in_combo  = e.within(this.el);
10084         var in_list =  e.within(this.list);
10085         
10086         if (in_combo || in_list) {
10087             //e.stopPropagation();
10088             return;
10089         }
10090
10091         this.collapse();
10092         
10093     },
10094
10095     /**
10096      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10097      */
10098     expand : function(){
10099        
10100         if(this.isExpanded() || !this.hasFocus){
10101             return;
10102         }
10103          Roo.log('expand');
10104         this.list.alignTo(this.inputEl(), this.listAlign);
10105         this.list.show();
10106         Roo.get(document).on('mousedown', this.collapseIf, this);
10107         Roo.get(document).on('mousewheel', this.collapseIf, this);
10108         if (!this.editable) {
10109             Roo.get(document).on('keydown', this.listKeyPress, this);
10110         }
10111         
10112         this.fireEvent('expand', this);
10113     },
10114
10115     // private
10116     // Implements the default empty TriggerField.onTriggerClick function
10117     onTriggerClick : function()
10118     {
10119         Roo.log('trigger click');
10120         
10121         if(this.disabled){
10122             return;
10123         }
10124         
10125         this.page = 0;
10126         this.loadNext = false;
10127         
10128         if(this.isExpanded()){
10129             this.collapse();
10130             if (!this.blockFocus) {
10131                 this.inputEl().focus();
10132             }
10133             
10134         }else {
10135             this.hasFocus = true;
10136             if(this.triggerAction == 'all') {
10137                 this.doQuery(this.allQuery, true);
10138             } else {
10139                 this.doQuery(this.getRawValue());
10140             }
10141             if (!this.blockFocus) {
10142                 this.inputEl().focus();
10143             }
10144         }
10145     },
10146     listKeyPress : function(e)
10147     {
10148         //Roo.log('listkeypress');
10149         // scroll to first matching element based on key pres..
10150         if (e.isSpecialKey()) {
10151             return false;
10152         }
10153         var k = String.fromCharCode(e.getKey()).toUpperCase();
10154         //Roo.log(k);
10155         var match  = false;
10156         var csel = this.view.getSelectedNodes();
10157         var cselitem = false;
10158         if (csel.length) {
10159             var ix = this.view.indexOf(csel[0]);
10160             cselitem  = this.store.getAt(ix);
10161             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10162                 cselitem = false;
10163             }
10164             
10165         }
10166         
10167         this.store.each(function(v) { 
10168             if (cselitem) {
10169                 // start at existing selection.
10170                 if (cselitem.id == v.id) {
10171                     cselitem = false;
10172                 }
10173                 return true;
10174             }
10175                 
10176             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10177                 match = this.store.indexOf(v);
10178                 return false;
10179             }
10180             return true;
10181         }, this);
10182         
10183         if (match === false) {
10184             return true; // no more action?
10185         }
10186         // scroll to?
10187         this.view.select(match);
10188         var sn = Roo.get(this.view.getSelectedNodes()[0])
10189         //sn.scrollIntoView(sn.dom.parentNode, false);
10190     },
10191     
10192     onViewScroll : function(e, t){
10193         
10194         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10195             return;
10196         }
10197         
10198         this.hasQuery = true;
10199         
10200         this.loading = this.list.select('.loading', true).first();
10201         
10202         if(this.loading === null){
10203             this.list.createChild({
10204                 tag: 'div',
10205                 cls: 'loading select2-more-results select2-active',
10206                 html: 'Loading more results...'
10207             })
10208             
10209             this.loading = this.list.select('.loading', true).first();
10210             
10211             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10212             
10213             this.loading.hide();
10214         }
10215         
10216         this.loading.show();
10217         
10218         var _combo = this;
10219         
10220         this.page++;
10221         this.loadNext = true;
10222         
10223         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10224         
10225         return;
10226     },
10227     
10228     addItem : function(o)
10229     {   
10230         var dv = ''; // display value
10231         
10232         if (this.displayField) {
10233             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10234         } else {
10235             // this is an error condition!!!
10236             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10237         }
10238         
10239         if(!dv.length){
10240             return;
10241         }
10242         
10243         var choice = this.choices.createChild({
10244             tag: 'li',
10245             cls: 'select2-search-choice',
10246             cn: [
10247                 {
10248                     tag: 'div',
10249                     html: dv
10250                 },
10251                 {
10252                     tag: 'a',
10253                     href: '#',
10254                     cls: 'select2-search-choice-close',
10255                     tabindex: '-1'
10256                 }
10257             ]
10258             
10259         }, this.searchField);
10260         
10261         var close = choice.select('a.select2-search-choice-close', true).first()
10262         
10263         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10264         
10265         this.item.push(o);
10266         this.lastData = o;
10267         
10268         this.syncValue();
10269         
10270         this.inputEl().dom.value = '';
10271         
10272     },
10273     
10274     onRemoveItem : function(e, _self, o)
10275     {
10276         e.preventDefault();
10277         var index = this.item.indexOf(o.data) * 1;
10278         
10279         if( index < 0){
10280             Roo.log('not this item?!');
10281             return;
10282         }
10283         
10284         this.item.splice(index, 1);
10285         o.item.remove();
10286         
10287         this.syncValue();
10288         
10289         this.fireEvent('remove', this, e);
10290         
10291     },
10292     
10293     syncValue : function()
10294     {
10295         if(!this.item.length){
10296             this.clearValue();
10297             return;
10298         }
10299             
10300         var value = [];
10301         var _this = this;
10302         Roo.each(this.item, function(i){
10303             if(_this.valueField){
10304                 value.push(i[_this.valueField]);
10305                 return;
10306             }
10307
10308             value.push(i);
10309         });
10310
10311         this.value = value.join(',');
10312
10313         if(this.hiddenField){
10314             this.hiddenField.dom.value = this.value;
10315         }
10316     },
10317     
10318     clearItem : function()
10319     {
10320         if(!this.multiple){
10321             return;
10322         }
10323         
10324         this.item = [];
10325         
10326         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10327            c.remove();
10328         });
10329         
10330         this.syncValue();
10331     }
10332     
10333     
10334
10335     /** 
10336     * @cfg {Boolean} grow 
10337     * @hide 
10338     */
10339     /** 
10340     * @cfg {Number} growMin 
10341     * @hide 
10342     */
10343     /** 
10344     * @cfg {Number} growMax 
10345     * @hide 
10346     */
10347     /**
10348      * @hide
10349      * @method autoSize
10350      */
10351 });
10352 /*
10353  * Based on:
10354  * Ext JS Library 1.1.1
10355  * Copyright(c) 2006-2007, Ext JS, LLC.
10356  *
10357  * Originally Released Under LGPL - original licence link has changed is not relivant.
10358  *
10359  * Fork - LGPL
10360  * <script type="text/javascript">
10361  */
10362
10363 /**
10364  * @class Roo.View
10365  * @extends Roo.util.Observable
10366  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10367  * This class also supports single and multi selection modes. <br>
10368  * Create a data model bound view:
10369  <pre><code>
10370  var store = new Roo.data.Store(...);
10371
10372  var view = new Roo.View({
10373     el : "my-element",
10374     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10375  
10376     singleSelect: true,
10377     selectedClass: "ydataview-selected",
10378     store: store
10379  });
10380
10381  // listen for node click?
10382  view.on("click", function(vw, index, node, e){
10383  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10384  });
10385
10386  // load XML data
10387  dataModel.load("foobar.xml");
10388  </code></pre>
10389  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10390  * <br><br>
10391  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10392  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10393  * 
10394  * Note: old style constructor is still suported (container, template, config)
10395  * 
10396  * @constructor
10397  * Create a new View
10398  * @param {Object} config The config object
10399  * 
10400  */
10401 Roo.View = function(config, depreciated_tpl, depreciated_config){
10402     
10403     if (typeof(depreciated_tpl) == 'undefined') {
10404         // new way.. - universal constructor.
10405         Roo.apply(this, config);
10406         this.el  = Roo.get(this.el);
10407     } else {
10408         // old format..
10409         this.el  = Roo.get(config);
10410         this.tpl = depreciated_tpl;
10411         Roo.apply(this, depreciated_config);
10412     }
10413     this.wrapEl  = this.el.wrap().wrap();
10414     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10415     
10416     
10417     if(typeof(this.tpl) == "string"){
10418         this.tpl = new Roo.Template(this.tpl);
10419     } else {
10420         // support xtype ctors..
10421         this.tpl = new Roo.factory(this.tpl, Roo);
10422     }
10423     
10424     
10425     this.tpl.compile();
10426    
10427   
10428     
10429      
10430     /** @private */
10431     this.addEvents({
10432         /**
10433          * @event beforeclick
10434          * Fires before a click is processed. Returns false to cancel the default action.
10435          * @param {Roo.View} this
10436          * @param {Number} index The index of the target node
10437          * @param {HTMLElement} node The target node
10438          * @param {Roo.EventObject} e The raw event object
10439          */
10440             "beforeclick" : true,
10441         /**
10442          * @event click
10443          * Fires when a template node is clicked.
10444          * @param {Roo.View} this
10445          * @param {Number} index The index of the target node
10446          * @param {HTMLElement} node The target node
10447          * @param {Roo.EventObject} e The raw event object
10448          */
10449             "click" : true,
10450         /**
10451          * @event dblclick
10452          * Fires when a template node is double clicked.
10453          * @param {Roo.View} this
10454          * @param {Number} index The index of the target node
10455          * @param {HTMLElement} node The target node
10456          * @param {Roo.EventObject} e The raw event object
10457          */
10458             "dblclick" : true,
10459         /**
10460          * @event contextmenu
10461          * Fires when a template node is right clicked.
10462          * @param {Roo.View} this
10463          * @param {Number} index The index of the target node
10464          * @param {HTMLElement} node The target node
10465          * @param {Roo.EventObject} e The raw event object
10466          */
10467             "contextmenu" : true,
10468         /**
10469          * @event selectionchange
10470          * Fires when the selected nodes change.
10471          * @param {Roo.View} this
10472          * @param {Array} selections Array of the selected nodes
10473          */
10474             "selectionchange" : true,
10475     
10476         /**
10477          * @event beforeselect
10478          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10479          * @param {Roo.View} this
10480          * @param {HTMLElement} node The node to be selected
10481          * @param {Array} selections Array of currently selected nodes
10482          */
10483             "beforeselect" : true,
10484         /**
10485          * @event preparedata
10486          * Fires on every row to render, to allow you to change the data.
10487          * @param {Roo.View} this
10488          * @param {Object} data to be rendered (change this)
10489          */
10490           "preparedata" : true
10491           
10492           
10493         });
10494
10495
10496
10497     this.el.on({
10498         "click": this.onClick,
10499         "dblclick": this.onDblClick,
10500         "contextmenu": this.onContextMenu,
10501         scope:this
10502     });
10503
10504     this.selections = [];
10505     this.nodes = [];
10506     this.cmp = new Roo.CompositeElementLite([]);
10507     if(this.store){
10508         this.store = Roo.factory(this.store, Roo.data);
10509         this.setStore(this.store, true);
10510     }
10511     
10512     if ( this.footer && this.footer.xtype) {
10513            
10514          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10515         
10516         this.footer.dataSource = this.store
10517         this.footer.container = fctr;
10518         this.footer = Roo.factory(this.footer, Roo);
10519         fctr.insertFirst(this.el);
10520         
10521         // this is a bit insane - as the paging toolbar seems to detach the el..
10522 //        dom.parentNode.parentNode.parentNode
10523          // they get detached?
10524     }
10525     
10526     
10527     Roo.View.superclass.constructor.call(this);
10528     
10529     
10530 };
10531
10532 Roo.extend(Roo.View, Roo.util.Observable, {
10533     
10534      /**
10535      * @cfg {Roo.data.Store} store Data store to load data from.
10536      */
10537     store : false,
10538     
10539     /**
10540      * @cfg {String|Roo.Element} el The container element.
10541      */
10542     el : '',
10543     
10544     /**
10545      * @cfg {String|Roo.Template} tpl The template used by this View 
10546      */
10547     tpl : false,
10548     /**
10549      * @cfg {String} dataName the named area of the template to use as the data area
10550      *                          Works with domtemplates roo-name="name"
10551      */
10552     dataName: false,
10553     /**
10554      * @cfg {String} selectedClass The css class to add to selected nodes
10555      */
10556     selectedClass : "x-view-selected",
10557      /**
10558      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10559      */
10560     emptyText : "",
10561     
10562     /**
10563      * @cfg {String} text to display on mask (default Loading)
10564      */
10565     mask : false,
10566     /**
10567      * @cfg {Boolean} multiSelect Allow multiple selection
10568      */
10569     multiSelect : false,
10570     /**
10571      * @cfg {Boolean} singleSelect Allow single selection
10572      */
10573     singleSelect:  false,
10574     
10575     /**
10576      * @cfg {Boolean} toggleSelect - selecting 
10577      */
10578     toggleSelect : false,
10579     
10580     /**
10581      * Returns the element this view is bound to.
10582      * @return {Roo.Element}
10583      */
10584     getEl : function(){
10585         return this.wrapEl;
10586     },
10587     
10588     
10589
10590     /**
10591      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10592      */
10593     refresh : function(){
10594         Roo.log('refresh');
10595         var t = this.tpl;
10596         
10597         // if we are using something like 'domtemplate', then
10598         // the what gets used is:
10599         // t.applySubtemplate(NAME, data, wrapping data..)
10600         // the outer template then get' applied with
10601         //     the store 'extra data'
10602         // and the body get's added to the
10603         //      roo-name="data" node?
10604         //      <span class='roo-tpl-{name}'></span> ?????
10605         
10606         
10607         
10608         this.clearSelections();
10609         this.el.update("");
10610         var html = [];
10611         var records = this.store.getRange();
10612         if(records.length < 1) {
10613             
10614             // is this valid??  = should it render a template??
10615             
10616             this.el.update(this.emptyText);
10617             return;
10618         }
10619         var el = this.el;
10620         if (this.dataName) {
10621             this.el.update(t.apply(this.store.meta)); //????
10622             el = this.el.child('.roo-tpl-' + this.dataName);
10623         }
10624         
10625         for(var i = 0, len = records.length; i < len; i++){
10626             var data = this.prepareData(records[i].data, i, records[i]);
10627             this.fireEvent("preparedata", this, data, i, records[i]);
10628             html[html.length] = Roo.util.Format.trim(
10629                 this.dataName ?
10630                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10631                     t.apply(data)
10632             );
10633         }
10634         
10635         
10636         
10637         el.update(html.join(""));
10638         this.nodes = el.dom.childNodes;
10639         this.updateIndexes(0);
10640     },
10641     
10642
10643     /**
10644      * Function to override to reformat the data that is sent to
10645      * the template for each node.
10646      * DEPRICATED - use the preparedata event handler.
10647      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10648      * a JSON object for an UpdateManager bound view).
10649      */
10650     prepareData : function(data, index, record)
10651     {
10652         this.fireEvent("preparedata", this, data, index, record);
10653         return data;
10654     },
10655
10656     onUpdate : function(ds, record){
10657          Roo.log('on update');   
10658         this.clearSelections();
10659         var index = this.store.indexOf(record);
10660         var n = this.nodes[index];
10661         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10662         n.parentNode.removeChild(n);
10663         this.updateIndexes(index, index);
10664     },
10665
10666     
10667     
10668 // --------- FIXME     
10669     onAdd : function(ds, records, index)
10670     {
10671         Roo.log(['on Add', ds, records, index] );        
10672         this.clearSelections();
10673         if(this.nodes.length == 0){
10674             this.refresh();
10675             return;
10676         }
10677         var n = this.nodes[index];
10678         for(var i = 0, len = records.length; i < len; i++){
10679             var d = this.prepareData(records[i].data, i, records[i]);
10680             if(n){
10681                 this.tpl.insertBefore(n, d);
10682             }else{
10683                 
10684                 this.tpl.append(this.el, d);
10685             }
10686         }
10687         this.updateIndexes(index);
10688     },
10689
10690     onRemove : function(ds, record, index){
10691         Roo.log('onRemove');
10692         this.clearSelections();
10693         var el = this.dataName  ?
10694             this.el.child('.roo-tpl-' + this.dataName) :
10695             this.el; 
10696         
10697         el.dom.removeChild(this.nodes[index]);
10698         this.updateIndexes(index);
10699     },
10700
10701     /**
10702      * Refresh an individual node.
10703      * @param {Number} index
10704      */
10705     refreshNode : function(index){
10706         this.onUpdate(this.store, this.store.getAt(index));
10707     },
10708
10709     updateIndexes : function(startIndex, endIndex){
10710         var ns = this.nodes;
10711         startIndex = startIndex || 0;
10712         endIndex = endIndex || ns.length - 1;
10713         for(var i = startIndex; i <= endIndex; i++){
10714             ns[i].nodeIndex = i;
10715         }
10716     },
10717
10718     /**
10719      * Changes the data store this view uses and refresh the view.
10720      * @param {Store} store
10721      */
10722     setStore : function(store, initial){
10723         if(!initial && this.store){
10724             this.store.un("datachanged", this.refresh);
10725             this.store.un("add", this.onAdd);
10726             this.store.un("remove", this.onRemove);
10727             this.store.un("update", this.onUpdate);
10728             this.store.un("clear", this.refresh);
10729             this.store.un("beforeload", this.onBeforeLoad);
10730             this.store.un("load", this.onLoad);
10731             this.store.un("loadexception", this.onLoad);
10732         }
10733         if(store){
10734           
10735             store.on("datachanged", this.refresh, this);
10736             store.on("add", this.onAdd, this);
10737             store.on("remove", this.onRemove, this);
10738             store.on("update", this.onUpdate, this);
10739             store.on("clear", this.refresh, this);
10740             store.on("beforeload", this.onBeforeLoad, this);
10741             store.on("load", this.onLoad, this);
10742             store.on("loadexception", this.onLoad, this);
10743         }
10744         
10745         if(store){
10746             this.refresh();
10747         }
10748     },
10749     /**
10750      * onbeforeLoad - masks the loading area.
10751      *
10752      */
10753     onBeforeLoad : function(store,opts)
10754     {
10755          Roo.log('onBeforeLoad');   
10756         if (!opts.add) {
10757             this.el.update("");
10758         }
10759         this.el.mask(this.mask ? this.mask : "Loading" ); 
10760     },
10761     onLoad : function ()
10762     {
10763         this.el.unmask();
10764     },
10765     
10766
10767     /**
10768      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10769      * @param {HTMLElement} node
10770      * @return {HTMLElement} The template node
10771      */
10772     findItemFromChild : function(node){
10773         var el = this.dataName  ?
10774             this.el.child('.roo-tpl-' + this.dataName,true) :
10775             this.el.dom; 
10776         
10777         if(!node || node.parentNode == el){
10778                     return node;
10779             }
10780             var p = node.parentNode;
10781             while(p && p != el){
10782             if(p.parentNode == el){
10783                 return p;
10784             }
10785             p = p.parentNode;
10786         }
10787             return null;
10788     },
10789
10790     /** @ignore */
10791     onClick : function(e){
10792         var item = this.findItemFromChild(e.getTarget());
10793         if(item){
10794             var index = this.indexOf(item);
10795             if(this.onItemClick(item, index, e) !== false){
10796                 this.fireEvent("click", this, index, item, e);
10797             }
10798         }else{
10799             this.clearSelections();
10800         }
10801     },
10802
10803     /** @ignore */
10804     onContextMenu : function(e){
10805         var item = this.findItemFromChild(e.getTarget());
10806         if(item){
10807             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10808         }
10809     },
10810
10811     /** @ignore */
10812     onDblClick : function(e){
10813         var item = this.findItemFromChild(e.getTarget());
10814         if(item){
10815             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10816         }
10817     },
10818
10819     onItemClick : function(item, index, e)
10820     {
10821         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10822             return false;
10823         }
10824         if (this.toggleSelect) {
10825             var m = this.isSelected(item) ? 'unselect' : 'select';
10826             Roo.log(m);
10827             var _t = this;
10828             _t[m](item, true, false);
10829             return true;
10830         }
10831         if(this.multiSelect || this.singleSelect){
10832             if(this.multiSelect && e.shiftKey && this.lastSelection){
10833                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10834             }else{
10835                 this.select(item, this.multiSelect && e.ctrlKey);
10836                 this.lastSelection = item;
10837             }
10838             e.preventDefault();
10839         }
10840         return true;
10841     },
10842
10843     /**
10844      * Get the number of selected nodes.
10845      * @return {Number}
10846      */
10847     getSelectionCount : function(){
10848         return this.selections.length;
10849     },
10850
10851     /**
10852      * Get the currently selected nodes.
10853      * @return {Array} An array of HTMLElements
10854      */
10855     getSelectedNodes : function(){
10856         return this.selections;
10857     },
10858
10859     /**
10860      * Get the indexes of the selected nodes.
10861      * @return {Array}
10862      */
10863     getSelectedIndexes : function(){
10864         var indexes = [], s = this.selections;
10865         for(var i = 0, len = s.length; i < len; i++){
10866             indexes.push(s[i].nodeIndex);
10867         }
10868         return indexes;
10869     },
10870
10871     /**
10872      * Clear all selections
10873      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10874      */
10875     clearSelections : function(suppressEvent){
10876         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10877             this.cmp.elements = this.selections;
10878             this.cmp.removeClass(this.selectedClass);
10879             this.selections = [];
10880             if(!suppressEvent){
10881                 this.fireEvent("selectionchange", this, this.selections);
10882             }
10883         }
10884     },
10885
10886     /**
10887      * Returns true if the passed node is selected
10888      * @param {HTMLElement/Number} node The node or node index
10889      * @return {Boolean}
10890      */
10891     isSelected : function(node){
10892         var s = this.selections;
10893         if(s.length < 1){
10894             return false;
10895         }
10896         node = this.getNode(node);
10897         return s.indexOf(node) !== -1;
10898     },
10899
10900     /**
10901      * Selects nodes.
10902      * @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
10903      * @param {Boolean} keepExisting (optional) true to keep existing selections
10904      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10905      */
10906     select : function(nodeInfo, keepExisting, suppressEvent){
10907         if(nodeInfo instanceof Array){
10908             if(!keepExisting){
10909                 this.clearSelections(true);
10910             }
10911             for(var i = 0, len = nodeInfo.length; i < len; i++){
10912                 this.select(nodeInfo[i], true, true);
10913             }
10914             return;
10915         } 
10916         var node = this.getNode(nodeInfo);
10917         if(!node || this.isSelected(node)){
10918             return; // already selected.
10919         }
10920         if(!keepExisting){
10921             this.clearSelections(true);
10922         }
10923         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10924             Roo.fly(node).addClass(this.selectedClass);
10925             this.selections.push(node);
10926             if(!suppressEvent){
10927                 this.fireEvent("selectionchange", this, this.selections);
10928             }
10929         }
10930         
10931         
10932     },
10933       /**
10934      * Unselects nodes.
10935      * @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
10936      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10937      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10938      */
10939     unselect : function(nodeInfo, keepExisting, suppressEvent)
10940     {
10941         if(nodeInfo instanceof Array){
10942             Roo.each(this.selections, function(s) {
10943                 this.unselect(s, nodeInfo);
10944             }, this);
10945             return;
10946         }
10947         var node = this.getNode(nodeInfo);
10948         if(!node || !this.isSelected(node)){
10949             Roo.log("not selected");
10950             return; // not selected.
10951         }
10952         // fireevent???
10953         var ns = [];
10954         Roo.each(this.selections, function(s) {
10955             if (s == node ) {
10956                 Roo.fly(node).removeClass(this.selectedClass);
10957
10958                 return;
10959             }
10960             ns.push(s);
10961         },this);
10962         
10963         this.selections= ns;
10964         this.fireEvent("selectionchange", this, this.selections);
10965     },
10966
10967     /**
10968      * Gets a template node.
10969      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10970      * @return {HTMLElement} The node or null if it wasn't found
10971      */
10972     getNode : function(nodeInfo){
10973         if(typeof nodeInfo == "string"){
10974             return document.getElementById(nodeInfo);
10975         }else if(typeof nodeInfo == "number"){
10976             return this.nodes[nodeInfo];
10977         }
10978         return nodeInfo;
10979     },
10980
10981     /**
10982      * Gets a range template nodes.
10983      * @param {Number} startIndex
10984      * @param {Number} endIndex
10985      * @return {Array} An array of nodes
10986      */
10987     getNodes : function(start, end){
10988         var ns = this.nodes;
10989         start = start || 0;
10990         end = typeof end == "undefined" ? ns.length - 1 : end;
10991         var nodes = [];
10992         if(start <= end){
10993             for(var i = start; i <= end; i++){
10994                 nodes.push(ns[i]);
10995             }
10996         } else{
10997             for(var i = start; i >= end; i--){
10998                 nodes.push(ns[i]);
10999             }
11000         }
11001         return nodes;
11002     },
11003
11004     /**
11005      * Finds the index of the passed node
11006      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11007      * @return {Number} The index of the node or -1
11008      */
11009     indexOf : function(node){
11010         node = this.getNode(node);
11011         if(typeof node.nodeIndex == "number"){
11012             return node.nodeIndex;
11013         }
11014         var ns = this.nodes;
11015         for(var i = 0, len = ns.length; i < len; i++){
11016             if(ns[i] == node){
11017                 return i;
11018             }
11019         }
11020         return -1;
11021     }
11022 });
11023 /*
11024  * - LGPL
11025  *
11026  * based on jquery fullcalendar
11027  * 
11028  */
11029
11030 Roo.bootstrap = Roo.bootstrap || {};
11031 /**
11032  * @class Roo.bootstrap.Calendar
11033  * @extends Roo.bootstrap.Component
11034  * Bootstrap Calendar class
11035  * @cfg {Boolean} loadMask (true|false) default false
11036  * @cfg {Object} header generate the user specific header of the calendar, default false
11037
11038  * @constructor
11039  * Create a new Container
11040  * @param {Object} config The config object
11041  */
11042
11043
11044
11045 Roo.bootstrap.Calendar = function(config){
11046     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11047      this.addEvents({
11048         /**
11049              * @event select
11050              * Fires when a date is selected
11051              * @param {DatePicker} this
11052              * @param {Date} date The selected date
11053              */
11054         'select': true,
11055         /**
11056              * @event monthchange
11057              * Fires when the displayed month changes 
11058              * @param {DatePicker} this
11059              * @param {Date} date The selected month
11060              */
11061         'monthchange': true,
11062         /**
11063              * @event evententer
11064              * Fires when mouse over an event
11065              * @param {Calendar} this
11066              * @param {event} Event
11067              */
11068         'evententer': true,
11069         /**
11070              * @event eventleave
11071              * Fires when the mouse leaves an
11072              * @param {Calendar} this
11073              * @param {event}
11074              */
11075         'eventleave': true,
11076         /**
11077              * @event eventclick
11078              * Fires when the mouse click an
11079              * @param {Calendar} this
11080              * @param {event}
11081              */
11082         'eventclick': true
11083         
11084     });
11085
11086 };
11087
11088 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11089     
11090      /**
11091      * @cfg {Number} startDay
11092      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11093      */
11094     startDay : 0,
11095     
11096     loadMask : false,
11097     
11098     header : false,
11099       
11100     getAutoCreate : function(){
11101         
11102         
11103         var fc_button = function(name, corner, style, content ) {
11104             return Roo.apply({},{
11105                 tag : 'span',
11106                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11107                          (corner.length ?
11108                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11109                             ''
11110                         ),
11111                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11112                 unselectable: 'on'
11113             });
11114         };
11115         
11116         var header = {};
11117         
11118         if(!this.header){
11119             header = {
11120                 tag : 'table',
11121                 cls : 'fc-header',
11122                 style : 'width:100%',
11123                 cn : [
11124                     {
11125                         tag: 'tr',
11126                         cn : [
11127                             {
11128                                 tag : 'td',
11129                                 cls : 'fc-header-left',
11130                                 cn : [
11131                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11132                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11133                                     { tag: 'span', cls: 'fc-header-space' },
11134                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11135
11136
11137                                 ]
11138                             },
11139
11140                             {
11141                                 tag : 'td',
11142                                 cls : 'fc-header-center',
11143                                 cn : [
11144                                     {
11145                                         tag: 'span',
11146                                         cls: 'fc-header-title',
11147                                         cn : {
11148                                             tag: 'H2',
11149                                             html : 'month / year'
11150                                         }
11151                                     }
11152
11153                                 ]
11154                             },
11155                             {
11156                                 tag : 'td',
11157                                 cls : 'fc-header-right',
11158                                 cn : [
11159                               /*      fc_button('month', 'left', '', 'month' ),
11160                                     fc_button('week', '', '', 'week' ),
11161                                     fc_button('day', 'right', '', 'day' )
11162                                 */    
11163
11164                                 ]
11165                             }
11166
11167                         ]
11168                     }
11169                 ]
11170             };
11171         }
11172         
11173         header = this.header;
11174         
11175        
11176         var cal_heads = function() {
11177             var ret = [];
11178             // fixme - handle this.
11179             
11180             for (var i =0; i < Date.dayNames.length; i++) {
11181                 var d = Date.dayNames[i];
11182                 ret.push({
11183                     tag: 'th',
11184                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11185                     html : d.substring(0,3)
11186                 });
11187                 
11188             }
11189             ret[0].cls += ' fc-first';
11190             ret[6].cls += ' fc-last';
11191             return ret;
11192         };
11193         var cal_cell = function(n) {
11194             return  {
11195                 tag: 'td',
11196                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11197                 cn : [
11198                     {
11199                         cn : [
11200                             {
11201                                 cls: 'fc-day-number',
11202                                 html: 'D'
11203                             },
11204                             {
11205                                 cls: 'fc-day-content',
11206                              
11207                                 cn : [
11208                                      {
11209                                         style: 'position: relative;' // height: 17px;
11210                                     }
11211                                 ]
11212                             }
11213                             
11214                             
11215                         ]
11216                     }
11217                 ]
11218                 
11219             }
11220         };
11221         var cal_rows = function() {
11222             
11223             var ret = []
11224             for (var r = 0; r < 6; r++) {
11225                 var row= {
11226                     tag : 'tr',
11227                     cls : 'fc-week',
11228                     cn : []
11229                 };
11230                 
11231                 for (var i =0; i < Date.dayNames.length; i++) {
11232                     var d = Date.dayNames[i];
11233                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11234
11235                 }
11236                 row.cn[0].cls+=' fc-first';
11237                 row.cn[0].cn[0].style = 'min-height:90px';
11238                 row.cn[6].cls+=' fc-last';
11239                 ret.push(row);
11240                 
11241             }
11242             ret[0].cls += ' fc-first';
11243             ret[4].cls += ' fc-prev-last';
11244             ret[5].cls += ' fc-last';
11245             return ret;
11246             
11247         };
11248         
11249         var cal_table = {
11250             tag: 'table',
11251             cls: 'fc-border-separate',
11252             style : 'width:100%',
11253             cellspacing  : 0,
11254             cn : [
11255                 { 
11256                     tag: 'thead',
11257                     cn : [
11258                         { 
11259                             tag: 'tr',
11260                             cls : 'fc-first fc-last',
11261                             cn : cal_heads()
11262                         }
11263                     ]
11264                 },
11265                 { 
11266                     tag: 'tbody',
11267                     cn : cal_rows()
11268                 }
11269                   
11270             ]
11271         };
11272          
11273          var cfg = {
11274             cls : 'fc fc-ltr',
11275             cn : [
11276                 header,
11277                 {
11278                     cls : 'fc-content',
11279                     style : "position: relative;",
11280                     cn : [
11281                         {
11282                             cls : 'fc-view fc-view-month fc-grid',
11283                             style : 'position: relative',
11284                             unselectable : 'on',
11285                             cn : [
11286                                 {
11287                                     cls : 'fc-event-container',
11288                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11289                                 },
11290                                 cal_table
11291                             ]
11292                         }
11293                     ]
11294     
11295                 }
11296            ] 
11297             
11298         };
11299         
11300          
11301         
11302         return cfg;
11303     },
11304     
11305     
11306     initEvents : function()
11307     {
11308         if(!this.store){
11309             throw "can not find store for calendar";
11310         }
11311         
11312         var mark = {
11313             tag: "div",
11314             cls:"x-dlg-mask",
11315             style: "text-align:center",
11316             cn: [
11317                 {
11318                     tag: "div",
11319                     style: "background-color:white;width:50%;margin:250 auto",
11320                     cn: [
11321                         {
11322                             tag: "img",
11323                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11324                         },
11325                         {
11326                             tag: "span",
11327                             html: "Loading"
11328                         }
11329                         
11330                     ]
11331                 }
11332             ]
11333         }
11334         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11335         
11336         var size = this.el.select('.fc-content', true).first().getSize();
11337         this.maskEl.setSize(size.width, size.height);
11338         this.maskEl.enableDisplayMode("block");
11339         if(!this.loadMask){
11340             this.maskEl.hide();
11341         }
11342         
11343         this.store = Roo.factory(this.store, Roo.data);
11344         this.store.on('load', this.onLoad, this);
11345         this.store.on('beforeload', this.onBeforeLoad, this);
11346         
11347         this.resize();
11348         
11349         this.cells = this.el.select('.fc-day',true);
11350         //Roo.log(this.cells);
11351         this.textNodes = this.el.query('.fc-day-number');
11352         this.cells.addClassOnOver('fc-state-hover');
11353         
11354         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11355         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11356         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11357         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11358         
11359         this.on('monthchange', this.onMonthChange, this);
11360         
11361         this.update(new Date().clearTime());
11362     },
11363     
11364     resize : function() {
11365         var sz  = this.el.getSize();
11366         
11367         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11368         this.el.select('.fc-day-content div',true).setHeight(34);
11369     },
11370     
11371     
11372     // private
11373     showPrevMonth : function(e){
11374         this.update(this.activeDate.add("mo", -1));
11375     },
11376     showToday : function(e){
11377         this.update(new Date().clearTime());
11378     },
11379     // private
11380     showNextMonth : function(e){
11381         this.update(this.activeDate.add("mo", 1));
11382     },
11383
11384     // private
11385     showPrevYear : function(){
11386         this.update(this.activeDate.add("y", -1));
11387     },
11388
11389     // private
11390     showNextYear : function(){
11391         this.update(this.activeDate.add("y", 1));
11392     },
11393
11394     
11395    // private
11396     update : function(date)
11397     {
11398         var vd = this.activeDate;
11399         this.activeDate = date;
11400 //        if(vd && this.el){
11401 //            var t = date.getTime();
11402 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11403 //                Roo.log('using add remove');
11404 //                
11405 //                this.fireEvent('monthchange', this, date);
11406 //                
11407 //                this.cells.removeClass("fc-state-highlight");
11408 //                this.cells.each(function(c){
11409 //                   if(c.dateValue == t){
11410 //                       c.addClass("fc-state-highlight");
11411 //                       setTimeout(function(){
11412 //                            try{c.dom.firstChild.focus();}catch(e){}
11413 //                       }, 50);
11414 //                       return false;
11415 //                   }
11416 //                   return true;
11417 //                });
11418 //                return;
11419 //            }
11420 //        }
11421         
11422         var days = date.getDaysInMonth();
11423         
11424         var firstOfMonth = date.getFirstDateOfMonth();
11425         var startingPos = firstOfMonth.getDay()-this.startDay;
11426         
11427         if(startingPos < this.startDay){
11428             startingPos += 7;
11429         }
11430         
11431         var pm = date.add(Date.MONTH, -1);
11432         var prevStart = pm.getDaysInMonth()-startingPos;
11433 //        
11434         this.cells = this.el.select('.fc-day',true);
11435         this.textNodes = this.el.query('.fc-day-number');
11436         this.cells.addClassOnOver('fc-state-hover');
11437         
11438         var cells = this.cells.elements;
11439         var textEls = this.textNodes;
11440         
11441         Roo.each(cells, function(cell){
11442             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11443         });
11444         
11445         days += startingPos;
11446
11447         // convert everything to numbers so it's fast
11448         var day = 86400000;
11449         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11450         //Roo.log(d);
11451         //Roo.log(pm);
11452         //Roo.log(prevStart);
11453         
11454         var today = new Date().clearTime().getTime();
11455         var sel = date.clearTime().getTime();
11456         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11457         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11458         var ddMatch = this.disabledDatesRE;
11459         var ddText = this.disabledDatesText;
11460         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11461         var ddaysText = this.disabledDaysText;
11462         var format = this.format;
11463         
11464         var setCellClass = function(cal, cell){
11465             
11466             //Roo.log('set Cell Class');
11467             cell.title = "";
11468             var t = d.getTime();
11469             
11470             //Roo.log(d);
11471             
11472             cell.dateValue = t;
11473             if(t == today){
11474                 cell.className += " fc-today";
11475                 cell.className += " fc-state-highlight";
11476                 cell.title = cal.todayText;
11477             }
11478             if(t == sel){
11479                 // disable highlight in other month..
11480                 //cell.className += " fc-state-highlight";
11481                 
11482             }
11483             // disabling
11484             if(t < min) {
11485                 cell.className = " fc-state-disabled";
11486                 cell.title = cal.minText;
11487                 return;
11488             }
11489             if(t > max) {
11490                 cell.className = " fc-state-disabled";
11491                 cell.title = cal.maxText;
11492                 return;
11493             }
11494             if(ddays){
11495                 if(ddays.indexOf(d.getDay()) != -1){
11496                     cell.title = ddaysText;
11497                     cell.className = " fc-state-disabled";
11498                 }
11499             }
11500             if(ddMatch && format){
11501                 var fvalue = d.dateFormat(format);
11502                 if(ddMatch.test(fvalue)){
11503                     cell.title = ddText.replace("%0", fvalue);
11504                     cell.className = " fc-state-disabled";
11505                 }
11506             }
11507             
11508             if (!cell.initialClassName) {
11509                 cell.initialClassName = cell.dom.className;
11510             }
11511             
11512             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11513         };
11514
11515         var i = 0;
11516         
11517         for(; i < startingPos; i++) {
11518             textEls[i].innerHTML = (++prevStart);
11519             d.setDate(d.getDate()+1);
11520             
11521             cells[i].className = "fc-past fc-other-month";
11522             setCellClass(this, cells[i]);
11523         }
11524         
11525         var intDay = 0;
11526         
11527         for(; i < days; i++){
11528             intDay = i - startingPos + 1;
11529             textEls[i].innerHTML = (intDay);
11530             d.setDate(d.getDate()+1);
11531             
11532             cells[i].className = ''; // "x-date-active";
11533             setCellClass(this, cells[i]);
11534         }
11535         var extraDays = 0;
11536         
11537         for(; i < 42; i++) {
11538             textEls[i].innerHTML = (++extraDays);
11539             d.setDate(d.getDate()+1);
11540             
11541             cells[i].className = "fc-future fc-other-month";
11542             setCellClass(this, cells[i]);
11543         }
11544         
11545         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11546         
11547         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11548         
11549         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11550         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11551         
11552         if(totalRows != 6){
11553             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11554             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11555         }
11556         
11557         this.fireEvent('monthchange', this, date);
11558         
11559         
11560         /*
11561         if(!this.internalRender){
11562             var main = this.el.dom.firstChild;
11563             var w = main.offsetWidth;
11564             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11565             Roo.fly(main).setWidth(w);
11566             this.internalRender = true;
11567             // opera does not respect the auto grow header center column
11568             // then, after it gets a width opera refuses to recalculate
11569             // without a second pass
11570             if(Roo.isOpera && !this.secondPass){
11571                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11572                 this.secondPass = true;
11573                 this.update.defer(10, this, [date]);
11574             }
11575         }
11576         */
11577         
11578     },
11579     
11580     findCell : function(dt) {
11581         dt = dt.clearTime().getTime();
11582         var ret = false;
11583         this.cells.each(function(c){
11584             //Roo.log("check " +c.dateValue + '?=' + dt);
11585             if(c.dateValue == dt){
11586                 ret = c;
11587                 return false;
11588             }
11589             return true;
11590         });
11591         
11592         return ret;
11593     },
11594     
11595     findCells : function(ev) {
11596         var s = ev.start.clone().clearTime().getTime();
11597        // Roo.log(s);
11598         var e= ev.end.clone().clearTime().getTime();
11599        // Roo.log(e);
11600         var ret = [];
11601         this.cells.each(function(c){
11602              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11603             
11604             if(c.dateValue > e){
11605                 return ;
11606             }
11607             if(c.dateValue < s){
11608                 return ;
11609             }
11610             ret.push(c);
11611         });
11612         
11613         return ret;    
11614     },
11615     
11616 //    findBestRow: function(cells)
11617 //    {
11618 //        var ret = 0;
11619 //        
11620 //        for (var i =0 ; i < cells.length;i++) {
11621 //            ret  = Math.max(cells[i].rows || 0,ret);
11622 //        }
11623 //        return ret;
11624 //        
11625 //    },
11626     
11627     
11628     addItem : function(ev)
11629     {
11630         // look for vertical location slot in
11631         var cells = this.findCells(ev);
11632         
11633 //        ev.row = this.findBestRow(cells);
11634         
11635         // work out the location.
11636         
11637         var crow = false;
11638         var rows = [];
11639         for(var i =0; i < cells.length; i++) {
11640             if (!crow) {
11641                 crow = {
11642                     start : cells[i],
11643                     end :  cells[i]
11644                 };
11645                 continue;
11646             }
11647             if (crow.start.getY() == cells[i].getY()) {
11648                 // on same row.
11649                 crow.end = cells[i];
11650                 continue;
11651             }
11652             // different row.
11653             rows.push(crow);
11654             crow = {
11655                 start: cells[i],
11656                 end : cells[i]
11657             };
11658             
11659         }
11660         
11661         rows.push(crow);
11662         ev.els = [];
11663         ev.rows = rows;
11664         ev.cells = cells;
11665         ev.rendered = false;
11666 //        for (var i = 0; i < cells.length;i++) {
11667 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11668 //            
11669 //        }
11670         
11671         this.calevents.push(ev);
11672     },
11673     
11674     clearEvents: function() {
11675         
11676         if(!this.calevents){
11677             return;
11678         }
11679         
11680         Roo.each(this.cells.elements, function(c){
11681             c.rows = [];
11682             c.more = [];
11683         });
11684         
11685         Roo.each(this.calevents, function(e) {
11686             Roo.each(e.els, function(el) {
11687                 el.un('mouseenter' ,this.onEventEnter, this);
11688                 el.un('mouseleave' ,this.onEventLeave, this);
11689                 el.remove();
11690             },this);
11691         },this);
11692         
11693         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11694             e.remove();
11695         });
11696         
11697     },
11698     
11699     renderEvents: function()
11700     {   
11701         // first make sure there is enough space..
11702         this.cells.each(function(c) {
11703             c.rows = [];
11704             c.more = [];
11705         });
11706         
11707         for (var e = 0; e < this.calevents.length; e++) {
11708             
11709             var ev = this.calevents[e];
11710             var cells = ev.cells;
11711             var rows = ev.rows;
11712             
11713             for(var i = 0; i < cells.length; i++){
11714                 
11715                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11716                 
11717                 if(cells.length < 2 && cbox.rows.length > 3){
11718                     cbox.more.push(ev);
11719                     continue;
11720                 }
11721                 
11722                 cbox.rows.push(ev);
11723             }
11724         }
11725         
11726         var _this = this;
11727         
11728         this.cells.each(function(c) {
11729             if(c.more.length && c.more.length == 1){
11730                 c.rows.push(c.more.pop());
11731             }
11732             
11733             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11734             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11735             
11736             
11737             for (var e = 0; e < c.rows.length; e++){
11738                 var ev = c.rows[e];
11739                 
11740                 if(ev.rendered){
11741                     continue;
11742                 }
11743                 
11744                 var cells = ev.cells;
11745                 var rows = ev.rows;
11746                 
11747                 for(var i = 0; i < rows.length; i++) {
11748                 
11749                     // how many rows should it span..
11750
11751                     var  cfg = {
11752                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11753                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11754
11755                         unselectable : "on",
11756                         cn : [
11757                             {
11758                                 cls: 'fc-event-inner',
11759                                 cn : [
11760     //                                {
11761     //                                  tag:'span',
11762     //                                  cls: 'fc-event-time',
11763     //                                  html : cells.length > 1 ? '' : ev.time
11764     //                                },
11765                                     {
11766                                       tag:'span',
11767                                       cls: 'fc-event-title',
11768                                       html : String.format('{0}', ev.title)
11769                                     }
11770
11771
11772                                 ]
11773                             },
11774                             {
11775                                 cls: 'ui-resizable-handle ui-resizable-e',
11776                                 html : '&nbsp;&nbsp;&nbsp'
11777                             }
11778
11779                         ]
11780                     };
11781
11782                     if (i == 0) {
11783                         cfg.cls += ' fc-event-start';
11784                     }
11785                     if ((i+1) == rows.length) {
11786                         cfg.cls += ' fc-event-end';
11787                     }
11788
11789                     var ctr = _this.el.select('.fc-event-container',true).first();
11790                     var cg = ctr.createChild(cfg);
11791
11792                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11793                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11794
11795                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11796                     cg.setWidth(ebox.right - sbox.x -2);
11797
11798                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11799                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11800                     cg.on('click', _this.onEventClick, _this, ev);
11801
11802                     ev.els.push(cg);
11803                     
11804                     ev.rendered = true;
11805                 }
11806                 
11807             }
11808             
11809             
11810             if(c.more.length){
11811                 var  cfg = {
11812                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11813                     style : 'position: absolute',
11814                     unselectable : "on",
11815                     cn : [
11816                         {
11817                             cls: 'fc-event-inner',
11818                             cn : [
11819                                 {
11820                                   tag:'span',
11821                                   cls: 'fc-event-title',
11822                                   html : 'More'
11823                                 }
11824
11825
11826                             ]
11827                         },
11828                         {
11829                             cls: 'ui-resizable-handle ui-resizable-e',
11830                             html : '&nbsp;&nbsp;&nbsp'
11831                         }
11832
11833                     ]
11834                 };
11835
11836                 var ctr = _this.el.select('.fc-event-container',true).first();
11837                 var cg = ctr.createChild(cfg);
11838
11839                 var sbox = c.select('.fc-day-content',true).first().getBox();
11840                 var ebox = c.select('.fc-day-content',true).first().getBox();
11841                 //Roo.log(cg);
11842                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11843                 cg.setWidth(ebox.right - sbox.x -2);
11844
11845                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11846                 
11847             }
11848             
11849         });
11850         
11851         
11852         
11853     },
11854     
11855     onEventEnter: function (e, el,event,d) {
11856         this.fireEvent('evententer', this, el, event);
11857     },
11858     
11859     onEventLeave: function (e, el,event,d) {
11860         this.fireEvent('eventleave', this, el, event);
11861     },
11862     
11863     onEventClick: function (e, el,event,d) {
11864         this.fireEvent('eventclick', this, el, event);
11865     },
11866     
11867     onMonthChange: function () {
11868         this.store.load();
11869     },
11870     
11871     onMoreEventClick: function(e, el, more)
11872     {
11873         var _this = this;
11874         
11875         this.calpopover.placement = 'right';
11876         this.calpopover.setTitle('More');
11877         
11878         this.calpopover.setContent('');
11879         
11880         var ctr = this.calpopover.el.select('.popover-content', true).first();
11881         
11882         Roo.each(more, function(m){
11883             var cfg = {
11884                 cls : 'fc-event-hori fc-event-draggable',
11885                 html : m.title
11886             }
11887             var cg = ctr.createChild(cfg);
11888             
11889             cg.on('click', _this.onEventClick, _this, m);
11890         });
11891         
11892         this.calpopover.show(el);
11893         
11894         
11895     },
11896     
11897     onLoad: function () 
11898     {   
11899         this.calevents = [];
11900         var cal = this;
11901         
11902         if(this.store.getCount() > 0){
11903             this.store.data.each(function(d){
11904                cal.addItem({
11905                     id : d.data.id,
11906                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11907                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11908                     time : d.data.start_time,
11909                     title : d.data.title,
11910                     description : d.data.description,
11911                     venue : d.data.venue
11912                 });
11913             });
11914         }
11915         
11916         this.renderEvents();
11917         
11918         if(this.calevents.length && this.loadMask){
11919             this.maskEl.hide();
11920         }
11921     },
11922     
11923     onBeforeLoad: function()
11924     {
11925         this.clearEvents();
11926         if(this.loadMask){
11927             this.maskEl.show();
11928         }
11929     }
11930 });
11931
11932  
11933  /*
11934  * - LGPL
11935  *
11936  * element
11937  * 
11938  */
11939
11940 /**
11941  * @class Roo.bootstrap.Popover
11942  * @extends Roo.bootstrap.Component
11943  * Bootstrap Popover class
11944  * @cfg {String} html contents of the popover   (or false to use children..)
11945  * @cfg {String} title of popover (or false to hide)
11946  * @cfg {String} placement how it is placed
11947  * @cfg {String} trigger click || hover (or false to trigger manually)
11948  * @cfg {String} over what (parent or false to trigger manually.)
11949  * 
11950  * @constructor
11951  * Create a new Popover
11952  * @param {Object} config The config object
11953  */
11954
11955 Roo.bootstrap.Popover = function(config){
11956     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11957 };
11958
11959 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11960     
11961     title: 'Fill in a title',
11962     html: false,
11963     
11964     placement : 'right',
11965     trigger : 'hover', // hover
11966     
11967     over: 'parent',
11968     
11969     can_build_overlaid : false,
11970     
11971     getChildContainer : function()
11972     {
11973         return this.el.select('.popover-content',true).first();
11974     },
11975     
11976     getAutoCreate : function(){
11977          Roo.log('make popover?');
11978         var cfg = {
11979            cls : 'popover roo-dynamic',
11980            style: 'display:block',
11981            cn : [
11982                 {
11983                     cls : 'arrow'
11984                 },
11985                 {
11986                     cls : 'popover-inner',
11987                     cn : [
11988                         {
11989                             tag: 'h3',
11990                             cls: 'popover-title',
11991                             html : this.title
11992                         },
11993                         {
11994                             cls : 'popover-content',
11995                             html : this.html
11996                         }
11997                     ]
11998                     
11999                 }
12000            ]
12001         };
12002         
12003         return cfg;
12004     },
12005     setTitle: function(str)
12006     {
12007         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12008     },
12009     setContent: function(str)
12010     {
12011         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12012     },
12013     // as it get's added to the bottom of the page.
12014     onRender : function(ct, position)
12015     {
12016         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12017         if(!this.el){
12018             var cfg = Roo.apply({},  this.getAutoCreate());
12019             cfg.id = Roo.id();
12020             
12021             if (this.cls) {
12022                 cfg.cls += ' ' + this.cls;
12023             }
12024             if (this.style) {
12025                 cfg.style = this.style;
12026             }
12027             Roo.log("adding to ")
12028             this.el = Roo.get(document.body).createChild(cfg, position);
12029             Roo.log(this.el);
12030         }
12031         this.initEvents();
12032     },
12033     
12034     initEvents : function()
12035     {
12036         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12037         this.el.enableDisplayMode('block');
12038         this.el.hide();
12039         if (this.over === false) {
12040             return; 
12041         }
12042         if (this.triggers === false) {
12043             return;
12044         }
12045         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12046         var triggers = this.trigger ? this.trigger.split(' ') : [];
12047         Roo.each(triggers, function(trigger) {
12048         
12049             if (trigger == 'click') {
12050                 on_el.on('click', this.toggle, this);
12051             } else if (trigger != 'manual') {
12052                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12053                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12054       
12055                 on_el.on(eventIn  ,this.enter, this);
12056                 on_el.on(eventOut, this.leave, this);
12057             }
12058         }, this);
12059         
12060     },
12061     
12062     
12063     // private
12064     timeout : null,
12065     hoverState : null,
12066     
12067     toggle : function () {
12068         this.hoverState == 'in' ? this.leave() : this.enter();
12069     },
12070     
12071     enter : function () {
12072        
12073     
12074         clearTimeout(this.timeout);
12075     
12076         this.hoverState = 'in'
12077     
12078         if (!this.delay || !this.delay.show) {
12079             this.show();
12080             return 
12081         }
12082         var _t = this;
12083         this.timeout = setTimeout(function () {
12084             if (_t.hoverState == 'in') {
12085                 _t.show();
12086             }
12087         }, this.delay.show)
12088     },
12089     leave : function() {
12090         clearTimeout(this.timeout);
12091     
12092         this.hoverState = 'out'
12093     
12094         if (!this.delay || !this.delay.hide) {
12095             this.hide();
12096             return 
12097         }
12098         var _t = this;
12099         this.timeout = setTimeout(function () {
12100             if (_t.hoverState == 'out') {
12101                 _t.hide();
12102             }
12103         }, this.delay.hide)
12104     },
12105     
12106     show : function (on_el)
12107     {
12108         if (!on_el) {
12109             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12110         }
12111         // set content.
12112         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12113         if (this.html !== false) {
12114             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12115         }
12116         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12117         if (!this.title.length) {
12118             this.el.select('.popover-title',true).hide();
12119         }
12120         
12121         var placement = typeof this.placement == 'function' ?
12122             this.placement.call(this, this.el, on_el) :
12123             this.placement;
12124             
12125         var autoToken = /\s?auto?\s?/i;
12126         var autoPlace = autoToken.test(placement);
12127         if (autoPlace) {
12128             placement = placement.replace(autoToken, '') || 'top';
12129         }
12130         
12131         //this.el.detach()
12132         //this.el.setXY([0,0]);
12133         this.el.show();
12134         this.el.dom.style.display='block';
12135         this.el.addClass(placement);
12136         
12137         //this.el.appendTo(on_el);
12138         
12139         var p = this.getPosition();
12140         var box = this.el.getBox();
12141         
12142         if (autoPlace) {
12143             // fixme..
12144         }
12145         var align = Roo.bootstrap.Popover.alignment[placement]
12146         this.el.alignTo(on_el, align[0],align[1]);
12147         //var arrow = this.el.select('.arrow',true).first();
12148         //arrow.set(align[2], 
12149         
12150         this.el.addClass('in');
12151         this.hoverState = null;
12152         
12153         if (this.el.hasClass('fade')) {
12154             // fade it?
12155         }
12156         
12157     },
12158     hide : function()
12159     {
12160         this.el.setXY([0,0]);
12161         this.el.removeClass('in');
12162         this.el.hide();
12163         
12164     }
12165     
12166 });
12167
12168 Roo.bootstrap.Popover.alignment = {
12169     'left' : ['r-l', [-10,0], 'right'],
12170     'right' : ['l-r', [10,0], 'left'],
12171     'bottom' : ['t-b', [0,10], 'top'],
12172     'top' : [ 'b-t', [0,-10], 'bottom']
12173 };
12174
12175  /*
12176  * - LGPL
12177  *
12178  * Progress
12179  * 
12180  */
12181
12182 /**
12183  * @class Roo.bootstrap.Progress
12184  * @extends Roo.bootstrap.Component
12185  * Bootstrap Progress class
12186  * @cfg {Boolean} striped striped of the progress bar
12187  * @cfg {Boolean} active animated of the progress bar
12188  * 
12189  * 
12190  * @constructor
12191  * Create a new Progress
12192  * @param {Object} config The config object
12193  */
12194
12195 Roo.bootstrap.Progress = function(config){
12196     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12197 };
12198
12199 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12200     
12201     striped : false,
12202     active: false,
12203     
12204     getAutoCreate : function(){
12205         var cfg = {
12206             tag: 'div',
12207             cls: 'progress'
12208         };
12209         
12210         
12211         if(this.striped){
12212             cfg.cls += ' progress-striped';
12213         }
12214       
12215         if(this.active){
12216             cfg.cls += ' active';
12217         }
12218         
12219         
12220         return cfg;
12221     }
12222    
12223 });
12224
12225  
12226
12227  /*
12228  * - LGPL
12229  *
12230  * ProgressBar
12231  * 
12232  */
12233
12234 /**
12235  * @class Roo.bootstrap.ProgressBar
12236  * @extends Roo.bootstrap.Component
12237  * Bootstrap ProgressBar class
12238  * @cfg {Number} aria_valuenow aria-value now
12239  * @cfg {Number} aria_valuemin aria-value min
12240  * @cfg {Number} aria_valuemax aria-value max
12241  * @cfg {String} label label for the progress bar
12242  * @cfg {String} panel (success | info | warning | danger )
12243  * @cfg {String} role role of the progress bar
12244  * @cfg {String} sr_only text
12245  * 
12246  * 
12247  * @constructor
12248  * Create a new ProgressBar
12249  * @param {Object} config The config object
12250  */
12251
12252 Roo.bootstrap.ProgressBar = function(config){
12253     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12254 };
12255
12256 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12257     
12258     aria_valuenow : 0,
12259     aria_valuemin : 0,
12260     aria_valuemax : 100,
12261     label : false,
12262     panel : false,
12263     role : false,
12264     sr_only: false,
12265     
12266     getAutoCreate : function()
12267     {
12268         
12269         var cfg = {
12270             tag: 'div',
12271             cls: 'progress-bar',
12272             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12273         };
12274         
12275         if(this.sr_only){
12276             cfg.cn = {
12277                 tag: 'span',
12278                 cls: 'sr-only',
12279                 html: this.sr_only
12280             }
12281         }
12282         
12283         if(this.role){
12284             cfg.role = this.role;
12285         }
12286         
12287         if(this.aria_valuenow){
12288             cfg['aria-valuenow'] = this.aria_valuenow;
12289         }
12290         
12291         if(this.aria_valuemin){
12292             cfg['aria-valuemin'] = this.aria_valuemin;
12293         }
12294         
12295         if(this.aria_valuemax){
12296             cfg['aria-valuemax'] = this.aria_valuemax;
12297         }
12298         
12299         if(this.label && !this.sr_only){
12300             cfg.html = this.label;
12301         }
12302         
12303         if(this.panel){
12304             cfg.cls += ' progress-bar-' + this.panel;
12305         }
12306         
12307         return cfg;
12308     },
12309     
12310     update : function(aria_valuenow)
12311     {
12312         this.aria_valuenow = aria_valuenow;
12313         
12314         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12315     }
12316    
12317 });
12318
12319  
12320
12321  /*
12322  * - LGPL
12323  *
12324  * TabPanel
12325  * 
12326  */
12327
12328 /**
12329  * @class Roo.bootstrap.TabPanel
12330  * @extends Roo.bootstrap.Component
12331  * Bootstrap TabPanel class
12332  * @cfg {Boolean} active panel active
12333  * @cfg {String} html panel content
12334  * @cfg {String} tabId tab relate id
12335  * @cfg {String} navId The navbar which triggers show hide
12336  * 
12337  * 
12338  * @constructor
12339  * Create a new TabPanel
12340  * @param {Object} config The config object
12341  */
12342
12343 Roo.bootstrap.TabPanel = function(config){
12344     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12345      this.addEvents({
12346         /**
12347              * @event changed
12348              * Fires when the active status changes
12349              * @param {Roo.bootstrap.TabPanel} this
12350              * @param {Boolean} state the new state
12351             
12352          */
12353         'changed': true
12354      });
12355 };
12356
12357 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12358     
12359     active: false,
12360     html: false,
12361     tabId: false,
12362     navId : false,
12363     
12364     getAutoCreate : function(){
12365         var cfg = {
12366             tag: 'div',
12367             cls: 'tab-pane',
12368             html: this.html || ''
12369         };
12370         
12371         if(this.active){
12372             cfg.cls += ' active';
12373         }
12374         
12375         if(this.tabId){
12376             cfg.tabId = this.tabId;
12377         }
12378         
12379         return cfg;
12380     },
12381     onRender : function(ct, position)
12382     {
12383        // Roo.log("Call onRender: " + this.xtype);
12384         
12385         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12386         
12387         if (this.navId && this.tabId) {
12388             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12389             if (!item) {
12390                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12391             } else {
12392                 item.on('changed', function(item, state) {
12393                     this.setActive(state);
12394                 }, this);
12395             }
12396         }
12397         
12398     },
12399     setActive: function(state)
12400     {
12401         Roo.log("panel - set active " + this.tabId + "=" + state);
12402         
12403         this.active = state;
12404         if (!state) {
12405             this.el.removeClass('active');
12406             
12407         } else  if (!this.el.hasClass('active')) {
12408             this.el.addClass('active');
12409         }
12410         this.fireEvent('changed', this, state);
12411     }
12412     
12413     
12414 });
12415  
12416
12417  
12418
12419  /*
12420  * - LGPL
12421  *
12422  * DateField
12423  * 
12424  */
12425
12426 /**
12427  * @class Roo.bootstrap.DateField
12428  * @extends Roo.bootstrap.Input
12429  * Bootstrap DateField class
12430  * @cfg {Number} weekStart default 0
12431  * @cfg {Number} weekStart default 0
12432  * @cfg {Number} viewMode default empty, (months|years)
12433  * @cfg {Number} minViewMode default empty, (months|years)
12434  * @cfg {Number} startDate default -Infinity
12435  * @cfg {Number} endDate default Infinity
12436  * @cfg {Boolean} todayHighlight default false
12437  * @cfg {Boolean} todayBtn default false
12438  * @cfg {Boolean} calendarWeeks default false
12439  * @cfg {Object} daysOfWeekDisabled default empty
12440  * 
12441  * @cfg {Boolean} keyboardNavigation default true
12442  * @cfg {String} language default en
12443  * 
12444  * @constructor
12445  * Create a new DateField
12446  * @param {Object} config The config object
12447  */
12448
12449 Roo.bootstrap.DateField = function(config){
12450     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12451      this.addEvents({
12452             /**
12453              * @event show
12454              * Fires when this field show.
12455              * @param {Roo.bootstrap.DateField} this
12456              * @param {Mixed} date The date value
12457              */
12458             show : true,
12459             /**
12460              * @event show
12461              * Fires when this field hide.
12462              * @param {Roo.bootstrap.DateField} this
12463              * @param {Mixed} date The date value
12464              */
12465             hide : true,
12466             /**
12467              * @event select
12468              * Fires when select a date.
12469              * @param {Roo.bootstrap.DateField} this
12470              * @param {Mixed} date The date value
12471              */
12472             select : true
12473         });
12474 };
12475
12476 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12477     
12478     /**
12479      * @cfg {String} format
12480      * The default date format string which can be overriden for localization support.  The format must be
12481      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12482      */
12483     format : "m/d/y",
12484     /**
12485      * @cfg {String} altFormats
12486      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12487      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12488      */
12489     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12490     
12491     weekStart : 0,
12492     
12493     viewMode : '',
12494     
12495     minViewMode : '',
12496     
12497     todayHighlight : false,
12498     
12499     todayBtn: false,
12500     
12501     language: 'en',
12502     
12503     keyboardNavigation: true,
12504     
12505     calendarWeeks: false,
12506     
12507     startDate: -Infinity,
12508     
12509     endDate: Infinity,
12510     
12511     daysOfWeekDisabled: [],
12512     
12513     _events: [],
12514     
12515     UTCDate: function()
12516     {
12517         return new Date(Date.UTC.apply(Date, arguments));
12518     },
12519     
12520     UTCToday: function()
12521     {
12522         var today = new Date();
12523         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12524     },
12525     
12526     getDate: function() {
12527             var d = this.getUTCDate();
12528             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12529     },
12530     
12531     getUTCDate: function() {
12532             return this.date;
12533     },
12534     
12535     setDate: function(d) {
12536             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12537     },
12538     
12539     setUTCDate: function(d) {
12540             this.date = d;
12541             this.setValue(this.formatDate(this.date));
12542     },
12543         
12544     onRender: function(ct, position)
12545     {
12546         
12547         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12548         
12549         this.language = this.language || 'en';
12550         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12551         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12552         
12553         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12554         this.format = this.format || 'm/d/y';
12555         this.isInline = false;
12556         this.isInput = true;
12557         this.component = this.el.select('.add-on', true).first() || false;
12558         this.component = (this.component && this.component.length === 0) ? false : this.component;
12559         this.hasInput = this.component && this.inputEL().length;
12560         
12561         if (typeof(this.minViewMode === 'string')) {
12562             switch (this.minViewMode) {
12563                 case 'months':
12564                     this.minViewMode = 1;
12565                     break;
12566                 case 'years':
12567                     this.minViewMode = 2;
12568                     break;
12569                 default:
12570                     this.minViewMode = 0;
12571                     break;
12572             }
12573         }
12574         
12575         if (typeof(this.viewMode === 'string')) {
12576             switch (this.viewMode) {
12577                 case 'months':
12578                     this.viewMode = 1;
12579                     break;
12580                 case 'years':
12581                     this.viewMode = 2;
12582                     break;
12583                 default:
12584                     this.viewMode = 0;
12585                     break;
12586             }
12587         }
12588                 
12589         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12590         
12591         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12592         
12593         this.picker().on('mousedown', this.onMousedown, this);
12594         this.picker().on('click', this.onClick, this);
12595         
12596         this.picker().addClass('datepicker-dropdown');
12597         
12598         this.startViewMode = this.viewMode;
12599         
12600         
12601         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12602             if(!this.calendarWeeks){
12603                 v.remove();
12604                 return;
12605             };
12606             
12607             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12608             v.attr('colspan', function(i, val){
12609                 return parseInt(val) + 1;
12610             });
12611         })
12612                         
12613         
12614         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12615         
12616         this.setStartDate(this.startDate);
12617         this.setEndDate(this.endDate);
12618         
12619         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12620         
12621         this.fillDow();
12622         this.fillMonths();
12623         this.update();
12624         this.showMode();
12625         
12626         if(this.isInline) {
12627             this.show();
12628         }
12629     },
12630     
12631     picker : function()
12632     {
12633         return this.el.select('.datepicker', true).first();
12634     },
12635     
12636     fillDow: function()
12637     {
12638         var dowCnt = this.weekStart;
12639         
12640         var dow = {
12641             tag: 'tr',
12642             cn: [
12643                 
12644             ]
12645         };
12646         
12647         if(this.calendarWeeks){
12648             dow.cn.push({
12649                 tag: 'th',
12650                 cls: 'cw',
12651                 html: '&nbsp;'
12652             })
12653         }
12654         
12655         while (dowCnt < this.weekStart + 7) {
12656             dow.cn.push({
12657                 tag: 'th',
12658                 cls: 'dow',
12659                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12660             });
12661         }
12662         
12663         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12664     },
12665     
12666     fillMonths: function()
12667     {    
12668         var i = 0
12669         var months = this.picker().select('>.datepicker-months td', true).first();
12670         
12671         months.dom.innerHTML = '';
12672         
12673         while (i < 12) {
12674             var month = {
12675                 tag: 'span',
12676                 cls: 'month',
12677                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12678             }
12679             
12680             months.createChild(month);
12681         }
12682         
12683     },
12684     
12685     update: function(){
12686         
12687         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12688         
12689         if (this.date < this.startDate) {
12690             this.viewDate = new Date(this.startDate);
12691         } else if (this.date > this.endDate) {
12692             this.viewDate = new Date(this.endDate);
12693         } else {
12694             this.viewDate = new Date(this.date);
12695         }
12696         
12697         this.fill();
12698     },
12699     
12700     fill: function() {
12701         var d = new Date(this.viewDate),
12702                 year = d.getUTCFullYear(),
12703                 month = d.getUTCMonth(),
12704                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12705                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12706                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12707                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12708                 currentDate = this.date && this.date.valueOf(),
12709                 today = this.UTCToday();
12710         
12711         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12712         
12713 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12714         
12715 //        this.picker.select('>tfoot th.today').
12716 //                                              .text(dates[this.language].today)
12717 //                                              .toggle(this.todayBtn !== false);
12718     
12719         this.updateNavArrows();
12720         this.fillMonths();
12721                                                 
12722         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12723         
12724         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12725          
12726         prevMonth.setUTCDate(day);
12727         
12728         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12729         
12730         var nextMonth = new Date(prevMonth);
12731         
12732         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12733         
12734         nextMonth = nextMonth.valueOf();
12735         
12736         var fillMonths = false;
12737         
12738         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12739         
12740         while(prevMonth.valueOf() < nextMonth) {
12741             var clsName = '';
12742             
12743             if (prevMonth.getUTCDay() === this.weekStart) {
12744                 if(fillMonths){
12745                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12746                 }
12747                     
12748                 fillMonths = {
12749                     tag: 'tr',
12750                     cn: []
12751                 };
12752                 
12753                 if(this.calendarWeeks){
12754                     // ISO 8601: First week contains first thursday.
12755                     // ISO also states week starts on Monday, but we can be more abstract here.
12756                     var
12757                     // Start of current week: based on weekstart/current date
12758                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12759                     // Thursday of this week
12760                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12761                     // First Thursday of year, year from thursday
12762                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12763                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12764                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12765                     
12766                     fillMonths.cn.push({
12767                         tag: 'td',
12768                         cls: 'cw',
12769                         html: calWeek
12770                     });
12771                 }
12772             }
12773             
12774             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12775                 clsName += ' old';
12776             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12777                 clsName += ' new';
12778             }
12779             if (this.todayHighlight &&
12780                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12781                 prevMonth.getUTCMonth() == today.getMonth() &&
12782                 prevMonth.getUTCDate() == today.getDate()) {
12783                 clsName += ' today';
12784             }
12785             
12786             if (currentDate && prevMonth.valueOf() === currentDate) {
12787                 clsName += ' active';
12788             }
12789             
12790             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12791                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12792                     clsName += ' disabled';
12793             }
12794             
12795             fillMonths.cn.push({
12796                 tag: 'td',
12797                 cls: 'day ' + clsName,
12798                 html: prevMonth.getDate()
12799             })
12800             
12801             prevMonth.setDate(prevMonth.getDate()+1);
12802         }
12803           
12804         var currentYear = this.date && this.date.getUTCFullYear();
12805         var currentMonth = this.date && this.date.getUTCMonth();
12806         
12807         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12808         
12809         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12810             v.removeClass('active');
12811             
12812             if(currentYear === year && k === currentMonth){
12813                 v.addClass('active');
12814             }
12815             
12816             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12817                 v.addClass('disabled');
12818             }
12819             
12820         });
12821         
12822         
12823         year = parseInt(year/10, 10) * 10;
12824         
12825         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12826         
12827         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12828         
12829         year -= 1;
12830         for (var i = -1; i < 11; i++) {
12831             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12832                 tag: 'span',
12833                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12834                 html: year
12835             })
12836             
12837             year += 1;
12838         }
12839     },
12840     
12841     showMode: function(dir) {
12842         if (dir) {
12843             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12844         }
12845         Roo.each(this.picker().select('>div',true).elements, function(v){
12846             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12847             v.hide();
12848         });
12849         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12850     },
12851     
12852     place: function()
12853     {
12854         if(this.isInline) return;
12855         
12856         this.picker().removeClass(['bottom', 'top']);
12857         
12858         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12859             /*
12860              * place to the top of element!
12861              *
12862              */
12863             
12864             this.picker().addClass('top');
12865             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12866             
12867             return;
12868         }
12869         
12870         this.picker().addClass('bottom');
12871         
12872         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12873     },
12874     
12875     parseDate : function(value){
12876         if(!value || value instanceof Date){
12877             return value;
12878         }
12879         var v = Date.parseDate(value, this.format);
12880         if (!v && this.useIso) {
12881             v = Date.parseDate(value, 'Y-m-d');
12882         }
12883         if(!v && this.altFormats){
12884             if(!this.altFormatsArray){
12885                 this.altFormatsArray = this.altFormats.split("|");
12886             }
12887             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12888                 v = Date.parseDate(value, this.altFormatsArray[i]);
12889             }
12890         }
12891         return v;
12892     },
12893     
12894     formatDate : function(date, fmt){
12895         return (!date || !(date instanceof Date)) ?
12896         date : date.dateFormat(fmt || this.format);
12897     },
12898     
12899     onFocus : function()
12900     {
12901         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12902         this.show();
12903     },
12904     
12905     onBlur : function()
12906     {
12907         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12908         this.hide();
12909     },
12910     
12911     show : function()
12912     {
12913         this.picker().show();
12914         this.update();
12915         this.place();
12916         
12917         this.fireEvent('show', this, this.date);
12918     },
12919     
12920     hide : function()
12921     {
12922         if(this.isInline) return;
12923         this.picker().hide();
12924         this.viewMode = this.startViewMode;
12925         this.showMode();
12926         
12927         this.fireEvent('hide', this, this.date);
12928         
12929     },
12930     
12931     onMousedown: function(e){
12932         e.stopPropagation();
12933         e.preventDefault();
12934     },
12935     
12936     keyup: function(e){
12937         Roo.bootstrap.DateField.superclass.keyup.call(this);
12938         this.update();
12939         
12940     },
12941
12942     setValue: function(v){
12943         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12944         
12945         this.fireEvent('select', this, this.date);
12946         
12947     },
12948     
12949     fireKey: function(e){
12950         if (!this.picker().isVisible()){
12951             if (e.keyCode == 27) // allow escape to hide and re-show picker
12952                 this.show();
12953             return;
12954         }
12955         var dateChanged = false,
12956         dir, day, month,
12957         newDate, newViewDate;
12958         switch(e.keyCode){
12959             case 27: // escape
12960                 this.hide();
12961                 e.preventDefault();
12962                 break;
12963             case 37: // left
12964             case 39: // right
12965                 if (!this.keyboardNavigation) break;
12966                 dir = e.keyCode == 37 ? -1 : 1;
12967                 
12968                 if (e.ctrlKey){
12969                     newDate = this.moveYear(this.date, dir);
12970                     newViewDate = this.moveYear(this.viewDate, dir);
12971                 } else if (e.shiftKey){
12972                     newDate = this.moveMonth(this.date, dir);
12973                     newViewDate = this.moveMonth(this.viewDate, dir);
12974                 } else {
12975                     newDate = new Date(this.date);
12976                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12977                     newViewDate = new Date(this.viewDate);
12978                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12979                 }
12980                 if (this.dateWithinRange(newDate)){
12981                     this.date = newDate;
12982                     this.viewDate = newViewDate;
12983                     this.setValue(this.formatDate(this.date));
12984                     this.update();
12985                     e.preventDefault();
12986                     dateChanged = true;
12987                 }
12988                 break;
12989             case 38: // up
12990             case 40: // down
12991                 if (!this.keyboardNavigation) break;
12992                 dir = e.keyCode == 38 ? -1 : 1;
12993                 if (e.ctrlKey){
12994                     newDate = this.moveYear(this.date, dir);
12995                     newViewDate = this.moveYear(this.viewDate, dir);
12996                 } else if (e.shiftKey){
12997                     newDate = this.moveMonth(this.date, dir);
12998                     newViewDate = this.moveMonth(this.viewDate, dir);
12999                 } else {
13000                     newDate = new Date(this.date);
13001                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13002                     newViewDate = new Date(this.viewDate);
13003                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13004                 }
13005                 if (this.dateWithinRange(newDate)){
13006                     this.date = newDate;
13007                     this.viewDate = newViewDate;
13008                     this.setValue(this.formatDate(this.date));
13009                     this.update();
13010                     e.preventDefault();
13011                     dateChanged = true;
13012                 }
13013                 break;
13014             case 13: // enter
13015                 this.setValue(this.formatDate(this.date));
13016                 this.hide();
13017                 e.preventDefault();
13018                 break;
13019             case 9: // tab
13020                 this.setValue(this.formatDate(this.date));
13021                 this.hide();
13022                 break;
13023         }
13024     },
13025     
13026     
13027     onClick: function(e) {
13028         e.stopPropagation();
13029         e.preventDefault();
13030         
13031         var target = e.getTarget();
13032         
13033         if(target.nodeName.toLowerCase() === 'i'){
13034             target = Roo.get(target).dom.parentNode;
13035         }
13036         
13037         var nodeName = target.nodeName;
13038         var className = target.className;
13039         var html = target.innerHTML;
13040         
13041         switch(nodeName.toLowerCase()) {
13042             case 'th':
13043                 switch(className) {
13044                     case 'switch':
13045                         this.showMode(1);
13046                         break;
13047                     case 'prev':
13048                     case 'next':
13049                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13050                         switch(this.viewMode){
13051                                 case 0:
13052                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13053                                         break;
13054                                 case 1:
13055                                 case 2:
13056                                         this.viewDate = this.moveYear(this.viewDate, dir);
13057                                         break;
13058                         }
13059                         this.fill();
13060                         break;
13061                     case 'today':
13062                         var date = new Date();
13063                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13064                         this.fill()
13065                         this.setValue(this.formatDate(this.date));
13066                         this.hide();
13067                         break;
13068                 }
13069                 break;
13070             case 'span':
13071                 if (className.indexOf('disabled') === -1) {
13072                     this.viewDate.setUTCDate(1);
13073                     if (className.indexOf('month') !== -1) {
13074                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13075                     } else {
13076                         var year = parseInt(html, 10) || 0;
13077                         this.viewDate.setUTCFullYear(year);
13078                         
13079                     }
13080                     this.showMode(-1);
13081                     this.fill();
13082                 }
13083                 break;
13084                 
13085             case 'td':
13086                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13087                     var day = parseInt(html, 10) || 1;
13088                     var year = this.viewDate.getUTCFullYear(),
13089                         month = this.viewDate.getUTCMonth();
13090
13091                     if (className.indexOf('old') !== -1) {
13092                         if(month === 0 ){
13093                             month = 11;
13094                             year -= 1;
13095                         }else{
13096                             month -= 1;
13097                         }
13098                     } else if (className.indexOf('new') !== -1) {
13099                         if (month == 11) {
13100                             month = 0;
13101                             year += 1;
13102                         } else {
13103                             month += 1;
13104                         }
13105                     }
13106                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13107                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13108                     this.fill();
13109                     this.setValue(this.formatDate(this.date));
13110                     this.hide();
13111                 }
13112                 break;
13113         }
13114     },
13115     
13116     setStartDate: function(startDate){
13117         this.startDate = startDate || -Infinity;
13118         if (this.startDate !== -Infinity) {
13119             this.startDate = this.parseDate(this.startDate);
13120         }
13121         this.update();
13122         this.updateNavArrows();
13123     },
13124
13125     setEndDate: function(endDate){
13126         this.endDate = endDate || Infinity;
13127         if (this.endDate !== Infinity) {
13128             this.endDate = this.parseDate(this.endDate);
13129         }
13130         this.update();
13131         this.updateNavArrows();
13132     },
13133     
13134     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13135         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13136         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13137             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13138         }
13139         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13140             return parseInt(d, 10);
13141         });
13142         this.update();
13143         this.updateNavArrows();
13144     },
13145     
13146     updateNavArrows: function() {
13147         var d = new Date(this.viewDate),
13148         year = d.getUTCFullYear(),
13149         month = d.getUTCMonth();
13150         
13151         Roo.each(this.picker().select('.prev', true).elements, function(v){
13152             v.show();
13153             switch (this.viewMode) {
13154                 case 0:
13155
13156                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13157                         v.hide();
13158                     }
13159                     break;
13160                 case 1:
13161                 case 2:
13162                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13163                         v.hide();
13164                     }
13165                     break;
13166             }
13167         });
13168         
13169         Roo.each(this.picker().select('.next', true).elements, function(v){
13170             v.show();
13171             switch (this.viewMode) {
13172                 case 0:
13173
13174                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13175                         v.hide();
13176                     }
13177                     break;
13178                 case 1:
13179                 case 2:
13180                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13181                         v.hide();
13182                     }
13183                     break;
13184             }
13185         })
13186     },
13187     
13188     moveMonth: function(date, dir){
13189         if (!dir) return date;
13190         var new_date = new Date(date.valueOf()),
13191         day = new_date.getUTCDate(),
13192         month = new_date.getUTCMonth(),
13193         mag = Math.abs(dir),
13194         new_month, test;
13195         dir = dir > 0 ? 1 : -1;
13196         if (mag == 1){
13197             test = dir == -1
13198             // If going back one month, make sure month is not current month
13199             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13200             ? function(){
13201                 return new_date.getUTCMonth() == month;
13202             }
13203             // If going forward one month, make sure month is as expected
13204             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13205             : function(){
13206                 return new_date.getUTCMonth() != new_month;
13207             };
13208             new_month = month + dir;
13209             new_date.setUTCMonth(new_month);
13210             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13211             if (new_month < 0 || new_month > 11)
13212                 new_month = (new_month + 12) % 12;
13213         } else {
13214             // For magnitudes >1, move one month at a time...
13215             for (var i=0; i<mag; i++)
13216                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13217                 new_date = this.moveMonth(new_date, dir);
13218             // ...then reset the day, keeping it in the new month
13219             new_month = new_date.getUTCMonth();
13220             new_date.setUTCDate(day);
13221             test = function(){
13222                 return new_month != new_date.getUTCMonth();
13223             };
13224         }
13225         // Common date-resetting loop -- if date is beyond end of month, make it
13226         // end of month
13227         while (test()){
13228             new_date.setUTCDate(--day);
13229             new_date.setUTCMonth(new_month);
13230         }
13231         return new_date;
13232     },
13233
13234     moveYear: function(date, dir){
13235         return this.moveMonth(date, dir*12);
13236     },
13237
13238     dateWithinRange: function(date){
13239         return date >= this.startDate && date <= this.endDate;
13240     },
13241
13242     
13243     remove: function() {
13244         this.picker().remove();
13245     }
13246    
13247 });
13248
13249 Roo.apply(Roo.bootstrap.DateField,  {
13250     
13251     head : {
13252         tag: 'thead',
13253         cn: [
13254         {
13255             tag: 'tr',
13256             cn: [
13257             {
13258                 tag: 'th',
13259                 cls: 'prev',
13260                 html: '<i class="icon-arrow-left"/>'
13261             },
13262             {
13263                 tag: 'th',
13264                 cls: 'switch',
13265                 colspan: '5'
13266             },
13267             {
13268                 tag: 'th',
13269                 cls: 'next',
13270                 html: '<i class="icon-arrow-right"/>'
13271             }
13272
13273             ]
13274         }
13275         ]
13276     },
13277     
13278     content : {
13279         tag: 'tbody',
13280         cn: [
13281         {
13282             tag: 'tr',
13283             cn: [
13284             {
13285                 tag: 'td',
13286                 colspan: '7'
13287             }
13288             ]
13289         }
13290         ]
13291     },
13292     
13293     footer : {
13294         tag: 'tfoot',
13295         cn: [
13296         {
13297             tag: 'tr',
13298             cn: [
13299             {
13300                 tag: 'th',
13301                 colspan: '7',
13302                 cls: 'today'
13303             }
13304                     
13305             ]
13306         }
13307         ]
13308     },
13309     
13310     dates:{
13311         en: {
13312             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13313             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13314             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13315             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13316             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13317             today: "Today"
13318         }
13319     },
13320     
13321     modes: [
13322     {
13323         clsName: 'days',
13324         navFnc: 'Month',
13325         navStep: 1
13326     },
13327     {
13328         clsName: 'months',
13329         navFnc: 'FullYear',
13330         navStep: 1
13331     },
13332     {
13333         clsName: 'years',
13334         navFnc: 'FullYear',
13335         navStep: 10
13336     }]
13337 });
13338
13339 Roo.apply(Roo.bootstrap.DateField,  {
13340   
13341     template : {
13342         tag: 'div',
13343         cls: 'datepicker dropdown-menu',
13344         cn: [
13345         {
13346             tag: 'div',
13347             cls: 'datepicker-days',
13348             cn: [
13349             {
13350                 tag: 'table',
13351                 cls: 'table-condensed',
13352                 cn:[
13353                 Roo.bootstrap.DateField.head,
13354                 {
13355                     tag: 'tbody'
13356                 },
13357                 Roo.bootstrap.DateField.footer
13358                 ]
13359             }
13360             ]
13361         },
13362         {
13363             tag: 'div',
13364             cls: 'datepicker-months',
13365             cn: [
13366             {
13367                 tag: 'table',
13368                 cls: 'table-condensed',
13369                 cn:[
13370                 Roo.bootstrap.DateField.head,
13371                 Roo.bootstrap.DateField.content,
13372                 Roo.bootstrap.DateField.footer
13373                 ]
13374             }
13375             ]
13376         },
13377         {
13378             tag: 'div',
13379             cls: 'datepicker-years',
13380             cn: [
13381             {
13382                 tag: 'table',
13383                 cls: 'table-condensed',
13384                 cn:[
13385                 Roo.bootstrap.DateField.head,
13386                 Roo.bootstrap.DateField.content,
13387                 Roo.bootstrap.DateField.footer
13388                 ]
13389             }
13390             ]
13391         }
13392         ]
13393     }
13394 });
13395
13396  
13397
13398  /*
13399  * - LGPL
13400  *
13401  * TimeField
13402  * 
13403  */
13404
13405 /**
13406  * @class Roo.bootstrap.TimeField
13407  * @extends Roo.bootstrap.Input
13408  * Bootstrap DateField class
13409  * 
13410  * 
13411  * @constructor
13412  * Create a new TimeField
13413  * @param {Object} config The config object
13414  */
13415
13416 Roo.bootstrap.TimeField = function(config){
13417     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13418     this.addEvents({
13419             /**
13420              * @event show
13421              * Fires when this field show.
13422              * @param {Roo.bootstrap.DateField} this
13423              * @param {Mixed} date The date value
13424              */
13425             show : true,
13426             /**
13427              * @event show
13428              * Fires when this field hide.
13429              * @param {Roo.bootstrap.DateField} this
13430              * @param {Mixed} date The date value
13431              */
13432             hide : true,
13433             /**
13434              * @event select
13435              * Fires when select a date.
13436              * @param {Roo.bootstrap.DateField} this
13437              * @param {Mixed} date The date value
13438              */
13439             select : true
13440         });
13441 };
13442
13443 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13444     
13445     /**
13446      * @cfg {String} format
13447      * The default time format string which can be overriden for localization support.  The format must be
13448      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13449      */
13450     format : "H:i",
13451        
13452     onRender: function(ct, position)
13453     {
13454         
13455         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13456                 
13457         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13458         
13459         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13460         
13461         this.pop = this.picker().select('>.datepicker-time',true).first();
13462         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13463         
13464         this.picker().on('mousedown', this.onMousedown, this);
13465         this.picker().on('click', this.onClick, this);
13466         
13467         this.picker().addClass('datepicker-dropdown');
13468     
13469         this.fillTime();
13470         this.update();
13471             
13472         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13473         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13474         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13475         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13476         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13477         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13478
13479     },
13480     
13481     fireKey: function(e){
13482         if (!this.picker().isVisible()){
13483             if (e.keyCode == 27) // allow escape to hide and re-show picker
13484                 this.show();
13485             return;
13486         }
13487
13488         e.preventDefault();
13489         
13490         switch(e.keyCode){
13491             case 27: // escape
13492                 this.hide();
13493                 break;
13494             case 37: // left
13495             case 39: // right
13496                 this.onTogglePeriod();
13497                 break;
13498             case 38: // up
13499                 this.onIncrementMinutes();
13500                 break;
13501             case 40: // down
13502                 this.onDecrementMinutes();
13503                 break;
13504             case 13: // enter
13505             case 9: // tab
13506                 this.setTime();
13507                 break;
13508         }
13509     },
13510     
13511     onClick: function(e) {
13512         e.stopPropagation();
13513         e.preventDefault();
13514     },
13515     
13516     picker : function()
13517     {
13518         return this.el.select('.datepicker', true).first();
13519     },
13520     
13521     fillTime: function()
13522     {    
13523         var time = this.pop.select('tbody', true).first();
13524         
13525         time.dom.innerHTML = '';
13526         
13527         time.createChild({
13528             tag: 'tr',
13529             cn: [
13530                 {
13531                     tag: 'td',
13532                     cn: [
13533                         {
13534                             tag: 'a',
13535                             href: '#',
13536                             cls: 'btn',
13537                             cn: [
13538                                 {
13539                                     tag: 'span',
13540                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13541                                 }
13542                             ]
13543                         } 
13544                     ]
13545                 },
13546                 {
13547                     tag: 'td',
13548                     cls: 'separator'
13549                 },
13550                 {
13551                     tag: 'td',
13552                     cn: [
13553                         {
13554                             tag: 'a',
13555                             href: '#',
13556                             cls: 'btn',
13557                             cn: [
13558                                 {
13559                                     tag: 'span',
13560                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13561                                 }
13562                             ]
13563                         }
13564                     ]
13565                 },
13566                 {
13567                     tag: 'td',
13568                     cls: 'separator'
13569                 }
13570             ]
13571         });
13572         
13573         time.createChild({
13574             tag: 'tr',
13575             cn: [
13576                 {
13577                     tag: 'td',
13578                     cn: [
13579                         {
13580                             tag: 'span',
13581                             cls: 'timepicker-hour',
13582                             html: '00'
13583                         }  
13584                     ]
13585                 },
13586                 {
13587                     tag: 'td',
13588                     cls: 'separator',
13589                     html: ':'
13590                 },
13591                 {
13592                     tag: 'td',
13593                     cn: [
13594                         {
13595                             tag: 'span',
13596                             cls: 'timepicker-minute',
13597                             html: '00'
13598                         }  
13599                     ]
13600                 },
13601                 {
13602                     tag: 'td',
13603                     cls: 'separator'
13604                 },
13605                 {
13606                     tag: 'td',
13607                     cn: [
13608                         {
13609                             tag: 'button',
13610                             type: 'button',
13611                             cls: 'btn btn-primary period',
13612                             html: 'AM'
13613                             
13614                         }
13615                     ]
13616                 }
13617             ]
13618         });
13619         
13620         time.createChild({
13621             tag: 'tr',
13622             cn: [
13623                 {
13624                     tag: 'td',
13625                     cn: [
13626                         {
13627                             tag: 'a',
13628                             href: '#',
13629                             cls: 'btn',
13630                             cn: [
13631                                 {
13632                                     tag: 'span',
13633                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13634                                 }
13635                             ]
13636                         }
13637                     ]
13638                 },
13639                 {
13640                     tag: 'td',
13641                     cls: 'separator'
13642                 },
13643                 {
13644                     tag: 'td',
13645                     cn: [
13646                         {
13647                             tag: 'a',
13648                             href: '#',
13649                             cls: 'btn',
13650                             cn: [
13651                                 {
13652                                     tag: 'span',
13653                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13654                                 }
13655                             ]
13656                         }
13657                     ]
13658                 },
13659                 {
13660                     tag: 'td',
13661                     cls: 'separator'
13662                 }
13663             ]
13664         });
13665         
13666     },
13667     
13668     update: function()
13669     {
13670         
13671         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13672         
13673         this.fill();
13674     },
13675     
13676     fill: function() 
13677     {
13678         var hours = this.time.getHours();
13679         var minutes = this.time.getMinutes();
13680         var period = 'AM';
13681         
13682         if(hours > 11){
13683             period = 'PM';
13684         }
13685         
13686         if(hours == 0){
13687             hours = 12;
13688         }
13689         
13690         
13691         if(hours > 12){
13692             hours = hours - 12;
13693         }
13694         
13695         if(hours < 10){
13696             hours = '0' + hours;
13697         }
13698         
13699         if(minutes < 10){
13700             minutes = '0' + minutes;
13701         }
13702         
13703         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13704         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13705         this.pop.select('button', true).first().dom.innerHTML = period;
13706         
13707     },
13708     
13709     place: function()
13710     {   
13711         this.picker().removeClass(['bottom', 'top']);
13712         
13713         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13714             /*
13715              * place to the top of element!
13716              *
13717              */
13718             
13719             this.picker().addClass('top');
13720             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13721             
13722             return;
13723         }
13724         
13725         this.picker().addClass('bottom');
13726         
13727         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13728     },
13729   
13730     onFocus : function()
13731     {
13732         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13733         this.show();
13734     },
13735     
13736     onBlur : function()
13737     {
13738         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13739         this.hide();
13740     },
13741     
13742     show : function()
13743     {
13744         this.picker().show();
13745         this.pop.show();
13746         this.update();
13747         this.place();
13748         
13749         this.fireEvent('show', this, this.date);
13750     },
13751     
13752     hide : function()
13753     {
13754         this.picker().hide();
13755         this.pop.hide();
13756         
13757         this.fireEvent('hide', this, this.date);
13758     },
13759     
13760     setTime : function()
13761     {
13762         this.hide();
13763         this.setValue(this.time.format(this.format));
13764         
13765         this.fireEvent('select', this, this.date);
13766         
13767         
13768     },
13769     
13770     onMousedown: function(e){
13771         e.stopPropagation();
13772         e.preventDefault();
13773     },
13774     
13775     onIncrementHours: function()
13776     {
13777         Roo.log('onIncrementHours');
13778         this.time = this.time.add(Date.HOUR, 1);
13779         this.update();
13780         
13781     },
13782     
13783     onDecrementHours: function()
13784     {
13785         Roo.log('onDecrementHours');
13786         this.time = this.time.add(Date.HOUR, -1);
13787         this.update();
13788     },
13789     
13790     onIncrementMinutes: function()
13791     {
13792         Roo.log('onIncrementMinutes');
13793         this.time = this.time.add(Date.MINUTE, 1);
13794         this.update();
13795     },
13796     
13797     onDecrementMinutes: function()
13798     {
13799         Roo.log('onDecrementMinutes');
13800         this.time = this.time.add(Date.MINUTE, -1);
13801         this.update();
13802     },
13803     
13804     onTogglePeriod: function()
13805     {
13806         Roo.log('onTogglePeriod');
13807         this.time = this.time.add(Date.HOUR, 12);
13808         this.update();
13809     }
13810     
13811    
13812 });
13813
13814 Roo.apply(Roo.bootstrap.TimeField,  {
13815     
13816     content : {
13817         tag: 'tbody',
13818         cn: [
13819             {
13820                 tag: 'tr',
13821                 cn: [
13822                 {
13823                     tag: 'td',
13824                     colspan: '7'
13825                 }
13826                 ]
13827             }
13828         ]
13829     },
13830     
13831     footer : {
13832         tag: 'tfoot',
13833         cn: [
13834             {
13835                 tag: 'tr',
13836                 cn: [
13837                 {
13838                     tag: 'th',
13839                     colspan: '7',
13840                     cls: '',
13841                     cn: [
13842                         {
13843                             tag: 'button',
13844                             cls: 'btn btn-info ok',
13845                             html: 'OK'
13846                         }
13847                     ]
13848                 }
13849
13850                 ]
13851             }
13852         ]
13853     }
13854 });
13855
13856 Roo.apply(Roo.bootstrap.TimeField,  {
13857   
13858     template : {
13859         tag: 'div',
13860         cls: 'datepicker dropdown-menu',
13861         cn: [
13862             {
13863                 tag: 'div',
13864                 cls: 'datepicker-time',
13865                 cn: [
13866                 {
13867                     tag: 'table',
13868                     cls: 'table-condensed',
13869                     cn:[
13870                     Roo.bootstrap.TimeField.content,
13871                     Roo.bootstrap.TimeField.footer
13872                     ]
13873                 }
13874                 ]
13875             }
13876         ]
13877     }
13878 });
13879
13880  
13881
13882  /*
13883  * - LGPL
13884  *
13885  * CheckBox
13886  * 
13887  */
13888
13889 /**
13890  * @class Roo.bootstrap.CheckBox
13891  * @extends Roo.bootstrap.Input
13892  * Bootstrap CheckBox class
13893  * 
13894  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13895  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13896  * @cfg {String} boxLabel The text that appears beside the checkbox
13897  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13898  * @cfg {Boolean} checked initnal the element
13899  * 
13900  * 
13901  * @constructor
13902  * Create a new CheckBox
13903  * @param {Object} config The config object
13904  */
13905
13906 Roo.bootstrap.CheckBox = function(config){
13907     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13908    
13909         this.addEvents({
13910             /**
13911             * @event check
13912             * Fires when the element is checked or unchecked.
13913             * @param {Roo.bootstrap.CheckBox} this This input
13914             * @param {Boolean} checked The new checked value
13915             */
13916            check : true
13917         });
13918 };
13919
13920 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13921     
13922     inputType: 'checkbox',
13923     inputValue: 1,
13924     valueOff: 0,
13925     boxLabel: false,
13926     checked: false,
13927     weight : false,
13928     
13929     getAutoCreate : function()
13930     {
13931         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13932         
13933         var id = Roo.id();
13934         
13935         var cfg = {};
13936         
13937         cfg.cls = 'form-group checkbox' //input-group
13938         
13939         
13940         
13941         
13942         var input =  {
13943             tag: 'input',
13944             id : id,
13945             type : this.inputType,
13946             value : (!this.checked) ? this.valueOff : this.inputValue,
13947             cls : 'roo-checkbox', //'form-box',
13948             placeholder : this.placeholder || ''
13949             
13950         };
13951         
13952         if (this.weight) { // Validity check?
13953             cfg.cls += " checkbox-" + this.weight;
13954         }
13955         
13956         if (this.disabled) {
13957             input.disabled=true;
13958         }
13959         
13960         if(this.checked){
13961             input.checked = this.checked;
13962         }
13963         
13964         if (this.name) {
13965             input.name = this.name;
13966         }
13967         
13968         if (this.size) {
13969             input.cls += ' input-' + this.size;
13970         }
13971         
13972         var settings=this;
13973         ['xs','sm','md','lg'].map(function(size){
13974             if (settings[size]) {
13975                 cfg.cls += ' col-' + size + '-' + settings[size];
13976             }
13977         });
13978         
13979        
13980         
13981         var inputblock = input;
13982         
13983         
13984         
13985         
13986         if (this.before || this.after) {
13987             
13988             inputblock = {
13989                 cls : 'input-group',
13990                 cn :  [] 
13991             };
13992             if (this.before) {
13993                 inputblock.cn.push({
13994                     tag :'span',
13995                     cls : 'input-group-addon',
13996                     html : this.before
13997                 });
13998             }
13999             inputblock.cn.push(input);
14000             if (this.after) {
14001                 inputblock.cn.push({
14002                     tag :'span',
14003                     cls : 'input-group-addon',
14004                     html : this.after
14005                 });
14006             }
14007             
14008         };
14009         
14010         if (align ==='left' && this.fieldLabel.length) {
14011                 Roo.log("left and has label");
14012                 cfg.cn = [
14013                     
14014                     {
14015                         tag: 'label',
14016                         'for' :  id,
14017                         cls : 'control-label col-md-' + this.labelWidth,
14018                         html : this.fieldLabel
14019                         
14020                     },
14021                     {
14022                         cls : "col-md-" + (12 - this.labelWidth), 
14023                         cn: [
14024                             inputblock
14025                         ]
14026                     }
14027                     
14028                 ];
14029         } else if ( this.fieldLabel.length) {
14030                 Roo.log(" label");
14031                 cfg.cn = [
14032                    
14033                     {
14034                         tag: this.boxLabel ? 'span' : 'label',
14035                         'for': id,
14036                         cls: 'control-label box-input-label',
14037                         //cls : 'input-group-addon',
14038                         html : this.fieldLabel
14039                         
14040                     },
14041                     
14042                     inputblock
14043                     
14044                 ];
14045
14046         } else {
14047             
14048                 Roo.log(" no label && no align");
14049                 cfg.cn = [  inputblock ] ;
14050                 
14051                 
14052         };
14053          if(this.boxLabel){
14054             cfg.cn.push( {
14055                 tag: 'label',
14056                 'for': id,
14057                 cls: 'box-label',
14058                 html: this.boxLabel
14059                 
14060             });
14061         }
14062         
14063         
14064        
14065         return cfg;
14066         
14067     },
14068     
14069     /**
14070      * return the real input element.
14071      */
14072     inputEl: function ()
14073     {
14074         return this.el.select('input.roo-checkbox',true).first();
14075     },
14076     
14077     label: function()
14078     {
14079         return this.el.select('label.control-label',true).first();
14080     },
14081     
14082     initEvents : function()
14083     {
14084 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14085         
14086         this.inputEl().on('click', this.onClick,  this);
14087         
14088     },
14089     
14090     onClick : function()
14091     {   
14092         this.setChecked(!this.checked);
14093     },
14094     
14095     setChecked : function(state,suppressEvent)
14096     {
14097         this.checked = state;
14098         
14099         this.inputEl().dom.checked = state;
14100         
14101         if(suppressEvent !== true){
14102             this.fireEvent('check', this, state);
14103         }
14104         
14105         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14106         
14107     },
14108     
14109     setValue : function(v,suppressEvent)
14110     {
14111         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14112     }
14113     
14114 });
14115
14116  
14117 /*
14118  * - LGPL
14119  *
14120  * Radio
14121  * 
14122  */
14123
14124 /**
14125  * @class Roo.bootstrap.Radio
14126  * @extends Roo.bootstrap.CheckBox
14127  * Bootstrap Radio class
14128
14129  * @constructor
14130  * Create a new Radio
14131  * @param {Object} config The config object
14132  */
14133
14134 Roo.bootstrap.Radio = function(config){
14135     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14136    
14137 };
14138
14139 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14140     
14141     inputType: 'radio',
14142     inputValue: '',
14143     valueOff: '',
14144     
14145     getAutoCreate : function()
14146     {
14147         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14148         
14149         var id = Roo.id();
14150         
14151         var cfg = {};
14152         
14153         cfg.cls = 'form-group radio' //input-group
14154         
14155         var input =  {
14156             tag: 'input',
14157             id : id,
14158             type : this.inputType,
14159             value : (!this.checked) ? this.valueOff : this.inputValue,
14160             cls : 'roo-radio',
14161             placeholder : this.placeholder || ''
14162             
14163         };
14164           if (this.weight) { // Validity check?
14165             cfg.cls += " radio-" + this.weight;
14166         }
14167         if (this.disabled) {
14168             input.disabled=true;
14169         }
14170         
14171         if(this.checked){
14172             input.checked = this.checked;
14173         }
14174         
14175         if (this.name) {
14176             input.name = this.name;
14177         }
14178         
14179         if (this.size) {
14180             input.cls += ' input-' + this.size;
14181         }
14182         
14183         var settings=this;
14184         ['xs','sm','md','lg'].map(function(size){
14185             if (settings[size]) {
14186                 cfg.cls += ' col-' + size + '-' + settings[size];
14187             }
14188         });
14189         
14190         var inputblock = input;
14191         
14192         if (this.before || this.after) {
14193             
14194             inputblock = {
14195                 cls : 'input-group',
14196                 cn :  [] 
14197             };
14198             if (this.before) {
14199                 inputblock.cn.push({
14200                     tag :'span',
14201                     cls : 'input-group-addon',
14202                     html : this.before
14203                 });
14204             }
14205             inputblock.cn.push(input);
14206             if (this.after) {
14207                 inputblock.cn.push({
14208                     tag :'span',
14209                     cls : 'input-group-addon',
14210                     html : this.after
14211                 });
14212             }
14213             
14214         };
14215         
14216         if (align ==='left' && this.fieldLabel.length) {
14217                 Roo.log("left and has label");
14218                 cfg.cn = [
14219                     
14220                     {
14221                         tag: 'label',
14222                         'for' :  id,
14223                         cls : 'control-label col-md-' + this.labelWidth,
14224                         html : this.fieldLabel
14225                         
14226                     },
14227                     {
14228                         cls : "col-md-" + (12 - this.labelWidth), 
14229                         cn: [
14230                             inputblock
14231                         ]
14232                     }
14233                     
14234                 ];
14235         } else if ( this.fieldLabel.length) {
14236                 Roo.log(" label");
14237                  cfg.cn = [
14238                    
14239                     {
14240                         tag: 'label',
14241                         'for': id,
14242                         cls: 'control-label box-input-label',
14243                         //cls : 'input-group-addon',
14244                         html : this.fieldLabel
14245                         
14246                     },
14247                     
14248                     inputblock
14249                     
14250                 ];
14251
14252         } else {
14253             
14254                    Roo.log(" no label && no align");
14255                 cfg.cn = [
14256                     
14257                         inputblock
14258                     
14259                 ];
14260                 
14261                 
14262         };
14263         
14264         if(this.boxLabel){
14265             cfg.cn.push({
14266                 tag: 'label',
14267                 'for': id,
14268                 cls: 'box-label',
14269                 html: this.boxLabel
14270             })
14271         }
14272         
14273         return cfg;
14274         
14275     },
14276     inputEl: function ()
14277     {
14278         return this.el.select('input.roo-radio',true).first();
14279     },
14280     onClick : function()
14281     {   
14282         this.setChecked(true);
14283     },
14284     
14285     setChecked : function(state,suppressEvent)
14286     {
14287         if(state){
14288             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14289                 v.dom.checked = false;
14290             });
14291         }
14292         
14293         this.checked = state;
14294         this.inputEl().dom.checked = state;
14295         
14296         if(suppressEvent !== true){
14297             this.fireEvent('check', this, state);
14298         }
14299         
14300         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14301         
14302     },
14303     
14304     getGroupValue : function()
14305     {
14306         var value = ''
14307         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14308             if(v.dom.checked == true){
14309                 value = v.dom.value;
14310             }
14311         });
14312         
14313         return value;
14314     },
14315     
14316     /**
14317      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14318      * @return {Mixed} value The field value
14319      */
14320     getValue : function(){
14321         return this.getGroupValue();
14322     }
14323     
14324 });
14325
14326  
14327 //<script type="text/javascript">
14328
14329 /*
14330  * Based  Ext JS Library 1.1.1
14331  * Copyright(c) 2006-2007, Ext JS, LLC.
14332  * LGPL
14333  *
14334  */
14335  
14336 /**
14337  * @class Roo.HtmlEditorCore
14338  * @extends Roo.Component
14339  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14340  *
14341  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14342  */
14343
14344 Roo.HtmlEditorCore = function(config){
14345     
14346     
14347     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14348     this.addEvents({
14349         /**
14350          * @event initialize
14351          * Fires when the editor is fully initialized (including the iframe)
14352          * @param {Roo.HtmlEditorCore} this
14353          */
14354         initialize: true,
14355         /**
14356          * @event activate
14357          * Fires when the editor is first receives the focus. Any insertion must wait
14358          * until after this event.
14359          * @param {Roo.HtmlEditorCore} this
14360          */
14361         activate: true,
14362          /**
14363          * @event beforesync
14364          * Fires before the textarea is updated with content from the editor iframe. Return false
14365          * to cancel the sync.
14366          * @param {Roo.HtmlEditorCore} this
14367          * @param {String} html
14368          */
14369         beforesync: true,
14370          /**
14371          * @event beforepush
14372          * Fires before the iframe editor is updated with content from the textarea. Return false
14373          * to cancel the push.
14374          * @param {Roo.HtmlEditorCore} this
14375          * @param {String} html
14376          */
14377         beforepush: true,
14378          /**
14379          * @event sync
14380          * Fires when the textarea is updated with content from the editor iframe.
14381          * @param {Roo.HtmlEditorCore} this
14382          * @param {String} html
14383          */
14384         sync: true,
14385          /**
14386          * @event push
14387          * Fires when the iframe editor is updated with content from the textarea.
14388          * @param {Roo.HtmlEditorCore} this
14389          * @param {String} html
14390          */
14391         push: true,
14392         
14393         /**
14394          * @event editorevent
14395          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14396          * @param {Roo.HtmlEditorCore} this
14397          */
14398         editorevent: true
14399     });
14400      
14401 };
14402
14403
14404 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14405
14406
14407      /**
14408      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14409      */
14410     
14411     owner : false,
14412     
14413      /**
14414      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14415      *                        Roo.resizable.
14416      */
14417     resizable : false,
14418      /**
14419      * @cfg {Number} height (in pixels)
14420      */   
14421     height: 300,
14422    /**
14423      * @cfg {Number} width (in pixels)
14424      */   
14425     width: 500,
14426     
14427     /**
14428      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14429      * 
14430      */
14431     stylesheets: false,
14432     
14433     // id of frame..
14434     frameId: false,
14435     
14436     // private properties
14437     validationEvent : false,
14438     deferHeight: true,
14439     initialized : false,
14440     activated : false,
14441     sourceEditMode : false,
14442     onFocus : Roo.emptyFn,
14443     iframePad:3,
14444     hideMode:'offsets',
14445     
14446     clearUp: true,
14447     
14448      
14449     
14450
14451     /**
14452      * Protected method that will not generally be called directly. It
14453      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14454      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14455      */
14456     getDocMarkup : function(){
14457         // body styles..
14458         var st = '';
14459         Roo.log(this.stylesheets);
14460         
14461         // inherit styels from page...?? 
14462         if (this.stylesheets === false) {
14463             
14464             Roo.get(document.head).select('style').each(function(node) {
14465                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14466             });
14467             
14468             Roo.get(document.head).select('link').each(function(node) { 
14469                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14470             });
14471             
14472         } else if (!this.stylesheets.length) {
14473                 // simple..
14474                 st = '<style type="text/css">' +
14475                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14476                    '</style>';
14477         } else {
14478             Roo.each(this.stylesheets, function(s) {
14479                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14480             });
14481             
14482         }
14483         
14484         st +=  '<style type="text/css">' +
14485             'IMG { cursor: pointer } ' +
14486         '</style>';
14487
14488         
14489         return '<html><head>' + st  +
14490             //<style type="text/css">' +
14491             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14492             //'</style>' +
14493             ' </head><body class="roo-htmleditor-body"></body></html>';
14494     },
14495
14496     // private
14497     onRender : function(ct, position)
14498     {
14499         var _t = this;
14500         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14501         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14502         
14503         
14504         this.el.dom.style.border = '0 none';
14505         this.el.dom.setAttribute('tabIndex', -1);
14506         this.el.addClass('x-hidden hide');
14507         
14508         
14509         
14510         if(Roo.isIE){ // fix IE 1px bogus margin
14511             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14512         }
14513        
14514         
14515         this.frameId = Roo.id();
14516         
14517          
14518         
14519         var iframe = this.owner.wrap.createChild({
14520             tag: 'iframe',
14521             cls: 'form-control', // bootstrap..
14522             id: this.frameId,
14523             name: this.frameId,
14524             frameBorder : 'no',
14525             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14526         }, this.el
14527         );
14528         
14529         
14530         this.iframe = iframe.dom;
14531
14532          this.assignDocWin();
14533         
14534         this.doc.designMode = 'on';
14535        
14536         this.doc.open();
14537         this.doc.write(this.getDocMarkup());
14538         this.doc.close();
14539
14540         
14541         var task = { // must defer to wait for browser to be ready
14542             run : function(){
14543                 //console.log("run task?" + this.doc.readyState);
14544                 this.assignDocWin();
14545                 if(this.doc.body || this.doc.readyState == 'complete'){
14546                     try {
14547                         this.doc.designMode="on";
14548                     } catch (e) {
14549                         return;
14550                     }
14551                     Roo.TaskMgr.stop(task);
14552                     this.initEditor.defer(10, this);
14553                 }
14554             },
14555             interval : 10,
14556             duration: 10000,
14557             scope: this
14558         };
14559         Roo.TaskMgr.start(task);
14560
14561         
14562          
14563     },
14564
14565     // private
14566     onResize : function(w, h)
14567     {
14568          Roo.log('resize: ' +w + ',' + h );
14569         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14570         if(!this.iframe){
14571             return;
14572         }
14573         if(typeof w == 'number'){
14574             
14575             this.iframe.style.width = w + 'px';
14576         }
14577         if(typeof h == 'number'){
14578             
14579             this.iframe.style.height = h + 'px';
14580             if(this.doc){
14581                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14582             }
14583         }
14584         
14585     },
14586
14587     /**
14588      * Toggles the editor between standard and source edit mode.
14589      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14590      */
14591     toggleSourceEdit : function(sourceEditMode){
14592         
14593         this.sourceEditMode = sourceEditMode === true;
14594         
14595         if(this.sourceEditMode){
14596  
14597             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14598             
14599         }else{
14600             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14601             //this.iframe.className = '';
14602             this.deferFocus();
14603         }
14604         //this.setSize(this.owner.wrap.getSize());
14605         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14606     },
14607
14608     
14609   
14610
14611     /**
14612      * Protected method that will not generally be called directly. If you need/want
14613      * custom HTML cleanup, this is the method you should override.
14614      * @param {String} html The HTML to be cleaned
14615      * return {String} The cleaned HTML
14616      */
14617     cleanHtml : function(html){
14618         html = String(html);
14619         if(html.length > 5){
14620             if(Roo.isSafari){ // strip safari nonsense
14621                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14622             }
14623         }
14624         if(html == '&nbsp;'){
14625             html = '';
14626         }
14627         return html;
14628     },
14629
14630     /**
14631      * HTML Editor -> Textarea
14632      * Protected method that will not generally be called directly. Syncs the contents
14633      * of the editor iframe with the textarea.
14634      */
14635     syncValue : function(){
14636         if(this.initialized){
14637             var bd = (this.doc.body || this.doc.documentElement);
14638             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14639             var html = bd.innerHTML;
14640             if(Roo.isSafari){
14641                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14642                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14643                 if(m && m[1]){
14644                     html = '<div style="'+m[0]+'">' + html + '</div>';
14645                 }
14646             }
14647             html = this.cleanHtml(html);
14648             // fix up the special chars.. normaly like back quotes in word...
14649             // however we do not want to do this with chinese..
14650             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14651                 var cc = b.charCodeAt();
14652                 if (
14653                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14654                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14655                     (cc >= 0xf900 && cc < 0xfb00 )
14656                 ) {
14657                         return b;
14658                 }
14659                 return "&#"+cc+";" 
14660             });
14661             if(this.owner.fireEvent('beforesync', this, html) !== false){
14662                 this.el.dom.value = html;
14663                 this.owner.fireEvent('sync', this, html);
14664             }
14665         }
14666     },
14667
14668     /**
14669      * Protected method that will not generally be called directly. Pushes the value of the textarea
14670      * into the iframe editor.
14671      */
14672     pushValue : function(){
14673         if(this.initialized){
14674             var v = this.el.dom.value.trim();
14675             
14676 //            if(v.length < 1){
14677 //                v = '&#160;';
14678 //            }
14679             
14680             if(this.owner.fireEvent('beforepush', this, v) !== false){
14681                 var d = (this.doc.body || this.doc.documentElement);
14682                 d.innerHTML = v;
14683                 this.cleanUpPaste();
14684                 this.el.dom.value = d.innerHTML;
14685                 this.owner.fireEvent('push', this, v);
14686             }
14687         }
14688     },
14689
14690     // private
14691     deferFocus : function(){
14692         this.focus.defer(10, this);
14693     },
14694
14695     // doc'ed in Field
14696     focus : function(){
14697         if(this.win && !this.sourceEditMode){
14698             this.win.focus();
14699         }else{
14700             this.el.focus();
14701         }
14702     },
14703     
14704     assignDocWin: function()
14705     {
14706         var iframe = this.iframe;
14707         
14708          if(Roo.isIE){
14709             this.doc = iframe.contentWindow.document;
14710             this.win = iframe.contentWindow;
14711         } else {
14712             if (!Roo.get(this.frameId)) {
14713                 return;
14714             }
14715             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14716             this.win = Roo.get(this.frameId).dom.contentWindow;
14717         }
14718     },
14719     
14720     // private
14721     initEditor : function(){
14722         //console.log("INIT EDITOR");
14723         this.assignDocWin();
14724         
14725         
14726         
14727         this.doc.designMode="on";
14728         this.doc.open();
14729         this.doc.write(this.getDocMarkup());
14730         this.doc.close();
14731         
14732         var dbody = (this.doc.body || this.doc.documentElement);
14733         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14734         // this copies styles from the containing element into thsi one..
14735         // not sure why we need all of this..
14736         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14737         ss['background-attachment'] = 'fixed'; // w3c
14738         dbody.bgProperties = 'fixed'; // ie
14739         Roo.DomHelper.applyStyles(dbody, ss);
14740         Roo.EventManager.on(this.doc, {
14741             //'mousedown': this.onEditorEvent,
14742             'mouseup': this.onEditorEvent,
14743             'dblclick': this.onEditorEvent,
14744             'click': this.onEditorEvent,
14745             'keyup': this.onEditorEvent,
14746             buffer:100,
14747             scope: this
14748         });
14749         if(Roo.isGecko){
14750             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14751         }
14752         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14753             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14754         }
14755         this.initialized = true;
14756
14757         this.owner.fireEvent('initialize', this);
14758         this.pushValue();
14759     },
14760
14761     // private
14762     onDestroy : function(){
14763         
14764         
14765         
14766         if(this.rendered){
14767             
14768             //for (var i =0; i < this.toolbars.length;i++) {
14769             //    // fixme - ask toolbars for heights?
14770             //    this.toolbars[i].onDestroy();
14771            // }
14772             
14773             //this.wrap.dom.innerHTML = '';
14774             //this.wrap.remove();
14775         }
14776     },
14777
14778     // private
14779     onFirstFocus : function(){
14780         
14781         this.assignDocWin();
14782         
14783         
14784         this.activated = true;
14785          
14786     
14787         if(Roo.isGecko){ // prevent silly gecko errors
14788             this.win.focus();
14789             var s = this.win.getSelection();
14790             if(!s.focusNode || s.focusNode.nodeType != 3){
14791                 var r = s.getRangeAt(0);
14792                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14793                 r.collapse(true);
14794                 this.deferFocus();
14795             }
14796             try{
14797                 this.execCmd('useCSS', true);
14798                 this.execCmd('styleWithCSS', false);
14799             }catch(e){}
14800         }
14801         this.owner.fireEvent('activate', this);
14802     },
14803
14804     // private
14805     adjustFont: function(btn){
14806         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14807         //if(Roo.isSafari){ // safari
14808         //    adjust *= 2;
14809        // }
14810         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14811         if(Roo.isSafari){ // safari
14812             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14813             v =  (v < 10) ? 10 : v;
14814             v =  (v > 48) ? 48 : v;
14815             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14816             
14817         }
14818         
14819         
14820         v = Math.max(1, v+adjust);
14821         
14822         this.execCmd('FontSize', v  );
14823     },
14824
14825     onEditorEvent : function(e){
14826         this.owner.fireEvent('editorevent', this, e);
14827       //  this.updateToolbar();
14828         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14829     },
14830
14831     insertTag : function(tg)
14832     {
14833         // could be a bit smarter... -> wrap the current selected tRoo..
14834         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14835             
14836             range = this.createRange(this.getSelection());
14837             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14838             wrappingNode.appendChild(range.extractContents());
14839             range.insertNode(wrappingNode);
14840
14841             return;
14842             
14843             
14844             
14845         }
14846         this.execCmd("formatblock",   tg);
14847         
14848     },
14849     
14850     insertText : function(txt)
14851     {
14852         
14853         
14854         var range = this.createRange();
14855         range.deleteContents();
14856                //alert(Sender.getAttribute('label'));
14857                
14858         range.insertNode(this.doc.createTextNode(txt));
14859     } ,
14860     
14861      
14862
14863     /**
14864      * Executes a Midas editor command on the editor document and performs necessary focus and
14865      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14866      * @param {String} cmd The Midas command
14867      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14868      */
14869     relayCmd : function(cmd, value){
14870         this.win.focus();
14871         this.execCmd(cmd, value);
14872         this.owner.fireEvent('editorevent', this);
14873         //this.updateToolbar();
14874         this.owner.deferFocus();
14875     },
14876
14877     /**
14878      * Executes a Midas editor command directly on the editor document.
14879      * For visual commands, you should use {@link #relayCmd} instead.
14880      * <b>This should only be called after the editor is initialized.</b>
14881      * @param {String} cmd The Midas command
14882      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14883      */
14884     execCmd : function(cmd, value){
14885         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14886         this.syncValue();
14887     },
14888  
14889  
14890    
14891     /**
14892      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14893      * to insert tRoo.
14894      * @param {String} text | dom node.. 
14895      */
14896     insertAtCursor : function(text)
14897     {
14898         
14899         
14900         
14901         if(!this.activated){
14902             return;
14903         }
14904         /*
14905         if(Roo.isIE){
14906             this.win.focus();
14907             var r = this.doc.selection.createRange();
14908             if(r){
14909                 r.collapse(true);
14910                 r.pasteHTML(text);
14911                 this.syncValue();
14912                 this.deferFocus();
14913             
14914             }
14915             return;
14916         }
14917         */
14918         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14919             this.win.focus();
14920             
14921             
14922             // from jquery ui (MIT licenced)
14923             var range, node;
14924             var win = this.win;
14925             
14926             if (win.getSelection && win.getSelection().getRangeAt) {
14927                 range = win.getSelection().getRangeAt(0);
14928                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14929                 range.insertNode(node);
14930             } else if (win.document.selection && win.document.selection.createRange) {
14931                 // no firefox support
14932                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14933                 win.document.selection.createRange().pasteHTML(txt);
14934             } else {
14935                 // no firefox support
14936                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14937                 this.execCmd('InsertHTML', txt);
14938             } 
14939             
14940             this.syncValue();
14941             
14942             this.deferFocus();
14943         }
14944     },
14945  // private
14946     mozKeyPress : function(e){
14947         if(e.ctrlKey){
14948             var c = e.getCharCode(), cmd;
14949           
14950             if(c > 0){
14951                 c = String.fromCharCode(c).toLowerCase();
14952                 switch(c){
14953                     case 'b':
14954                         cmd = 'bold';
14955                         break;
14956                     case 'i':
14957                         cmd = 'italic';
14958                         break;
14959                     
14960                     case 'u':
14961                         cmd = 'underline';
14962                         break;
14963                     
14964                     case 'v':
14965                         this.cleanUpPaste.defer(100, this);
14966                         return;
14967                         
14968                 }
14969                 if(cmd){
14970                     this.win.focus();
14971                     this.execCmd(cmd);
14972                     this.deferFocus();
14973                     e.preventDefault();
14974                 }
14975                 
14976             }
14977         }
14978     },
14979
14980     // private
14981     fixKeys : function(){ // load time branching for fastest keydown performance
14982         if(Roo.isIE){
14983             return function(e){
14984                 var k = e.getKey(), r;
14985                 if(k == e.TAB){
14986                     e.stopEvent();
14987                     r = this.doc.selection.createRange();
14988                     if(r){
14989                         r.collapse(true);
14990                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14991                         this.deferFocus();
14992                     }
14993                     return;
14994                 }
14995                 
14996                 if(k == e.ENTER){
14997                     r = this.doc.selection.createRange();
14998                     if(r){
14999                         var target = r.parentElement();
15000                         if(!target || target.tagName.toLowerCase() != 'li'){
15001                             e.stopEvent();
15002                             r.pasteHTML('<br />');
15003                             r.collapse(false);
15004                             r.select();
15005                         }
15006                     }
15007                 }
15008                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15009                     this.cleanUpPaste.defer(100, this);
15010                     return;
15011                 }
15012                 
15013                 
15014             };
15015         }else if(Roo.isOpera){
15016             return function(e){
15017                 var k = e.getKey();
15018                 if(k == e.TAB){
15019                     e.stopEvent();
15020                     this.win.focus();
15021                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15022                     this.deferFocus();
15023                 }
15024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15025                     this.cleanUpPaste.defer(100, this);
15026                     return;
15027                 }
15028                 
15029             };
15030         }else if(Roo.isSafari){
15031             return function(e){
15032                 var k = e.getKey();
15033                 
15034                 if(k == e.TAB){
15035                     e.stopEvent();
15036                     this.execCmd('InsertText','\t');
15037                     this.deferFocus();
15038                     return;
15039                 }
15040                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15041                     this.cleanUpPaste.defer(100, this);
15042                     return;
15043                 }
15044                 
15045              };
15046         }
15047     }(),
15048     
15049     getAllAncestors: function()
15050     {
15051         var p = this.getSelectedNode();
15052         var a = [];
15053         if (!p) {
15054             a.push(p); // push blank onto stack..
15055             p = this.getParentElement();
15056         }
15057         
15058         
15059         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15060             a.push(p);
15061             p = p.parentNode;
15062         }
15063         a.push(this.doc.body);
15064         return a;
15065     },
15066     lastSel : false,
15067     lastSelNode : false,
15068     
15069     
15070     getSelection : function() 
15071     {
15072         this.assignDocWin();
15073         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15074     },
15075     
15076     getSelectedNode: function() 
15077     {
15078         // this may only work on Gecko!!!
15079         
15080         // should we cache this!!!!
15081         
15082         
15083         
15084          
15085         var range = this.createRange(this.getSelection()).cloneRange();
15086         
15087         if (Roo.isIE) {
15088             var parent = range.parentElement();
15089             while (true) {
15090                 var testRange = range.duplicate();
15091                 testRange.moveToElementText(parent);
15092                 if (testRange.inRange(range)) {
15093                     break;
15094                 }
15095                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15096                     break;
15097                 }
15098                 parent = parent.parentElement;
15099             }
15100             return parent;
15101         }
15102         
15103         // is ancestor a text element.
15104         var ac =  range.commonAncestorContainer;
15105         if (ac.nodeType == 3) {
15106             ac = ac.parentNode;
15107         }
15108         
15109         var ar = ac.childNodes;
15110          
15111         var nodes = [];
15112         var other_nodes = [];
15113         var has_other_nodes = false;
15114         for (var i=0;i<ar.length;i++) {
15115             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15116                 continue;
15117             }
15118             // fullly contained node.
15119             
15120             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15121                 nodes.push(ar[i]);
15122                 continue;
15123             }
15124             
15125             // probably selected..
15126             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15127                 other_nodes.push(ar[i]);
15128                 continue;
15129             }
15130             // outer..
15131             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15132                 continue;
15133             }
15134             
15135             
15136             has_other_nodes = true;
15137         }
15138         if (!nodes.length && other_nodes.length) {
15139             nodes= other_nodes;
15140         }
15141         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15142             return false;
15143         }
15144         
15145         return nodes[0];
15146     },
15147     createRange: function(sel)
15148     {
15149         // this has strange effects when using with 
15150         // top toolbar - not sure if it's a great idea.
15151         //this.editor.contentWindow.focus();
15152         if (typeof sel != "undefined") {
15153             try {
15154                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15155             } catch(e) {
15156                 return this.doc.createRange();
15157             }
15158         } else {
15159             return this.doc.createRange();
15160         }
15161     },
15162     getParentElement: function()
15163     {
15164         
15165         this.assignDocWin();
15166         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15167         
15168         var range = this.createRange(sel);
15169          
15170         try {
15171             var p = range.commonAncestorContainer;
15172             while (p.nodeType == 3) { // text node
15173                 p = p.parentNode;
15174             }
15175             return p;
15176         } catch (e) {
15177             return null;
15178         }
15179     
15180     },
15181     /***
15182      *
15183      * Range intersection.. the hard stuff...
15184      *  '-1' = before
15185      *  '0' = hits..
15186      *  '1' = after.
15187      *         [ -- selected range --- ]
15188      *   [fail]                        [fail]
15189      *
15190      *    basically..
15191      *      if end is before start or  hits it. fail.
15192      *      if start is after end or hits it fail.
15193      *
15194      *   if either hits (but other is outside. - then it's not 
15195      *   
15196      *    
15197      **/
15198     
15199     
15200     // @see http://www.thismuchiknow.co.uk/?p=64.
15201     rangeIntersectsNode : function(range, node)
15202     {
15203         var nodeRange = node.ownerDocument.createRange();
15204         try {
15205             nodeRange.selectNode(node);
15206         } catch (e) {
15207             nodeRange.selectNodeContents(node);
15208         }
15209     
15210         var rangeStartRange = range.cloneRange();
15211         rangeStartRange.collapse(true);
15212     
15213         var rangeEndRange = range.cloneRange();
15214         rangeEndRange.collapse(false);
15215     
15216         var nodeStartRange = nodeRange.cloneRange();
15217         nodeStartRange.collapse(true);
15218     
15219         var nodeEndRange = nodeRange.cloneRange();
15220         nodeEndRange.collapse(false);
15221     
15222         return rangeStartRange.compareBoundaryPoints(
15223                  Range.START_TO_START, nodeEndRange) == -1 &&
15224                rangeEndRange.compareBoundaryPoints(
15225                  Range.START_TO_START, nodeStartRange) == 1;
15226         
15227          
15228     },
15229     rangeCompareNode : function(range, node)
15230     {
15231         var nodeRange = node.ownerDocument.createRange();
15232         try {
15233             nodeRange.selectNode(node);
15234         } catch (e) {
15235             nodeRange.selectNodeContents(node);
15236         }
15237         
15238         
15239         range.collapse(true);
15240     
15241         nodeRange.collapse(true);
15242      
15243         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15244         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15245          
15246         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15247         
15248         var nodeIsBefore   =  ss == 1;
15249         var nodeIsAfter    = ee == -1;
15250         
15251         if (nodeIsBefore && nodeIsAfter)
15252             return 0; // outer
15253         if (!nodeIsBefore && nodeIsAfter)
15254             return 1; //right trailed.
15255         
15256         if (nodeIsBefore && !nodeIsAfter)
15257             return 2;  // left trailed.
15258         // fully contined.
15259         return 3;
15260     },
15261
15262     // private? - in a new class?
15263     cleanUpPaste :  function()
15264     {
15265         // cleans up the whole document..
15266         Roo.log('cleanuppaste');
15267         
15268         this.cleanUpChildren(this.doc.body);
15269         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15270         if (clean != this.doc.body.innerHTML) {
15271             this.doc.body.innerHTML = clean;
15272         }
15273         
15274     },
15275     
15276     cleanWordChars : function(input) {// change the chars to hex code
15277         var he = Roo.HtmlEditorCore;
15278         
15279         var output = input;
15280         Roo.each(he.swapCodes, function(sw) { 
15281             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15282             
15283             output = output.replace(swapper, sw[1]);
15284         });
15285         
15286         return output;
15287     },
15288     
15289     
15290     cleanUpChildren : function (n)
15291     {
15292         if (!n.childNodes.length) {
15293             return;
15294         }
15295         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15296            this.cleanUpChild(n.childNodes[i]);
15297         }
15298     },
15299     
15300     
15301         
15302     
15303     cleanUpChild : function (node)
15304     {
15305         var ed = this;
15306         //console.log(node);
15307         if (node.nodeName == "#text") {
15308             // clean up silly Windows -- stuff?
15309             return; 
15310         }
15311         if (node.nodeName == "#comment") {
15312             node.parentNode.removeChild(node);
15313             // clean up silly Windows -- stuff?
15314             return; 
15315         }
15316         
15317         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15318             // remove node.
15319             node.parentNode.removeChild(node);
15320             return;
15321             
15322         }
15323         
15324         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15325         
15326         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15327         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15328         
15329         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15330         //    remove_keep_children = true;
15331         //}
15332         
15333         if (remove_keep_children) {
15334             this.cleanUpChildren(node);
15335             // inserts everything just before this node...
15336             while (node.childNodes.length) {
15337                 var cn = node.childNodes[0];
15338                 node.removeChild(cn);
15339                 node.parentNode.insertBefore(cn, node);
15340             }
15341             node.parentNode.removeChild(node);
15342             return;
15343         }
15344         
15345         if (!node.attributes || !node.attributes.length) {
15346             this.cleanUpChildren(node);
15347             return;
15348         }
15349         
15350         function cleanAttr(n,v)
15351         {
15352             
15353             if (v.match(/^\./) || v.match(/^\//)) {
15354                 return;
15355             }
15356             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15357                 return;
15358             }
15359             if (v.match(/^#/)) {
15360                 return;
15361             }
15362 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15363             node.removeAttribute(n);
15364             
15365         }
15366         
15367         function cleanStyle(n,v)
15368         {
15369             if (v.match(/expression/)) { //XSS?? should we even bother..
15370                 node.removeAttribute(n);
15371                 return;
15372             }
15373             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15374             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15375             
15376             
15377             var parts = v.split(/;/);
15378             var clean = [];
15379             
15380             Roo.each(parts, function(p) {
15381                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15382                 if (!p.length) {
15383                     return true;
15384                 }
15385                 var l = p.split(':').shift().replace(/\s+/g,'');
15386                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15387                 
15388                 if ( cblack.indexOf(l) > -1) {
15389 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15390                     //node.removeAttribute(n);
15391                     return true;
15392                 }
15393                 //Roo.log()
15394                 // only allow 'c whitelisted system attributes'
15395                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15396 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15397                     //node.removeAttribute(n);
15398                     return true;
15399                 }
15400                 
15401                 
15402                  
15403                 
15404                 clean.push(p);
15405                 return true;
15406             });
15407             if (clean.length) { 
15408                 node.setAttribute(n, clean.join(';'));
15409             } else {
15410                 node.removeAttribute(n);
15411             }
15412             
15413         }
15414         
15415         
15416         for (var i = node.attributes.length-1; i > -1 ; i--) {
15417             var a = node.attributes[i];
15418             //console.log(a);
15419             
15420             if (a.name.toLowerCase().substr(0,2)=='on')  {
15421                 node.removeAttribute(a.name);
15422                 continue;
15423             }
15424             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15425                 node.removeAttribute(a.name);
15426                 continue;
15427             }
15428             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15429                 cleanAttr(a.name,a.value); // fixme..
15430                 continue;
15431             }
15432             if (a.name == 'style') {
15433                 cleanStyle(a.name,a.value);
15434                 continue;
15435             }
15436             /// clean up MS crap..
15437             // tecnically this should be a list of valid class'es..
15438             
15439             
15440             if (a.name == 'class') {
15441                 if (a.value.match(/^Mso/)) {
15442                     node.className = '';
15443                 }
15444                 
15445                 if (a.value.match(/body/)) {
15446                     node.className = '';
15447                 }
15448                 continue;
15449             }
15450             
15451             // style cleanup!?
15452             // class cleanup?
15453             
15454         }
15455         
15456         
15457         this.cleanUpChildren(node);
15458         
15459         
15460     },
15461     /**
15462      * Clean up MS wordisms...
15463      */
15464     cleanWord : function(node)
15465     {
15466         var _t = this;
15467         var cleanWordChildren = function()
15468         {
15469             if (!node.childNodes.length) {
15470                 return;
15471             }
15472             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15473                _t.cleanWord(node.childNodes[i]);
15474             }
15475         }
15476         
15477         
15478         if (!node) {
15479             this.cleanWord(this.doc.body);
15480             return;
15481         }
15482         if (node.nodeName == "#text") {
15483             // clean up silly Windows -- stuff?
15484             return; 
15485         }
15486         if (node.nodeName == "#comment") {
15487             node.parentNode.removeChild(node);
15488             // clean up silly Windows -- stuff?
15489             return; 
15490         }
15491         
15492         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15493             node.parentNode.removeChild(node);
15494             return;
15495         }
15496         
15497         // remove - but keep children..
15498         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15499             while (node.childNodes.length) {
15500                 var cn = node.childNodes[0];
15501                 node.removeChild(cn);
15502                 node.parentNode.insertBefore(cn, node);
15503             }
15504             node.parentNode.removeChild(node);
15505             cleanWordChildren();
15506             return;
15507         }
15508         // clean styles
15509         if (node.className.length) {
15510             
15511             var cn = node.className.split(/\W+/);
15512             var cna = [];
15513             Roo.each(cn, function(cls) {
15514                 if (cls.match(/Mso[a-zA-Z]+/)) {
15515                     return;
15516                 }
15517                 cna.push(cls);
15518             });
15519             node.className = cna.length ? cna.join(' ') : '';
15520             if (!cna.length) {
15521                 node.removeAttribute("class");
15522             }
15523         }
15524         
15525         if (node.hasAttribute("lang")) {
15526             node.removeAttribute("lang");
15527         }
15528         
15529         if (node.hasAttribute("style")) {
15530             
15531             var styles = node.getAttribute("style").split(";");
15532             var nstyle = [];
15533             Roo.each(styles, function(s) {
15534                 if (!s.match(/:/)) {
15535                     return;
15536                 }
15537                 var kv = s.split(":");
15538                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15539                     return;
15540                 }
15541                 // what ever is left... we allow.
15542                 nstyle.push(s);
15543             });
15544             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15545             if (!nstyle.length) {
15546                 node.removeAttribute('style');
15547             }
15548         }
15549         
15550         cleanWordChildren();
15551         
15552         
15553     },
15554     domToHTML : function(currentElement, depth, nopadtext) {
15555         
15556             depth = depth || 0;
15557             nopadtext = nopadtext || false;
15558         
15559             if (!currentElement) {
15560                 return this.domToHTML(this.doc.body);
15561             }
15562             
15563             //Roo.log(currentElement);
15564             var j;
15565             var allText = false;
15566             var nodeName = currentElement.nodeName;
15567             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15568             
15569             if  (nodeName == '#text') {
15570                 return currentElement.nodeValue;
15571             }
15572             
15573             
15574             var ret = '';
15575             if (nodeName != 'BODY') {
15576                  
15577                 var i = 0;
15578                 // Prints the node tagName, such as <A>, <IMG>, etc
15579                 if (tagName) {
15580                     var attr = [];
15581                     for(i = 0; i < currentElement.attributes.length;i++) {
15582                         // quoting?
15583                         var aname = currentElement.attributes.item(i).name;
15584                         if (!currentElement.attributes.item(i).value.length) {
15585                             continue;
15586                         }
15587                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15588                     }
15589                     
15590                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15591                 } 
15592                 else {
15593                     
15594                     // eack
15595                 }
15596             } else {
15597                 tagName = false;
15598             }
15599             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15600                 return ret;
15601             }
15602             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15603                 nopadtext = true;
15604             }
15605             
15606             
15607             // Traverse the tree
15608             i = 0;
15609             var currentElementChild = currentElement.childNodes.item(i);
15610             var allText = true;
15611             var innerHTML  = '';
15612             lastnode = '';
15613             while (currentElementChild) {
15614                 // Formatting code (indent the tree so it looks nice on the screen)
15615                 var nopad = nopadtext;
15616                 if (lastnode == 'SPAN') {
15617                     nopad  = true;
15618                 }
15619                 // text
15620                 if  (currentElementChild.nodeName == '#text') {
15621                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15622                     if (!nopad && toadd.length > 80) {
15623                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15624                     }
15625                     innerHTML  += toadd;
15626                     
15627                     i++;
15628                     currentElementChild = currentElement.childNodes.item(i);
15629                     lastNode = '';
15630                     continue;
15631                 }
15632                 allText = false;
15633                 
15634                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15635                     
15636                 // Recursively traverse the tree structure of the child node
15637                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15638                 lastnode = currentElementChild.nodeName;
15639                 i++;
15640                 currentElementChild=currentElement.childNodes.item(i);
15641             }
15642             
15643             ret += innerHTML;
15644             
15645             if (!allText) {
15646                     // The remaining code is mostly for formatting the tree
15647                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15648             }
15649             
15650             
15651             if (tagName) {
15652                 ret+= "</"+tagName+">";
15653             }
15654             return ret;
15655             
15656         }
15657     
15658     // hide stuff that is not compatible
15659     /**
15660      * @event blur
15661      * @hide
15662      */
15663     /**
15664      * @event change
15665      * @hide
15666      */
15667     /**
15668      * @event focus
15669      * @hide
15670      */
15671     /**
15672      * @event specialkey
15673      * @hide
15674      */
15675     /**
15676      * @cfg {String} fieldClass @hide
15677      */
15678     /**
15679      * @cfg {String} focusClass @hide
15680      */
15681     /**
15682      * @cfg {String} autoCreate @hide
15683      */
15684     /**
15685      * @cfg {String} inputType @hide
15686      */
15687     /**
15688      * @cfg {String} invalidClass @hide
15689      */
15690     /**
15691      * @cfg {String} invalidText @hide
15692      */
15693     /**
15694      * @cfg {String} msgFx @hide
15695      */
15696     /**
15697      * @cfg {String} validateOnBlur @hide
15698      */
15699 });
15700
15701 Roo.HtmlEditorCore.white = [
15702         'area', 'br', 'img', 'input', 'hr', 'wbr',
15703         
15704        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15705        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15706        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15707        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15708        'table',   'ul',         'xmp', 
15709        
15710        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15711       'thead',   'tr', 
15712      
15713       'dir', 'menu', 'ol', 'ul', 'dl',
15714        
15715       'embed',  'object'
15716 ];
15717
15718
15719 Roo.HtmlEditorCore.black = [
15720     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15721         'applet', // 
15722         'base',   'basefont', 'bgsound', 'blink',  'body', 
15723         'frame',  'frameset', 'head',    'html',   'ilayer', 
15724         'iframe', 'layer',  'link',     'meta',    'object',   
15725         'script', 'style' ,'title',  'xml' // clean later..
15726 ];
15727 Roo.HtmlEditorCore.clean = [
15728     'script', 'style', 'title', 'xml'
15729 ];
15730 Roo.HtmlEditorCore.remove = [
15731     'font'
15732 ];
15733 // attributes..
15734
15735 Roo.HtmlEditorCore.ablack = [
15736     'on'
15737 ];
15738     
15739 Roo.HtmlEditorCore.aclean = [ 
15740     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15741 ];
15742
15743 // protocols..
15744 Roo.HtmlEditorCore.pwhite= [
15745         'http',  'https',  'mailto'
15746 ];
15747
15748 // white listed style attributes.
15749 Roo.HtmlEditorCore.cwhite= [
15750       //  'text-align', /// default is to allow most things..
15751       
15752          
15753 //        'font-size'//??
15754 ];
15755
15756 // black listed style attributes.
15757 Roo.HtmlEditorCore.cblack= [
15758       //  'font-size' -- this can be set by the project 
15759 ];
15760
15761
15762 Roo.HtmlEditorCore.swapCodes   =[ 
15763     [    8211, "--" ], 
15764     [    8212, "--" ], 
15765     [    8216,  "'" ],  
15766     [    8217, "'" ],  
15767     [    8220, '"' ],  
15768     [    8221, '"' ],  
15769     [    8226, "*" ],  
15770     [    8230, "..." ]
15771 ]; 
15772
15773     /*
15774  * - LGPL
15775  *
15776  * HtmlEditor
15777  * 
15778  */
15779
15780 /**
15781  * @class Roo.bootstrap.HtmlEditor
15782  * @extends Roo.bootstrap.TextArea
15783  * Bootstrap HtmlEditor class
15784
15785  * @constructor
15786  * Create a new HtmlEditor
15787  * @param {Object} config The config object
15788  */
15789
15790 Roo.bootstrap.HtmlEditor = function(config){
15791     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15792     if (!this.toolbars) {
15793         this.toolbars = [];
15794     }
15795     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15796     this.addEvents({
15797             /**
15798              * @event initialize
15799              * Fires when the editor is fully initialized (including the iframe)
15800              * @param {HtmlEditor} this
15801              */
15802             initialize: true,
15803             /**
15804              * @event activate
15805              * Fires when the editor is first receives the focus. Any insertion must wait
15806              * until after this event.
15807              * @param {HtmlEditor} this
15808              */
15809             activate: true,
15810              /**
15811              * @event beforesync
15812              * Fires before the textarea is updated with content from the editor iframe. Return false
15813              * to cancel the sync.
15814              * @param {HtmlEditor} this
15815              * @param {String} html
15816              */
15817             beforesync: true,
15818              /**
15819              * @event beforepush
15820              * Fires before the iframe editor is updated with content from the textarea. Return false
15821              * to cancel the push.
15822              * @param {HtmlEditor} this
15823              * @param {String} html
15824              */
15825             beforepush: true,
15826              /**
15827              * @event sync
15828              * Fires when the textarea is updated with content from the editor iframe.
15829              * @param {HtmlEditor} this
15830              * @param {String} html
15831              */
15832             sync: true,
15833              /**
15834              * @event push
15835              * Fires when the iframe editor is updated with content from the textarea.
15836              * @param {HtmlEditor} this
15837              * @param {String} html
15838              */
15839             push: true,
15840              /**
15841              * @event editmodechange
15842              * Fires when the editor switches edit modes
15843              * @param {HtmlEditor} this
15844              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15845              */
15846             editmodechange: true,
15847             /**
15848              * @event editorevent
15849              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15850              * @param {HtmlEditor} this
15851              */
15852             editorevent: true,
15853             /**
15854              * @event firstfocus
15855              * Fires when on first focus - needed by toolbars..
15856              * @param {HtmlEditor} this
15857              */
15858             firstfocus: true,
15859             /**
15860              * @event autosave
15861              * Auto save the htmlEditor value as a file into Events
15862              * @param {HtmlEditor} this
15863              */
15864             autosave: true,
15865             /**
15866              * @event savedpreview
15867              * preview the saved version of htmlEditor
15868              * @param {HtmlEditor} this
15869              */
15870             savedpreview: true
15871         });
15872 };
15873
15874
15875 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15876     
15877     
15878       /**
15879      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15880      */
15881     toolbars : false,
15882    
15883      /**
15884      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15885      *                        Roo.resizable.
15886      */
15887     resizable : false,
15888      /**
15889      * @cfg {Number} height (in pixels)
15890      */   
15891     height: 300,
15892    /**
15893      * @cfg {Number} width (in pixels)
15894      */   
15895     width: false,
15896     
15897     /**
15898      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15899      * 
15900      */
15901     stylesheets: false,
15902     
15903     // id of frame..
15904     frameId: false,
15905     
15906     // private properties
15907     validationEvent : false,
15908     deferHeight: true,
15909     initialized : false,
15910     activated : false,
15911     
15912     onFocus : Roo.emptyFn,
15913     iframePad:3,
15914     hideMode:'offsets',
15915     
15916     
15917     tbContainer : false,
15918     
15919     toolbarContainer :function() {
15920         return this.wrap.select('.x-html-editor-tb',true).first();
15921     },
15922
15923     /**
15924      * Protected method that will not generally be called directly. It
15925      * is called when the editor creates its toolbar. Override this method if you need to
15926      * add custom toolbar buttons.
15927      * @param {HtmlEditor} editor
15928      */
15929     createToolbar : function(){
15930         
15931         Roo.log("create toolbars");
15932         
15933         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15934         this.toolbars[0].render(this.toolbarContainer());
15935         
15936         return;
15937         
15938 //        if (!editor.toolbars || !editor.toolbars.length) {
15939 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15940 //        }
15941 //        
15942 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15943 //            editor.toolbars[i] = Roo.factory(
15944 //                    typeof(editor.toolbars[i]) == 'string' ?
15945 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15946 //                Roo.bootstrap.HtmlEditor);
15947 //            editor.toolbars[i].init(editor);
15948 //        }
15949     },
15950
15951      
15952     // private
15953     onRender : function(ct, position)
15954     {
15955        // Roo.log("Call onRender: " + this.xtype);
15956         var _t = this;
15957         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15958       
15959         this.wrap = this.inputEl().wrap({
15960             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15961         });
15962         
15963         this.editorcore.onRender(ct, position);
15964          
15965         if (this.resizable) {
15966             this.resizeEl = new Roo.Resizable(this.wrap, {
15967                 pinned : true,
15968                 wrap: true,
15969                 dynamic : true,
15970                 minHeight : this.height,
15971                 height: this.height,
15972                 handles : this.resizable,
15973                 width: this.width,
15974                 listeners : {
15975                     resize : function(r, w, h) {
15976                         _t.onResize(w,h); // -something
15977                     }
15978                 }
15979             });
15980             
15981         }
15982         this.createToolbar(this);
15983        
15984         
15985         if(!this.width && this.resizable){
15986             this.setSize(this.wrap.getSize());
15987         }
15988         if (this.resizeEl) {
15989             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15990             // should trigger onReize..
15991         }
15992         
15993     },
15994
15995     // private
15996     onResize : function(w, h)
15997     {
15998         Roo.log('resize: ' +w + ',' + h );
15999         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16000         var ew = false;
16001         var eh = false;
16002         
16003         if(this.inputEl() ){
16004             if(typeof w == 'number'){
16005                 var aw = w - this.wrap.getFrameWidth('lr');
16006                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16007                 ew = aw;
16008             }
16009             if(typeof h == 'number'){
16010                  var tbh = -11;  // fixme it needs to tool bar size!
16011                 for (var i =0; i < this.toolbars.length;i++) {
16012                     // fixme - ask toolbars for heights?
16013                     tbh += this.toolbars[i].el.getHeight();
16014                     //if (this.toolbars[i].footer) {
16015                     //    tbh += this.toolbars[i].footer.el.getHeight();
16016                     //}
16017                 }
16018               
16019                 
16020                 
16021                 
16022                 
16023                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16024                 ah -= 5; // knock a few pixes off for look..
16025                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16026                 var eh = ah;
16027             }
16028         }
16029         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16030         this.editorcore.onResize(ew,eh);
16031         
16032     },
16033
16034     /**
16035      * Toggles the editor between standard and source edit mode.
16036      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16037      */
16038     toggleSourceEdit : function(sourceEditMode)
16039     {
16040         this.editorcore.toggleSourceEdit(sourceEditMode);
16041         
16042         if(this.editorcore.sourceEditMode){
16043             Roo.log('editor - showing textarea');
16044             
16045 //            Roo.log('in');
16046 //            Roo.log(this.syncValue());
16047             this.syncValue();
16048             this.inputEl().removeClass('hide');
16049             this.inputEl().dom.removeAttribute('tabIndex');
16050             this.inputEl().focus();
16051         }else{
16052             Roo.log('editor - hiding textarea');
16053 //            Roo.log('out')
16054 //            Roo.log(this.pushValue()); 
16055             this.pushValue();
16056             
16057             this.inputEl().addClass('hide');
16058             this.inputEl().dom.setAttribute('tabIndex', -1);
16059             //this.deferFocus();
16060         }
16061          
16062         if(this.resizable){
16063             this.setSize(this.wrap.getSize());
16064         }
16065         
16066         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16067     },
16068  
16069     // private (for BoxComponent)
16070     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16071
16072     // private (for BoxComponent)
16073     getResizeEl : function(){
16074         return this.wrap;
16075     },
16076
16077     // private (for BoxComponent)
16078     getPositionEl : function(){
16079         return this.wrap;
16080     },
16081
16082     // private
16083     initEvents : function(){
16084         this.originalValue = this.getValue();
16085     },
16086
16087 //    /**
16088 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16089 //     * @method
16090 //     */
16091 //    markInvalid : Roo.emptyFn,
16092 //    /**
16093 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16094 //     * @method
16095 //     */
16096 //    clearInvalid : Roo.emptyFn,
16097
16098     setValue : function(v){
16099         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16100         this.editorcore.pushValue();
16101     },
16102
16103      
16104     // private
16105     deferFocus : function(){
16106         this.focus.defer(10, this);
16107     },
16108
16109     // doc'ed in Field
16110     focus : function(){
16111         this.editorcore.focus();
16112         
16113     },
16114       
16115
16116     // private
16117     onDestroy : function(){
16118         
16119         
16120         
16121         if(this.rendered){
16122             
16123             for (var i =0; i < this.toolbars.length;i++) {
16124                 // fixme - ask toolbars for heights?
16125                 this.toolbars[i].onDestroy();
16126             }
16127             
16128             this.wrap.dom.innerHTML = '';
16129             this.wrap.remove();
16130         }
16131     },
16132
16133     // private
16134     onFirstFocus : function(){
16135         //Roo.log("onFirstFocus");
16136         this.editorcore.onFirstFocus();
16137          for (var i =0; i < this.toolbars.length;i++) {
16138             this.toolbars[i].onFirstFocus();
16139         }
16140         
16141     },
16142     
16143     // private
16144     syncValue : function()
16145     {   
16146         this.editorcore.syncValue();
16147     },
16148     
16149     pushValue : function()
16150     {   
16151         this.editorcore.pushValue();
16152     }
16153      
16154     
16155     // hide stuff that is not compatible
16156     /**
16157      * @event blur
16158      * @hide
16159      */
16160     /**
16161      * @event change
16162      * @hide
16163      */
16164     /**
16165      * @event focus
16166      * @hide
16167      */
16168     /**
16169      * @event specialkey
16170      * @hide
16171      */
16172     /**
16173      * @cfg {String} fieldClass @hide
16174      */
16175     /**
16176      * @cfg {String} focusClass @hide
16177      */
16178     /**
16179      * @cfg {String} autoCreate @hide
16180      */
16181     /**
16182      * @cfg {String} inputType @hide
16183      */
16184     /**
16185      * @cfg {String} invalidClass @hide
16186      */
16187     /**
16188      * @cfg {String} invalidText @hide
16189      */
16190     /**
16191      * @cfg {String} msgFx @hide
16192      */
16193     /**
16194      * @cfg {String} validateOnBlur @hide
16195      */
16196 });
16197  
16198     
16199    
16200    
16201    
16202       
16203
16204 /**
16205  * @class Roo.bootstrap.HtmlEditorToolbar1
16206  * Basic Toolbar
16207  * 
16208  * Usage:
16209  *
16210  new Roo.bootstrap.HtmlEditor({
16211     ....
16212     toolbars : [
16213         new Roo.bootstrap.HtmlEditorToolbar1({
16214             disable : { fonts: 1 , format: 1, ..., ... , ...],
16215             btns : [ .... ]
16216         })
16217     }
16218      
16219  * 
16220  * @cfg {Object} disable List of elements to disable..
16221  * @cfg {Array} btns List of additional buttons.
16222  * 
16223  * 
16224  * NEEDS Extra CSS? 
16225  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16226  */
16227  
16228 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16229 {
16230     
16231     Roo.apply(this, config);
16232     
16233     // default disabled, based on 'good practice'..
16234     this.disable = this.disable || {};
16235     Roo.applyIf(this.disable, {
16236         fontSize : true,
16237         colors : true,
16238         specialElements : true
16239     });
16240     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16241     
16242     this.editor = config.editor;
16243     this.editorcore = config.editor.editorcore;
16244     
16245     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16246     
16247     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16248     // dont call parent... till later.
16249 }
16250 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16251     
16252     
16253     bar : true,
16254     
16255     editor : false,
16256     editorcore : false,
16257     
16258     
16259     formats : [
16260         "p" ,  
16261         "h1","h2","h3","h4","h5","h6", 
16262         "pre", "code", 
16263         "abbr", "acronym", "address", "cite", "samp", "var",
16264         'div','span'
16265     ],
16266     
16267     onRender : function(ct, position)
16268     {
16269        // Roo.log("Call onRender: " + this.xtype);
16270         
16271        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16272        Roo.log(this.el);
16273        this.el.dom.style.marginBottom = '0';
16274        var _this = this;
16275        var editorcore = this.editorcore;
16276        var editor= this.editor;
16277        
16278        var children = [];
16279        var btn = function(id,cmd , toggle, handler){
16280        
16281             var  event = toggle ? 'toggle' : 'click';
16282        
16283             var a = {
16284                 size : 'sm',
16285                 xtype: 'Button',
16286                 xns: Roo.bootstrap,
16287                 glyphicon : id,
16288                 cmd : id || cmd,
16289                 enableToggle:toggle !== false,
16290                 //html : 'submit'
16291                 pressed : toggle ? false : null,
16292                 listeners : {}
16293             }
16294             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16295                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16296             }
16297             children.push(a);
16298             return a;
16299        }
16300         
16301         var style = {
16302                 xtype: 'Button',
16303                 size : 'sm',
16304                 xns: Roo.bootstrap,
16305                 glyphicon : 'font',
16306                 //html : 'submit'
16307                 menu : {
16308                     xtype: 'Menu',
16309                     xns: Roo.bootstrap,
16310                     items:  []
16311                 }
16312         };
16313         Roo.each(this.formats, function(f) {
16314             style.menu.items.push({
16315                 xtype :'MenuItem',
16316                 xns: Roo.bootstrap,
16317                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16318                 tagname : f,
16319                 listeners : {
16320                     click : function()
16321                     {
16322                         editorcore.insertTag(this.tagname);
16323                         editor.focus();
16324                     }
16325                 }
16326                 
16327             });
16328         });
16329          children.push(style);   
16330             
16331             
16332         btn('bold',false,true);
16333         btn('italic',false,true);
16334         btn('align-left', 'justifyleft',true);
16335         btn('align-center', 'justifycenter',true);
16336         btn('align-right' , 'justifyright',true);
16337         btn('link', false, false, function(btn) {
16338             //Roo.log("create link?");
16339             var url = prompt(this.createLinkText, this.defaultLinkValue);
16340             if(url && url != 'http:/'+'/'){
16341                 this.editorcore.relayCmd('createlink', url);
16342             }
16343         }),
16344         btn('list','insertunorderedlist',true);
16345         btn('pencil', false,true, function(btn){
16346                 Roo.log(this);
16347                 
16348                 this.toggleSourceEdit(btn.pressed);
16349         });
16350         /*
16351         var cog = {
16352                 xtype: 'Button',
16353                 size : 'sm',
16354                 xns: Roo.bootstrap,
16355                 glyphicon : 'cog',
16356                 //html : 'submit'
16357                 menu : {
16358                     xtype: 'Menu',
16359                     xns: Roo.bootstrap,
16360                     items:  []
16361                 }
16362         };
16363         
16364         cog.menu.items.push({
16365             xtype :'MenuItem',
16366             xns: Roo.bootstrap,
16367             html : Clean styles,
16368             tagname : f,
16369             listeners : {
16370                 click : function()
16371                 {
16372                     editorcore.insertTag(this.tagname);
16373                     editor.focus();
16374                 }
16375             }
16376             
16377         });
16378        */
16379         
16380          
16381        this.xtype = 'NavSimplebar';
16382         
16383         for(var i=0;i< children.length;i++) {
16384             
16385             this.buttons.add(this.addxtypeChild(children[i]));
16386             
16387         }
16388         
16389         editor.on('editorevent', this.updateToolbar, this);
16390     },
16391     onBtnClick : function(id)
16392     {
16393        this.editorcore.relayCmd(id);
16394        this.editorcore.focus();
16395     },
16396     
16397     /**
16398      * Protected method that will not generally be called directly. It triggers
16399      * a toolbar update by reading the markup state of the current selection in the editor.
16400      */
16401     updateToolbar: function(){
16402
16403         if(!this.editorcore.activated){
16404             this.editor.onFirstFocus(); // is this neeed?
16405             return;
16406         }
16407
16408         var btns = this.buttons; 
16409         var doc = this.editorcore.doc;
16410         btns.get('bold').setActive(doc.queryCommandState('bold'));
16411         btns.get('italic').setActive(doc.queryCommandState('italic'));
16412         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16413         
16414         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16415         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16416         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16417         
16418         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16419         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16420          /*
16421         
16422         var ans = this.editorcore.getAllAncestors();
16423         if (this.formatCombo) {
16424             
16425             
16426             var store = this.formatCombo.store;
16427             this.formatCombo.setValue("");
16428             for (var i =0; i < ans.length;i++) {
16429                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16430                     // select it..
16431                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16432                     break;
16433                 }
16434             }
16435         }
16436         
16437         
16438         
16439         // hides menus... - so this cant be on a menu...
16440         Roo.bootstrap.MenuMgr.hideAll();
16441         */
16442         Roo.bootstrap.MenuMgr.hideAll();
16443         //this.editorsyncValue();
16444     },
16445     onFirstFocus: function() {
16446         this.buttons.each(function(item){
16447            item.enable();
16448         });
16449     },
16450     toggleSourceEdit : function(sourceEditMode){
16451         
16452           
16453         if(sourceEditMode){
16454             Roo.log("disabling buttons");
16455            this.buttons.each( function(item){
16456                 if(item.cmd != 'pencil'){
16457                     item.disable();
16458                 }
16459             });
16460           
16461         }else{
16462             Roo.log("enabling buttons");
16463             if(this.editorcore.initialized){
16464                 this.buttons.each( function(item){
16465                     item.enable();
16466                 });
16467             }
16468             
16469         }
16470         Roo.log("calling toggole on editor");
16471         // tell the editor that it's been pressed..
16472         this.editor.toggleSourceEdit(sourceEditMode);
16473        
16474     }
16475 });
16476
16477
16478
16479
16480
16481 /**
16482  * @class Roo.bootstrap.Table.AbstractSelectionModel
16483  * @extends Roo.util.Observable
16484  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16485  * implemented by descendant classes.  This class should not be directly instantiated.
16486  * @constructor
16487  */
16488 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16489     this.locked = false;
16490     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16491 };
16492
16493
16494 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16495     /** @ignore Called by the grid automatically. Do not call directly. */
16496     init : function(grid){
16497         this.grid = grid;
16498         this.initEvents();
16499     },
16500
16501     /**
16502      * Locks the selections.
16503      */
16504     lock : function(){
16505         this.locked = true;
16506     },
16507
16508     /**
16509      * Unlocks the selections.
16510      */
16511     unlock : function(){
16512         this.locked = false;
16513     },
16514
16515     /**
16516      * Returns true if the selections are locked.
16517      * @return {Boolean}
16518      */
16519     isLocked : function(){
16520         return this.locked;
16521     }
16522 });
16523 /**
16524  * @class Roo.bootstrap.Table.ColumnModel
16525  * @extends Roo.util.Observable
16526  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16527  * the columns in the table.
16528  
16529  * @constructor
16530  * @param {Object} config An Array of column config objects. See this class's
16531  * config objects for details.
16532 */
16533 Roo.bootstrap.Table.ColumnModel = function(config){
16534         /**
16535      * The config passed into the constructor
16536      */
16537     this.config = config;
16538     this.lookup = {};
16539
16540     // if no id, create one
16541     // if the column does not have a dataIndex mapping,
16542     // map it to the order it is in the config
16543     for(var i = 0, len = config.length; i < len; i++){
16544         var c = config[i];
16545         if(typeof c.dataIndex == "undefined"){
16546             c.dataIndex = i;
16547         }
16548         if(typeof c.renderer == "string"){
16549             c.renderer = Roo.util.Format[c.renderer];
16550         }
16551         if(typeof c.id == "undefined"){
16552             c.id = Roo.id();
16553         }
16554 //        if(c.editor && c.editor.xtype){
16555 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16556 //        }
16557 //        if(c.editor && c.editor.isFormField){
16558 //            c.editor = new Roo.grid.GridEditor(c.editor);
16559 //        }
16560
16561         this.lookup[c.id] = c;
16562     }
16563
16564     /**
16565      * The width of columns which have no width specified (defaults to 100)
16566      * @type Number
16567      */
16568     this.defaultWidth = 100;
16569
16570     /**
16571      * Default sortable of columns which have no sortable specified (defaults to false)
16572      * @type Boolean
16573      */
16574     this.defaultSortable = false;
16575
16576     this.addEvents({
16577         /**
16578              * @event widthchange
16579              * Fires when the width of a column changes.
16580              * @param {ColumnModel} this
16581              * @param {Number} columnIndex The column index
16582              * @param {Number} newWidth The new width
16583              */
16584             "widthchange": true,
16585         /**
16586              * @event headerchange
16587              * Fires when the text of a header changes.
16588              * @param {ColumnModel} this
16589              * @param {Number} columnIndex The column index
16590              * @param {Number} newText The new header text
16591              */
16592             "headerchange": true,
16593         /**
16594              * @event hiddenchange
16595              * Fires when a column is hidden or "unhidden".
16596              * @param {ColumnModel} this
16597              * @param {Number} columnIndex The column index
16598              * @param {Boolean} hidden true if hidden, false otherwise
16599              */
16600             "hiddenchange": true,
16601             /**
16602          * @event columnmoved
16603          * Fires when a column is moved.
16604          * @param {ColumnModel} this
16605          * @param {Number} oldIndex
16606          * @param {Number} newIndex
16607          */
16608         "columnmoved" : true,
16609         /**
16610          * @event columlockchange
16611          * Fires when a column's locked state is changed
16612          * @param {ColumnModel} this
16613          * @param {Number} colIndex
16614          * @param {Boolean} locked true if locked
16615          */
16616         "columnlockchange" : true
16617     });
16618     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16619 };
16620 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16621     /**
16622      * @cfg {String} header The header text to display in the Grid view.
16623      */
16624     /**
16625      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16626      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16627      * specified, the column's index is used as an index into the Record's data Array.
16628      */
16629     /**
16630      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16631      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16632      */
16633     /**
16634      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16635      * Defaults to the value of the {@link #defaultSortable} property.
16636      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16637      */
16638     /**
16639      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16640      */
16641     /**
16642      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16643      */
16644     /**
16645      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16646      */
16647     /**
16648      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16649      */
16650     /**
16651      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16652      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16653      * default renderer uses the raw data value.
16654      */
16655     /**
16656      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16657      */
16658
16659     /**
16660      * Returns the id of the column at the specified index.
16661      * @param {Number} index The column index
16662      * @return {String} the id
16663      */
16664     getColumnId : function(index){
16665         return this.config[index].id;
16666     },
16667
16668     /**
16669      * Returns the column for a specified id.
16670      * @param {String} id The column id
16671      * @return {Object} the column
16672      */
16673     getColumnById : function(id){
16674         return this.lookup[id];
16675     },
16676
16677     
16678     /**
16679      * Returns the column for a specified dataIndex.
16680      * @param {String} dataIndex The column dataIndex
16681      * @return {Object|Boolean} the column or false if not found
16682      */
16683     getColumnByDataIndex: function(dataIndex){
16684         var index = this.findColumnIndex(dataIndex);
16685         return index > -1 ? this.config[index] : false;
16686     },
16687     
16688     /**
16689      * Returns the index for a specified column id.
16690      * @param {String} id The column id
16691      * @return {Number} the index, or -1 if not found
16692      */
16693     getIndexById : function(id){
16694         for(var i = 0, len = this.config.length; i < len; i++){
16695             if(this.config[i].id == id){
16696                 return i;
16697             }
16698         }
16699         return -1;
16700     },
16701     
16702     /**
16703      * Returns the index for a specified column dataIndex.
16704      * @param {String} dataIndex The column dataIndex
16705      * @return {Number} the index, or -1 if not found
16706      */
16707     
16708     findColumnIndex : function(dataIndex){
16709         for(var i = 0, len = this.config.length; i < len; i++){
16710             if(this.config[i].dataIndex == dataIndex){
16711                 return i;
16712             }
16713         }
16714         return -1;
16715     },
16716     
16717     
16718     moveColumn : function(oldIndex, newIndex){
16719         var c = this.config[oldIndex];
16720         this.config.splice(oldIndex, 1);
16721         this.config.splice(newIndex, 0, c);
16722         this.dataMap = null;
16723         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16724     },
16725
16726     isLocked : function(colIndex){
16727         return this.config[colIndex].locked === true;
16728     },
16729
16730     setLocked : function(colIndex, value, suppressEvent){
16731         if(this.isLocked(colIndex) == value){
16732             return;
16733         }
16734         this.config[colIndex].locked = value;
16735         if(!suppressEvent){
16736             this.fireEvent("columnlockchange", this, colIndex, value);
16737         }
16738     },
16739
16740     getTotalLockedWidth : function(){
16741         var totalWidth = 0;
16742         for(var i = 0; i < this.config.length; i++){
16743             if(this.isLocked(i) && !this.isHidden(i)){
16744                 this.totalWidth += this.getColumnWidth(i);
16745             }
16746         }
16747         return totalWidth;
16748     },
16749
16750     getLockedCount : function(){
16751         for(var i = 0, len = this.config.length; i < len; i++){
16752             if(!this.isLocked(i)){
16753                 return i;
16754             }
16755         }
16756     },
16757
16758     /**
16759      * Returns the number of columns.
16760      * @return {Number}
16761      */
16762     getColumnCount : function(visibleOnly){
16763         if(visibleOnly === true){
16764             var c = 0;
16765             for(var i = 0, len = this.config.length; i < len; i++){
16766                 if(!this.isHidden(i)){
16767                     c++;
16768                 }
16769             }
16770             return c;
16771         }
16772         return this.config.length;
16773     },
16774
16775     /**
16776      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16777      * @param {Function} fn
16778      * @param {Object} scope (optional)
16779      * @return {Array} result
16780      */
16781     getColumnsBy : function(fn, scope){
16782         var r = [];
16783         for(var i = 0, len = this.config.length; i < len; i++){
16784             var c = this.config[i];
16785             if(fn.call(scope||this, c, i) === true){
16786                 r[r.length] = c;
16787             }
16788         }
16789         return r;
16790     },
16791
16792     /**
16793      * Returns true if the specified column is sortable.
16794      * @param {Number} col The column index
16795      * @return {Boolean}
16796      */
16797     isSortable : function(col){
16798         if(typeof this.config[col].sortable == "undefined"){
16799             return this.defaultSortable;
16800         }
16801         return this.config[col].sortable;
16802     },
16803
16804     /**
16805      * Returns the rendering (formatting) function defined for the column.
16806      * @param {Number} col The column index.
16807      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16808      */
16809     getRenderer : function(col){
16810         if(!this.config[col].renderer){
16811             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16812         }
16813         return this.config[col].renderer;
16814     },
16815
16816     /**
16817      * Sets the rendering (formatting) function for a column.
16818      * @param {Number} col The column index
16819      * @param {Function} fn The function to use to process the cell's raw data
16820      * to return HTML markup for the grid view. The render function is called with
16821      * the following parameters:<ul>
16822      * <li>Data value.</li>
16823      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16824      * <li>css A CSS style string to apply to the table cell.</li>
16825      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16826      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16827      * <li>Row index</li>
16828      * <li>Column index</li>
16829      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16830      */
16831     setRenderer : function(col, fn){
16832         this.config[col].renderer = fn;
16833     },
16834
16835     /**
16836      * Returns the width for the specified column.
16837      * @param {Number} col The column index
16838      * @return {Number}
16839      */
16840     getColumnWidth : function(col){
16841         return this.config[col].width * 1 || this.defaultWidth;
16842     },
16843
16844     /**
16845      * Sets the width for a column.
16846      * @param {Number} col The column index
16847      * @param {Number} width The new width
16848      */
16849     setColumnWidth : function(col, width, suppressEvent){
16850         this.config[col].width = width;
16851         this.totalWidth = null;
16852         if(!suppressEvent){
16853              this.fireEvent("widthchange", this, col, width);
16854         }
16855     },
16856
16857     /**
16858      * Returns the total width of all columns.
16859      * @param {Boolean} includeHidden True to include hidden column widths
16860      * @return {Number}
16861      */
16862     getTotalWidth : function(includeHidden){
16863         if(!this.totalWidth){
16864             this.totalWidth = 0;
16865             for(var i = 0, len = this.config.length; i < len; i++){
16866                 if(includeHidden || !this.isHidden(i)){
16867                     this.totalWidth += this.getColumnWidth(i);
16868                 }
16869             }
16870         }
16871         return this.totalWidth;
16872     },
16873
16874     /**
16875      * Returns the header for the specified column.
16876      * @param {Number} col The column index
16877      * @return {String}
16878      */
16879     getColumnHeader : function(col){
16880         return this.config[col].header;
16881     },
16882
16883     /**
16884      * Sets the header for a column.
16885      * @param {Number} col The column index
16886      * @param {String} header The new header
16887      */
16888     setColumnHeader : function(col, header){
16889         this.config[col].header = header;
16890         this.fireEvent("headerchange", this, col, header);
16891     },
16892
16893     /**
16894      * Returns the tooltip for the specified column.
16895      * @param {Number} col The column index
16896      * @return {String}
16897      */
16898     getColumnTooltip : function(col){
16899             return this.config[col].tooltip;
16900     },
16901     /**
16902      * Sets the tooltip for a column.
16903      * @param {Number} col The column index
16904      * @param {String} tooltip The new tooltip
16905      */
16906     setColumnTooltip : function(col, tooltip){
16907             this.config[col].tooltip = tooltip;
16908     },
16909
16910     /**
16911      * Returns the dataIndex for the specified column.
16912      * @param {Number} col The column index
16913      * @return {Number}
16914      */
16915     getDataIndex : function(col){
16916         return this.config[col].dataIndex;
16917     },
16918
16919     /**
16920      * Sets the dataIndex for a column.
16921      * @param {Number} col The column index
16922      * @param {Number} dataIndex The new dataIndex
16923      */
16924     setDataIndex : function(col, dataIndex){
16925         this.config[col].dataIndex = dataIndex;
16926     },
16927
16928     
16929     
16930     /**
16931      * Returns true if the cell is editable.
16932      * @param {Number} colIndex The column index
16933      * @param {Number} rowIndex The row index
16934      * @return {Boolean}
16935      */
16936     isCellEditable : function(colIndex, rowIndex){
16937         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16938     },
16939
16940     /**
16941      * Returns the editor defined for the cell/column.
16942      * return false or null to disable editing.
16943      * @param {Number} colIndex The column index
16944      * @param {Number} rowIndex The row index
16945      * @return {Object}
16946      */
16947     getCellEditor : function(colIndex, rowIndex){
16948         return this.config[colIndex].editor;
16949     },
16950
16951     /**
16952      * Sets if a column is editable.
16953      * @param {Number} col The column index
16954      * @param {Boolean} editable True if the column is editable
16955      */
16956     setEditable : function(col, editable){
16957         this.config[col].editable = editable;
16958     },
16959
16960
16961     /**
16962      * Returns true if the column is hidden.
16963      * @param {Number} colIndex The column index
16964      * @return {Boolean}
16965      */
16966     isHidden : function(colIndex){
16967         return this.config[colIndex].hidden;
16968     },
16969
16970
16971     /**
16972      * Returns true if the column width cannot be changed
16973      */
16974     isFixed : function(colIndex){
16975         return this.config[colIndex].fixed;
16976     },
16977
16978     /**
16979      * Returns true if the column can be resized
16980      * @return {Boolean}
16981      */
16982     isResizable : function(colIndex){
16983         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16984     },
16985     /**
16986      * Sets if a column is hidden.
16987      * @param {Number} colIndex The column index
16988      * @param {Boolean} hidden True if the column is hidden
16989      */
16990     setHidden : function(colIndex, hidden){
16991         this.config[colIndex].hidden = hidden;
16992         this.totalWidth = null;
16993         this.fireEvent("hiddenchange", this, colIndex, hidden);
16994     },
16995
16996     /**
16997      * Sets the editor for a column.
16998      * @param {Number} col The column index
16999      * @param {Object} editor The editor object
17000      */
17001     setEditor : function(col, editor){
17002         this.config[col].editor = editor;
17003     }
17004 });
17005
17006 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17007         if(typeof value == "string" && value.length < 1){
17008             return "&#160;";
17009         }
17010         return value;
17011 };
17012
17013 // Alias for backwards compatibility
17014 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17015
17016 /**
17017  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17018  * @class Roo.bootstrap.Table.RowSelectionModel
17019  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17020  * It supports multiple selections and keyboard selection/navigation. 
17021  * @constructor
17022  * @param {Object} config
17023  */
17024
17025 Roo.bootstrap.Table.RowSelectionModel = function(config){
17026     Roo.apply(this, config);
17027     this.selections = new Roo.util.MixedCollection(false, function(o){
17028         return o.id;
17029     });
17030
17031     this.last = false;
17032     this.lastActive = false;
17033
17034     this.addEvents({
17035         /**
17036              * @event selectionchange
17037              * Fires when the selection changes
17038              * @param {SelectionModel} this
17039              */
17040             "selectionchange" : true,
17041         /**
17042              * @event afterselectionchange
17043              * Fires after the selection changes (eg. by key press or clicking)
17044              * @param {SelectionModel} this
17045              */
17046             "afterselectionchange" : true,
17047         /**
17048              * @event beforerowselect
17049              * Fires when a row is selected being selected, return false to cancel.
17050              * @param {SelectionModel} this
17051              * @param {Number} rowIndex The selected index
17052              * @param {Boolean} keepExisting False if other selections will be cleared
17053              */
17054             "beforerowselect" : true,
17055         /**
17056              * @event rowselect
17057              * Fires when a row is selected.
17058              * @param {SelectionModel} this
17059              * @param {Number} rowIndex The selected index
17060              * @param {Roo.data.Record} r The record
17061              */
17062             "rowselect" : true,
17063         /**
17064              * @event rowdeselect
17065              * Fires when a row is deselected.
17066              * @param {SelectionModel} this
17067              * @param {Number} rowIndex The selected index
17068              */
17069         "rowdeselect" : true
17070     });
17071     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17072     this.locked = false;
17073 };
17074
17075 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17076     /**
17077      * @cfg {Boolean} singleSelect
17078      * True to allow selection of only one row at a time (defaults to false)
17079      */
17080     singleSelect : false,
17081
17082     // private
17083     initEvents : function(){
17084
17085         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17086             this.grid.on("mousedown", this.handleMouseDown, this);
17087         }else{ // allow click to work like normal
17088             this.grid.on("rowclick", this.handleDragableRowClick, this);
17089         }
17090
17091         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17092             "up" : function(e){
17093                 if(!e.shiftKey){
17094                     this.selectPrevious(e.shiftKey);
17095                 }else if(this.last !== false && this.lastActive !== false){
17096                     var last = this.last;
17097                     this.selectRange(this.last,  this.lastActive-1);
17098                     this.grid.getView().focusRow(this.lastActive);
17099                     if(last !== false){
17100                         this.last = last;
17101                     }
17102                 }else{
17103                     this.selectFirstRow();
17104                 }
17105                 this.fireEvent("afterselectionchange", this);
17106             },
17107             "down" : function(e){
17108                 if(!e.shiftKey){
17109                     this.selectNext(e.shiftKey);
17110                 }else if(this.last !== false && this.lastActive !== false){
17111                     var last = this.last;
17112                     this.selectRange(this.last,  this.lastActive+1);
17113                     this.grid.getView().focusRow(this.lastActive);
17114                     if(last !== false){
17115                         this.last = last;
17116                     }
17117                 }else{
17118                     this.selectFirstRow();
17119                 }
17120                 this.fireEvent("afterselectionchange", this);
17121             },
17122             scope: this
17123         });
17124
17125         var view = this.grid.view;
17126         view.on("refresh", this.onRefresh, this);
17127         view.on("rowupdated", this.onRowUpdated, this);
17128         view.on("rowremoved", this.onRemove, this);
17129     },
17130
17131     // private
17132     onRefresh : function(){
17133         var ds = this.grid.dataSource, i, v = this.grid.view;
17134         var s = this.selections;
17135         s.each(function(r){
17136             if((i = ds.indexOfId(r.id)) != -1){
17137                 v.onRowSelect(i);
17138             }else{
17139                 s.remove(r);
17140             }
17141         });
17142     },
17143
17144     // private
17145     onRemove : function(v, index, r){
17146         this.selections.remove(r);
17147     },
17148
17149     // private
17150     onRowUpdated : function(v, index, r){
17151         if(this.isSelected(r)){
17152             v.onRowSelect(index);
17153         }
17154     },
17155
17156     /**
17157      * Select records.
17158      * @param {Array} records The records to select
17159      * @param {Boolean} keepExisting (optional) True to keep existing selections
17160      */
17161     selectRecords : function(records, keepExisting){
17162         if(!keepExisting){
17163             this.clearSelections();
17164         }
17165         var ds = this.grid.dataSource;
17166         for(var i = 0, len = records.length; i < len; i++){
17167             this.selectRow(ds.indexOf(records[i]), true);
17168         }
17169     },
17170
17171     /**
17172      * Gets the number of selected rows.
17173      * @return {Number}
17174      */
17175     getCount : function(){
17176         return this.selections.length;
17177     },
17178
17179     /**
17180      * Selects the first row in the grid.
17181      */
17182     selectFirstRow : function(){
17183         this.selectRow(0);
17184     },
17185
17186     /**
17187      * Select the last row.
17188      * @param {Boolean} keepExisting (optional) True to keep existing selections
17189      */
17190     selectLastRow : function(keepExisting){
17191         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17192     },
17193
17194     /**
17195      * Selects the row immediately following the last selected row.
17196      * @param {Boolean} keepExisting (optional) True to keep existing selections
17197      */
17198     selectNext : function(keepExisting){
17199         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17200             this.selectRow(this.last+1, keepExisting);
17201             this.grid.getView().focusRow(this.last);
17202         }
17203     },
17204
17205     /**
17206      * Selects the row that precedes the last selected row.
17207      * @param {Boolean} keepExisting (optional) True to keep existing selections
17208      */
17209     selectPrevious : function(keepExisting){
17210         if(this.last){
17211             this.selectRow(this.last-1, keepExisting);
17212             this.grid.getView().focusRow(this.last);
17213         }
17214     },
17215
17216     /**
17217      * Returns the selected records
17218      * @return {Array} Array of selected records
17219      */
17220     getSelections : function(){
17221         return [].concat(this.selections.items);
17222     },
17223
17224     /**
17225      * Returns the first selected record.
17226      * @return {Record}
17227      */
17228     getSelected : function(){
17229         return this.selections.itemAt(0);
17230     },
17231
17232
17233     /**
17234      * Clears all selections.
17235      */
17236     clearSelections : function(fast){
17237         if(this.locked) return;
17238         if(fast !== true){
17239             var ds = this.grid.dataSource;
17240             var s = this.selections;
17241             s.each(function(r){
17242                 this.deselectRow(ds.indexOfId(r.id));
17243             }, this);
17244             s.clear();
17245         }else{
17246             this.selections.clear();
17247         }
17248         this.last = false;
17249     },
17250
17251
17252     /**
17253      * Selects all rows.
17254      */
17255     selectAll : function(){
17256         if(this.locked) return;
17257         this.selections.clear();
17258         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17259             this.selectRow(i, true);
17260         }
17261     },
17262
17263     /**
17264      * Returns True if there is a selection.
17265      * @return {Boolean}
17266      */
17267     hasSelection : function(){
17268         return this.selections.length > 0;
17269     },
17270
17271     /**
17272      * Returns True if the specified row is selected.
17273      * @param {Number/Record} record The record or index of the record to check
17274      * @return {Boolean}
17275      */
17276     isSelected : function(index){
17277         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17278         return (r && this.selections.key(r.id) ? true : false);
17279     },
17280
17281     /**
17282      * Returns True if the specified record id is selected.
17283      * @param {String} id The id of record to check
17284      * @return {Boolean}
17285      */
17286     isIdSelected : function(id){
17287         return (this.selections.key(id) ? true : false);
17288     },
17289
17290     // private
17291     handleMouseDown : function(e, t){
17292         var view = this.grid.getView(), rowIndex;
17293         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17294             return;
17295         };
17296         if(e.shiftKey && this.last !== false){
17297             var last = this.last;
17298             this.selectRange(last, rowIndex, e.ctrlKey);
17299             this.last = last; // reset the last
17300             view.focusRow(rowIndex);
17301         }else{
17302             var isSelected = this.isSelected(rowIndex);
17303             if(e.button !== 0 && isSelected){
17304                 view.focusRow(rowIndex);
17305             }else if(e.ctrlKey && isSelected){
17306                 this.deselectRow(rowIndex);
17307             }else if(!isSelected){
17308                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17309                 view.focusRow(rowIndex);
17310             }
17311         }
17312         this.fireEvent("afterselectionchange", this);
17313     },
17314     // private
17315     handleDragableRowClick :  function(grid, rowIndex, e) 
17316     {
17317         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17318             this.selectRow(rowIndex, false);
17319             grid.view.focusRow(rowIndex);
17320              this.fireEvent("afterselectionchange", this);
17321         }
17322     },
17323     
17324     /**
17325      * Selects multiple rows.
17326      * @param {Array} rows Array of the indexes of the row to select
17327      * @param {Boolean} keepExisting (optional) True to keep existing selections
17328      */
17329     selectRows : function(rows, keepExisting){
17330         if(!keepExisting){
17331             this.clearSelections();
17332         }
17333         for(var i = 0, len = rows.length; i < len; i++){
17334             this.selectRow(rows[i], true);
17335         }
17336     },
17337
17338     /**
17339      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17340      * @param {Number} startRow The index of the first row in the range
17341      * @param {Number} endRow The index of the last row in the range
17342      * @param {Boolean} keepExisting (optional) True to retain existing selections
17343      */
17344     selectRange : function(startRow, endRow, keepExisting){
17345         if(this.locked) return;
17346         if(!keepExisting){
17347             this.clearSelections();
17348         }
17349         if(startRow <= endRow){
17350             for(var i = startRow; i <= endRow; i++){
17351                 this.selectRow(i, true);
17352             }
17353         }else{
17354             for(var i = startRow; i >= endRow; i--){
17355                 this.selectRow(i, true);
17356             }
17357         }
17358     },
17359
17360     /**
17361      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17362      * @param {Number} startRow The index of the first row in the range
17363      * @param {Number} endRow The index of the last row in the range
17364      */
17365     deselectRange : function(startRow, endRow, preventViewNotify){
17366         if(this.locked) return;
17367         for(var i = startRow; i <= endRow; i++){
17368             this.deselectRow(i, preventViewNotify);
17369         }
17370     },
17371
17372     /**
17373      * Selects a row.
17374      * @param {Number} row The index of the row to select
17375      * @param {Boolean} keepExisting (optional) True to keep existing selections
17376      */
17377     selectRow : function(index, keepExisting, preventViewNotify){
17378         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17379         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17380             if(!keepExisting || this.singleSelect){
17381                 this.clearSelections();
17382             }
17383             var r = this.grid.dataSource.getAt(index);
17384             this.selections.add(r);
17385             this.last = this.lastActive = index;
17386             if(!preventViewNotify){
17387                 this.grid.getView().onRowSelect(index);
17388             }
17389             this.fireEvent("rowselect", this, index, r);
17390             this.fireEvent("selectionchange", this);
17391         }
17392     },
17393
17394     /**
17395      * Deselects a row.
17396      * @param {Number} row The index of the row to deselect
17397      */
17398     deselectRow : function(index, preventViewNotify){
17399         if(this.locked) return;
17400         if(this.last == index){
17401             this.last = false;
17402         }
17403         if(this.lastActive == index){
17404             this.lastActive = false;
17405         }
17406         var r = this.grid.dataSource.getAt(index);
17407         this.selections.remove(r);
17408         if(!preventViewNotify){
17409             this.grid.getView().onRowDeselect(index);
17410         }
17411         this.fireEvent("rowdeselect", this, index);
17412         this.fireEvent("selectionchange", this);
17413     },
17414
17415     // private
17416     restoreLast : function(){
17417         if(this._last){
17418             this.last = this._last;
17419         }
17420     },
17421
17422     // private
17423     acceptsNav : function(row, col, cm){
17424         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17425     },
17426
17427     // private
17428     onEditorKey : function(field, e){
17429         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17430         if(k == e.TAB){
17431             e.stopEvent();
17432             ed.completeEdit();
17433             if(e.shiftKey){
17434                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17435             }else{
17436                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17437             }
17438         }else if(k == e.ENTER && !e.ctrlKey){
17439             e.stopEvent();
17440             ed.completeEdit();
17441             if(e.shiftKey){
17442                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17443             }else{
17444                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17445             }
17446         }else if(k == e.ESC){
17447             ed.cancelEdit();
17448         }
17449         if(newCell){
17450             g.startEditing(newCell[0], newCell[1]);
17451         }
17452     }
17453 });/*
17454  * - LGPL
17455  *
17456  * element
17457  * 
17458  */
17459
17460 /**
17461  * @class Roo.bootstrap.MessageBar
17462  * @extends Roo.bootstrap.Component
17463  * Bootstrap MessageBar class
17464  * @cfg {String} html contents of the MessageBar
17465  * @cfg {String} weight (info | success | warning | danger) default info
17466  * @cfg {String} beforeClass insert the bar before the given class
17467  * @cfg {Boolean} closable (true | false) default false
17468  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17469  * 
17470  * @constructor
17471  * Create a new Element
17472  * @param {Object} config The config object
17473  */
17474
17475 Roo.bootstrap.MessageBar = function(config){
17476     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17477 };
17478
17479 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17480     
17481     html: '',
17482     weight: 'info',
17483     closable: false,
17484     fixed: false,
17485     beforeClass: 'bootstrap-sticky-wrap',
17486     
17487     getAutoCreate : function(){
17488         
17489         var cfg = {
17490             tag: 'div',
17491             cls: 'alert alert-dismissable alert-' + this.weight,
17492             cn: [
17493                 {
17494                     tag: 'span',
17495                     cls: 'message',
17496                     html: this.html || ''
17497                 }
17498             ]
17499         }
17500         
17501         if(this.fixed){
17502             cfg.cls += ' alert-messages-fixed';
17503         }
17504         
17505         if(this.closable){
17506             cfg.cn.push({
17507                 tag: 'button',
17508                 cls: 'close',
17509                 html: 'x'
17510             });
17511         }
17512         
17513         return cfg;
17514     },
17515     
17516     onRender : function(ct, position)
17517     {
17518         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17519         
17520         if(!this.el){
17521             var cfg = Roo.apply({},  this.getAutoCreate());
17522             cfg.id = Roo.id();
17523             
17524             if (this.cls) {
17525                 cfg.cls += ' ' + this.cls;
17526             }
17527             if (this.style) {
17528                 cfg.style = this.style;
17529             }
17530             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17531             
17532             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17533         }
17534         
17535         this.el.select('>button.close').on('click', this.hide, this);
17536         
17537     },
17538     
17539     show : function()
17540     {
17541         if (!this.rendered) {
17542             this.render();
17543         }
17544         
17545         this.el.show();
17546         
17547         this.fireEvent('show', this);
17548         
17549     },
17550     
17551     hide : function()
17552     {
17553         if (!this.rendered) {
17554             this.render();
17555         }
17556         
17557         this.el.hide();
17558         
17559         this.fireEvent('hide', this);
17560     },
17561     
17562     update : function()
17563     {
17564 //        var e = this.el.dom.firstChild;
17565 //        
17566 //        if(this.closable){
17567 //            e = e.nextSibling;
17568 //        }
17569 //        
17570 //        e.data = this.html || '';
17571
17572         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17573     }
17574    
17575 });
17576
17577  
17578
17579