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  * @cfg {Boolean} loadMask (true|false) default false
4041  *
4042  
4043  
4044  * 
4045  * @constructor
4046  * Create a new Table
4047  * @param {Object} config The config object
4048  */
4049
4050 Roo.bootstrap.Table = function(config){
4051     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4052     
4053     if (this.sm) {
4054         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4055         this.sm = this.selModel;
4056         this.sm.xmodule = this.xmodule || false;
4057     }
4058     if (this.cm && typeof(this.cm.config) == 'undefined') {
4059         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4060         this.cm = this.colModel;
4061         this.cm.xmodule = this.xmodule || false;
4062     }
4063     if (this.store) {
4064         this.store= Roo.factory(this.store, Roo.data);
4065         this.ds = this.store;
4066         this.ds.xmodule = this.xmodule || false;
4067          
4068     }
4069 };
4070
4071 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4072     
4073     cls: false,
4074     align: false,
4075     bgcolor: false,
4076     border: false,
4077     cellpadding: false,
4078     cellspacing: false,
4079     frame: false,
4080     rules: false,
4081     sortable: false,
4082     summary: false,
4083     width: false,
4084     striped : false,
4085     bordered: false,
4086     hover:  false,
4087     condensed : false,
4088     responsive : false,
4089     sm : false,
4090     cm : false,
4091     store : false,
4092     loadMask : false,
4093     
4094     getAutoCreate : function(){
4095         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4096         
4097         cfg = {
4098             tag: 'table',
4099             cls : 'table',
4100             cn : []
4101         }
4102             
4103         if (this.striped) {
4104             cfg.cls += ' table-striped';
4105         }
4106         if (this.hover) {
4107             cfg.cls += ' table-hover';
4108         }
4109         if (this.bordered) {
4110             cfg.cls += ' table-bordered';
4111         }
4112         if (this.condensed) {
4113             cfg.cls += ' table-condensed';
4114         }
4115         if (this.responsive) {
4116             cfg.cls += ' table-responsive';
4117         }
4118         
4119           
4120         
4121         
4122         if (this.cls) {
4123             cfg.cls+=  ' ' +this.cls;
4124         }
4125         
4126         // this lot should be simplifed...
4127         
4128         if (this.align) {
4129             cfg.align=this.align;
4130         }
4131         if (this.bgcolor) {
4132             cfg.bgcolor=this.bgcolor;
4133         }
4134         if (this.border) {
4135             cfg.border=this.border;
4136         }
4137         if (this.cellpadding) {
4138             cfg.cellpadding=this.cellpadding;
4139         }
4140         if (this.cellspacing) {
4141             cfg.cellspacing=this.cellspacing;
4142         }
4143         if (this.frame) {
4144             cfg.frame=this.frame;
4145         }
4146         if (this.rules) {
4147             cfg.rules=this.rules;
4148         }
4149         if (this.sortable) {
4150             cfg.sortable=this.sortable;
4151         }
4152         if (this.summary) {
4153             cfg.summary=this.summary;
4154         }
4155         if (this.width) {
4156             cfg.width=this.width;
4157         }
4158         
4159         if(this.store || this.cm){
4160             cfg.cn.push(this.renderHeader());
4161             cfg.cn.push(this.renderBody());
4162             cfg.cn.push(this.renderFooter());
4163             
4164             cfg.cls+=  ' TableGrid';
4165         }
4166         
4167         return cfg;
4168     },
4169 //    
4170 //    initTableGrid : function()
4171 //    {
4172 //        var cfg = {};
4173 //        
4174 //        var header = {
4175 //            tag: 'thead',
4176 //            cn : []
4177 //        };
4178 //        
4179 //        var cm = this.cm;
4180 //        
4181 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4182 //            header.cn.push({
4183 //                tag: 'th',
4184 //                html: cm.getColumnHeader(i)
4185 //            })
4186 //        }
4187 //        
4188 //        cfg.push(header);
4189 //        
4190 //        return cfg;
4191 //        
4192 //        
4193 //    },
4194     
4195     initEvents : function()
4196     {   
4197         if(!this.store || !this.cm){
4198             return;
4199         }
4200         
4201         Roo.log('initEvents with ds!!!!');
4202         
4203         var _this = this;
4204         
4205         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4206             e.on('click', _this.sort, _this);
4207         });
4208 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4209 //        this.maskEl.enableDisplayMode("block");
4210 //        this.maskEl.show();
4211         
4212         this.parent().el.setStyle('position', 'relative');
4213         
4214         var mark = {
4215             tag: "div",
4216             cls:"x-dlg-mask",
4217             style: "text-align:center",
4218             cn: [
4219                 {
4220                     tag: "div",
4221                     style: "background-color:white;width:50%;",
4222                     cn: [
4223                         {
4224                             tag: "img",
4225                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
4226                         },
4227                         {
4228                             tag: "span",
4229                             html: "Loading"
4230                         }
4231                         
4232                     ]
4233                 }
4234             ]
4235         }
4236         this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
4237         
4238         var size = this.parent().el.getSize();
4239         
4240         this.maskEl.setSize(size.width, size.height);
4241         this.maskEl.enableDisplayMode("block");
4242         
4243         if(!this.loadMask){
4244             this.maskEl.hide();
4245         }
4246         
4247         this.store.on('load', this.onLoad, this);
4248         this.store.on('beforeload', this.onBeforeLoad, this);
4249         
4250         this.store.load();
4251         
4252         
4253         
4254     },
4255     
4256     sort : function(e,el)
4257     {
4258         var col = Roo.get(el)
4259         
4260         if(!col.hasClass('sortable')){
4261             return;
4262         }
4263         
4264         var sort = col.attr('sort');
4265         var dir = 'ASC';
4266         
4267         if(col.hasClass('glyphicon-arrow-up')){
4268             dir = 'DESC';
4269         }
4270         
4271         this.store.sortInfo = {field : sort, direction : dir};
4272         
4273         this.store.load();
4274     },
4275     
4276     renderHeader : function()
4277     {
4278         var header = {
4279             tag: 'thead',
4280             cn : []
4281         };
4282         
4283         var cm = this.cm;
4284         
4285         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4286             
4287             var config = cm.config[i];
4288             
4289             var c = {
4290                 tag: 'th',
4291                 html: cm.getColumnHeader(i)
4292             };
4293             
4294             if(typeof(config.dataIndex) != 'undefined'){
4295                 c.sort = config.dataIndex;
4296             }
4297             
4298             if(typeof(config.sortable) != 'undefined' && config.sortable){
4299                 c.cls = 'sortable';
4300             }
4301             
4302             if(typeof(config.width) != 'undefined'){
4303                 c.style = 'width:' + config.width + 'px';
4304             }
4305             
4306             header.cn.push(c)
4307         }
4308         
4309         return header;
4310     },
4311     
4312     renderBody : function()
4313     {
4314         var body = {
4315             tag: 'tbody',
4316             cn : []
4317         };
4318         
4319         return body;
4320     },
4321     
4322     renderFooter : function()
4323     {
4324         var footer = {
4325             tag: 'tfoot',
4326             cn : []
4327         };
4328         
4329         return footer;
4330     },
4331     
4332     onLoad : function()
4333     {
4334         Roo.log('ds onload');
4335         
4336         var _this = this;
4337         var cm = this.cm;
4338         
4339         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4340             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4341             
4342             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4343                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4344             }
4345             
4346             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4347                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4348             }
4349         });
4350         
4351         var tbody = this.el.select('tbody', true).first();
4352         
4353         var renders = [];
4354         
4355         if(this.store.getCount() > 0){
4356             this.store.data.each(function(d){
4357                 var row = {
4358                     tag : 'tr',
4359                     cn : []
4360                 };
4361                 
4362                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4363                     var renderer = cm.getRenderer(i);
4364                     var config = cm.config[i];
4365                     var value = '';
4366                     var id = Roo.id();
4367                     
4368                     if(typeof(renderer) !== 'undefined'){
4369                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4370                     }
4371                     
4372                     if(typeof(value) === 'object'){
4373                         renders.push({
4374                             id : id,
4375                             cfg : value 
4376                         })
4377                     }
4378                     
4379                     var td = {
4380                         tag: 'td',
4381                         id: id,
4382                         html: (typeof(value) === 'object') ? '' : value
4383                     };
4384                     
4385                     if(typeof(config.width) != 'undefined'){
4386                         td.style = 'width:' +  config.width + 'px';
4387                     }
4388                     
4389                     row.cn.push(td);
4390                    
4391                 }
4392                 
4393                 tbody.createChild(row);
4394                 
4395             });
4396         }
4397         
4398         
4399         if(renders.length){
4400             var _this = this;
4401             Roo.each(renders, function(r){
4402                 _this.renderColumn(r);
4403             })
4404         }
4405 //        
4406 //        if(this.loadMask){
4407 //            this.maskEl.hide();
4408 //        }
4409     },
4410     
4411     onBeforeLoad : function()
4412     {
4413         Roo.log('ds onBeforeLoad');
4414         
4415         this.clear();
4416         
4417 //        if(this.loadMask){
4418 //            this.maskEl.show();
4419 //        }
4420     },
4421     
4422     clear : function()
4423     {
4424         this.el.select('tbody', true).first().dom.innerHTML = '';
4425     },
4426     
4427     getSelectionModel : function(){
4428         if(!this.selModel){
4429             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4430         }
4431         return this.selModel;
4432     },
4433     
4434     renderColumn : function(r)
4435     {
4436         var _this = this;
4437         r.cfg.render(Roo.get(r.id));
4438         
4439         if(r.cfg.cn){
4440             Roo.each(r.cfg.cn, function(c){
4441                 var child = {
4442                     id: r.id,
4443                     cfg: c
4444                 }
4445                 _this.renderColumn(child);
4446             })
4447         }
4448     }
4449    
4450 });
4451
4452  
4453
4454  /*
4455  * - LGPL
4456  *
4457  * table cell
4458  * 
4459  */
4460
4461 /**
4462  * @class Roo.bootstrap.TableCell
4463  * @extends Roo.bootstrap.Component
4464  * Bootstrap TableCell class
4465  * @cfg {String} html cell contain text
4466  * @cfg {String} cls cell class
4467  * @cfg {String} tag cell tag (td|th) default td
4468  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4469  * @cfg {String} align Aligns the content in a cell
4470  * @cfg {String} axis Categorizes cells
4471  * @cfg {String} bgcolor Specifies the background color of a cell
4472  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4473  * @cfg {Number} colspan Specifies the number of columns a cell should span
4474  * @cfg {String} headers Specifies one or more header cells a cell is related to
4475  * @cfg {Number} height Sets the height of a cell
4476  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4477  * @cfg {Number} rowspan Sets the number of rows a cell should span
4478  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4479  * @cfg {String} valign Vertical aligns the content in a cell
4480  * @cfg {Number} width Specifies the width of a cell
4481  * 
4482  * @constructor
4483  * Create a new TableCell
4484  * @param {Object} config The config object
4485  */
4486
4487 Roo.bootstrap.TableCell = function(config){
4488     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4489 };
4490
4491 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4492     
4493     html: false,
4494     cls: false,
4495     tag: false,
4496     abbr: false,
4497     align: false,
4498     axis: false,
4499     bgcolor: false,
4500     charoff: false,
4501     colspan: false,
4502     headers: false,
4503     height: false,
4504     nowrap: false,
4505     rowspan: false,
4506     scope: false,
4507     valign: false,
4508     width: false,
4509     
4510     
4511     getAutoCreate : function(){
4512         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4513         
4514         cfg = {
4515             tag: 'td'
4516         }
4517         
4518         if(this.tag){
4519             cfg.tag = this.tag;
4520         }
4521         
4522         if (this.html) {
4523             cfg.html=this.html
4524         }
4525         if (this.cls) {
4526             cfg.cls=this.cls
4527         }
4528         if (this.abbr) {
4529             cfg.abbr=this.abbr
4530         }
4531         if (this.align) {
4532             cfg.align=this.align
4533         }
4534         if (this.axis) {
4535             cfg.axis=this.axis
4536         }
4537         if (this.bgcolor) {
4538             cfg.bgcolor=this.bgcolor
4539         }
4540         if (this.charoff) {
4541             cfg.charoff=this.charoff
4542         }
4543         if (this.colspan) {
4544             cfg.colspan=this.colspan
4545         }
4546         if (this.headers) {
4547             cfg.headers=this.headers
4548         }
4549         if (this.height) {
4550             cfg.height=this.height
4551         }
4552         if (this.nowrap) {
4553             cfg.nowrap=this.nowrap
4554         }
4555         if (this.rowspan) {
4556             cfg.rowspan=this.rowspan
4557         }
4558         if (this.scope) {
4559             cfg.scope=this.scope
4560         }
4561         if (this.valign) {
4562             cfg.valign=this.valign
4563         }
4564         if (this.width) {
4565             cfg.width=this.width
4566         }
4567         
4568         
4569         return cfg;
4570     }
4571    
4572 });
4573
4574  
4575
4576  /*
4577  * - LGPL
4578  *
4579  * table row
4580  * 
4581  */
4582
4583 /**
4584  * @class Roo.bootstrap.TableRow
4585  * @extends Roo.bootstrap.Component
4586  * Bootstrap TableRow class
4587  * @cfg {String} cls row class
4588  * @cfg {String} align Aligns the content in a table row
4589  * @cfg {String} bgcolor Specifies a background color for a table row
4590  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4591  * @cfg {String} valign Vertical aligns the content in a table row
4592  * 
4593  * @constructor
4594  * Create a new TableRow
4595  * @param {Object} config The config object
4596  */
4597
4598 Roo.bootstrap.TableRow = function(config){
4599     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4600 };
4601
4602 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4603     
4604     cls: false,
4605     align: false,
4606     bgcolor: false,
4607     charoff: false,
4608     valign: false,
4609     
4610     getAutoCreate : function(){
4611         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4612         
4613         cfg = {
4614             tag: 'tr'
4615         }
4616             
4617         if(this.cls){
4618             cfg.cls = this.cls;
4619         }
4620         if(this.align){
4621             cfg.align = this.align;
4622         }
4623         if(this.bgcolor){
4624             cfg.bgcolor = this.bgcolor;
4625         }
4626         if(this.charoff){
4627             cfg.charoff = this.charoff;
4628         }
4629         if(this.valign){
4630             cfg.valign = this.valign;
4631         }
4632         
4633         return cfg;
4634     }
4635    
4636 });
4637
4638  
4639
4640  /*
4641  * - LGPL
4642  *
4643  * table body
4644  * 
4645  */
4646
4647 /**
4648  * @class Roo.bootstrap.TableBody
4649  * @extends Roo.bootstrap.Component
4650  * Bootstrap TableBody class
4651  * @cfg {String} cls element class
4652  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4653  * @cfg {String} align Aligns the content inside the element
4654  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4655  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4656  * 
4657  * @constructor
4658  * Create a new TableBody
4659  * @param {Object} config The config object
4660  */
4661
4662 Roo.bootstrap.TableBody = function(config){
4663     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4664 };
4665
4666 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4667     
4668     cls: false,
4669     tag: false,
4670     align: false,
4671     charoff: false,
4672     valign: false,
4673     
4674     getAutoCreate : function(){
4675         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4676         
4677         cfg = {
4678             tag: 'tbody'
4679         }
4680             
4681         if (this.cls) {
4682             cfg.cls=this.cls
4683         }
4684         if(this.tag){
4685             cfg.tag = this.tag;
4686         }
4687         
4688         if(this.align){
4689             cfg.align = this.align;
4690         }
4691         if(this.charoff){
4692             cfg.charoff = this.charoff;
4693         }
4694         if(this.valign){
4695             cfg.valign = this.valign;
4696         }
4697         
4698         return cfg;
4699     }
4700     
4701     
4702 //    initEvents : function()
4703 //    {
4704 //        
4705 //        if(!this.store){
4706 //            return;
4707 //        }
4708 //        
4709 //        this.store = Roo.factory(this.store, Roo.data);
4710 //        this.store.on('load', this.onLoad, this);
4711 //        
4712 //        this.store.load();
4713 //        
4714 //    },
4715 //    
4716 //    onLoad: function () 
4717 //    {   
4718 //        this.fireEvent('load', this);
4719 //    }
4720 //    
4721 //   
4722 });
4723
4724  
4725
4726  /*
4727  * Based on:
4728  * Ext JS Library 1.1.1
4729  * Copyright(c) 2006-2007, Ext JS, LLC.
4730  *
4731  * Originally Released Under LGPL - original licence link has changed is not relivant.
4732  *
4733  * Fork - LGPL
4734  * <script type="text/javascript">
4735  */
4736
4737 // as we use this in bootstrap.
4738 Roo.namespace('Roo.form');
4739  /**
4740  * @class Roo.form.Action
4741  * Internal Class used to handle form actions
4742  * @constructor
4743  * @param {Roo.form.BasicForm} el The form element or its id
4744  * @param {Object} config Configuration options
4745  */
4746
4747  
4748  
4749 // define the action interface
4750 Roo.form.Action = function(form, options){
4751     this.form = form;
4752     this.options = options || {};
4753 };
4754 /**
4755  * Client Validation Failed
4756  * @const 
4757  */
4758 Roo.form.Action.CLIENT_INVALID = 'client';
4759 /**
4760  * Server Validation Failed
4761  * @const 
4762  */
4763 Roo.form.Action.SERVER_INVALID = 'server';
4764  /**
4765  * Connect to Server Failed
4766  * @const 
4767  */
4768 Roo.form.Action.CONNECT_FAILURE = 'connect';
4769 /**
4770  * Reading Data from Server Failed
4771  * @const 
4772  */
4773 Roo.form.Action.LOAD_FAILURE = 'load';
4774
4775 Roo.form.Action.prototype = {
4776     type : 'default',
4777     failureType : undefined,
4778     response : undefined,
4779     result : undefined,
4780
4781     // interface method
4782     run : function(options){
4783
4784     },
4785
4786     // interface method
4787     success : function(response){
4788
4789     },
4790
4791     // interface method
4792     handleResponse : function(response){
4793
4794     },
4795
4796     // default connection failure
4797     failure : function(response){
4798         
4799         this.response = response;
4800         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4801         this.form.afterAction(this, false);
4802     },
4803
4804     processResponse : function(response){
4805         this.response = response;
4806         if(!response.responseText){
4807             return true;
4808         }
4809         this.result = this.handleResponse(response);
4810         return this.result;
4811     },
4812
4813     // utility functions used internally
4814     getUrl : function(appendParams){
4815         var url = this.options.url || this.form.url || this.form.el.dom.action;
4816         if(appendParams){
4817             var p = this.getParams();
4818             if(p){
4819                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4820             }
4821         }
4822         return url;
4823     },
4824
4825     getMethod : function(){
4826         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4827     },
4828
4829     getParams : function(){
4830         var bp = this.form.baseParams;
4831         var p = this.options.params;
4832         if(p){
4833             if(typeof p == "object"){
4834                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4835             }else if(typeof p == 'string' && bp){
4836                 p += '&' + Roo.urlEncode(bp);
4837             }
4838         }else if(bp){
4839             p = Roo.urlEncode(bp);
4840         }
4841         return p;
4842     },
4843
4844     createCallback : function(){
4845         return {
4846             success: this.success,
4847             failure: this.failure,
4848             scope: this,
4849             timeout: (this.form.timeout*1000),
4850             upload: this.form.fileUpload ? this.success : undefined
4851         };
4852     }
4853 };
4854
4855 Roo.form.Action.Submit = function(form, options){
4856     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4857 };
4858
4859 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4860     type : 'submit',
4861
4862     haveProgress : false,
4863     uploadComplete : false,
4864     
4865     // uploadProgress indicator.
4866     uploadProgress : function()
4867     {
4868         if (!this.form.progressUrl) {
4869             return;
4870         }
4871         
4872         if (!this.haveProgress) {
4873             Roo.MessageBox.progress("Uploading", "Uploading");
4874         }
4875         if (this.uploadComplete) {
4876            Roo.MessageBox.hide();
4877            return;
4878         }
4879         
4880         this.haveProgress = true;
4881    
4882         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4883         
4884         var c = new Roo.data.Connection();
4885         c.request({
4886             url : this.form.progressUrl,
4887             params: {
4888                 id : uid
4889             },
4890             method: 'GET',
4891             success : function(req){
4892                //console.log(data);
4893                 var rdata = false;
4894                 var edata;
4895                 try  {
4896                    rdata = Roo.decode(req.responseText)
4897                 } catch (e) {
4898                     Roo.log("Invalid data from server..");
4899                     Roo.log(edata);
4900                     return;
4901                 }
4902                 if (!rdata || !rdata.success) {
4903                     Roo.log(rdata);
4904                     Roo.MessageBox.alert(Roo.encode(rdata));
4905                     return;
4906                 }
4907                 var data = rdata.data;
4908                 
4909                 if (this.uploadComplete) {
4910                    Roo.MessageBox.hide();
4911                    return;
4912                 }
4913                    
4914                 if (data){
4915                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4916                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4917                     );
4918                 }
4919                 this.uploadProgress.defer(2000,this);
4920             },
4921        
4922             failure: function(data) {
4923                 Roo.log('progress url failed ');
4924                 Roo.log(data);
4925             },
4926             scope : this
4927         });
4928            
4929     },
4930     
4931     
4932     run : function()
4933     {
4934         // run get Values on the form, so it syncs any secondary forms.
4935         this.form.getValues();
4936         
4937         var o = this.options;
4938         var method = this.getMethod();
4939         var isPost = method == 'POST';
4940         if(o.clientValidation === false || this.form.isValid()){
4941             
4942             if (this.form.progressUrl) {
4943                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4944                     (new Date() * 1) + '' + Math.random());
4945                     
4946             } 
4947             
4948             
4949             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4950                 form:this.form.el.dom,
4951                 url:this.getUrl(!isPost),
4952                 method: method,
4953                 params:isPost ? this.getParams() : null,
4954                 isUpload: this.form.fileUpload
4955             }));
4956             
4957             this.uploadProgress();
4958
4959         }else if (o.clientValidation !== false){ // client validation failed
4960             this.failureType = Roo.form.Action.CLIENT_INVALID;
4961             this.form.afterAction(this, false);
4962         }
4963     },
4964
4965     success : function(response)
4966     {
4967         this.uploadComplete= true;
4968         if (this.haveProgress) {
4969             Roo.MessageBox.hide();
4970         }
4971         
4972         
4973         var result = this.processResponse(response);
4974         if(result === true || result.success){
4975             this.form.afterAction(this, true);
4976             return;
4977         }
4978         if(result.errors){
4979             this.form.markInvalid(result.errors);
4980             this.failureType = Roo.form.Action.SERVER_INVALID;
4981         }
4982         this.form.afterAction(this, false);
4983     },
4984     failure : function(response)
4985     {
4986         this.uploadComplete= true;
4987         if (this.haveProgress) {
4988             Roo.MessageBox.hide();
4989         }
4990         
4991         this.response = response;
4992         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4993         this.form.afterAction(this, false);
4994     },
4995     
4996     handleResponse : function(response){
4997         if(this.form.errorReader){
4998             var rs = this.form.errorReader.read(response);
4999             var errors = [];
5000             if(rs.records){
5001                 for(var i = 0, len = rs.records.length; i < len; i++) {
5002                     var r = rs.records[i];
5003                     errors[i] = r.data;
5004                 }
5005             }
5006             if(errors.length < 1){
5007                 errors = null;
5008             }
5009             return {
5010                 success : rs.success,
5011                 errors : errors
5012             };
5013         }
5014         var ret = false;
5015         try {
5016             ret = Roo.decode(response.responseText);
5017         } catch (e) {
5018             ret = {
5019                 success: false,
5020                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5021                 errors : []
5022             };
5023         }
5024         return ret;
5025         
5026     }
5027 });
5028
5029
5030 Roo.form.Action.Load = function(form, options){
5031     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5032     this.reader = this.form.reader;
5033 };
5034
5035 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5036     type : 'load',
5037
5038     run : function(){
5039         
5040         Roo.Ajax.request(Roo.apply(
5041                 this.createCallback(), {
5042                     method:this.getMethod(),
5043                     url:this.getUrl(false),
5044                     params:this.getParams()
5045         }));
5046     },
5047
5048     success : function(response){
5049         
5050         var result = this.processResponse(response);
5051         if(result === true || !result.success || !result.data){
5052             this.failureType = Roo.form.Action.LOAD_FAILURE;
5053             this.form.afterAction(this, false);
5054             return;
5055         }
5056         this.form.clearInvalid();
5057         this.form.setValues(result.data);
5058         this.form.afterAction(this, true);
5059     },
5060
5061     handleResponse : function(response){
5062         if(this.form.reader){
5063             var rs = this.form.reader.read(response);
5064             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5065             return {
5066                 success : rs.success,
5067                 data : data
5068             };
5069         }
5070         return Roo.decode(response.responseText);
5071     }
5072 });
5073
5074 Roo.form.Action.ACTION_TYPES = {
5075     'load' : Roo.form.Action.Load,
5076     'submit' : Roo.form.Action.Submit
5077 };/*
5078  * - LGPL
5079  *
5080  * form
5081  * 
5082  */
5083
5084 /**
5085  * @class Roo.bootstrap.Form
5086  * @extends Roo.bootstrap.Component
5087  * Bootstrap Form class
5088  * @cfg {String} method  GET | POST (default POST)
5089  * @cfg {String} labelAlign top | left (default top)
5090   * @cfg {String} align left  | right - for navbars
5091
5092  * 
5093  * @constructor
5094  * Create a new Form
5095  * @param {Object} config The config object
5096  */
5097
5098
5099 Roo.bootstrap.Form = function(config){
5100     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5101     this.addEvents({
5102         /**
5103          * @event clientvalidation
5104          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5105          * @param {Form} this
5106          * @param {Boolean} valid true if the form has passed client-side validation
5107          */
5108         clientvalidation: true,
5109         /**
5110          * @event beforeaction
5111          * Fires before any action is performed. Return false to cancel the action.
5112          * @param {Form} this
5113          * @param {Action} action The action to be performed
5114          */
5115         beforeaction: true,
5116         /**
5117          * @event actionfailed
5118          * Fires when an action fails.
5119          * @param {Form} this
5120          * @param {Action} action The action that failed
5121          */
5122         actionfailed : true,
5123         /**
5124          * @event actioncomplete
5125          * Fires when an action is completed.
5126          * @param {Form} this
5127          * @param {Action} action The action that completed
5128          */
5129         actioncomplete : true
5130     });
5131     
5132 };
5133
5134 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5135       
5136      /**
5137      * @cfg {String} method
5138      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5139      */
5140     method : 'POST',
5141     /**
5142      * @cfg {String} url
5143      * The URL to use for form actions if one isn't supplied in the action options.
5144      */
5145     /**
5146      * @cfg {Boolean} fileUpload
5147      * Set to true if this form is a file upload.
5148      */
5149      
5150     /**
5151      * @cfg {Object} baseParams
5152      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5153      */
5154       
5155     /**
5156      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5157      */
5158     timeout: 30,
5159     /**
5160      * @cfg {Sting} align (left|right) for navbar forms
5161      */
5162     align : 'left',
5163
5164     // private
5165     activeAction : null,
5166  
5167     /**
5168      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5169      * element by passing it or its id or mask the form itself by passing in true.
5170      * @type Mixed
5171      */
5172     waitMsgTarget : false,
5173     
5174      
5175     
5176     /**
5177      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5178      * element by passing it or its id or mask the form itself by passing in true.
5179      * @type Mixed
5180      */
5181     
5182     getAutoCreate : function(){
5183         
5184         var cfg = {
5185             tag: 'form',
5186             method : this.method || 'POST',
5187             id : this.id || Roo.id(),
5188             cls : ''
5189         }
5190         if (this.parent().xtype.match(/^Nav/)) {
5191             cfg.cls = 'navbar-form navbar-' + this.align;
5192             
5193         }
5194         
5195         if (this.labelAlign == 'left' ) {
5196             cfg.cls += ' form-horizontal';
5197         }
5198         
5199         
5200         return cfg;
5201     },
5202     initEvents : function()
5203     {
5204         this.el.on('submit', this.onSubmit, this);
5205         
5206         
5207     },
5208     // private
5209     onSubmit : function(e){
5210         e.stopEvent();
5211     },
5212     
5213      /**
5214      * Returns true if client-side validation on the form is successful.
5215      * @return Boolean
5216      */
5217     isValid : function(){
5218         var items = this.getItems();
5219         var valid = true;
5220         items.each(function(f){
5221            if(!f.validate()){
5222                valid = false;
5223                
5224            }
5225         });
5226         return valid;
5227     },
5228     /**
5229      * Returns true if any fields in this form have changed since their original load.
5230      * @return Boolean
5231      */
5232     isDirty : function(){
5233         var dirty = false;
5234         var items = this.getItems();
5235         items.each(function(f){
5236            if(f.isDirty()){
5237                dirty = true;
5238                return false;
5239            }
5240            return true;
5241         });
5242         return dirty;
5243     },
5244      /**
5245      * Performs a predefined action (submit or load) or custom actions you define on this form.
5246      * @param {String} actionName The name of the action type
5247      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5248      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5249      * accept other config options):
5250      * <pre>
5251 Property          Type             Description
5252 ----------------  ---------------  ----------------------------------------------------------------------------------
5253 url               String           The url for the action (defaults to the form's url)
5254 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5255 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5256 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5257                                    validate the form on the client (defaults to false)
5258      * </pre>
5259      * @return {BasicForm} this
5260      */
5261     doAction : function(action, options){
5262         if(typeof action == 'string'){
5263             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5264         }
5265         if(this.fireEvent('beforeaction', this, action) !== false){
5266             this.beforeAction(action);
5267             action.run.defer(100, action);
5268         }
5269         return this;
5270     },
5271     
5272     // private
5273     beforeAction : function(action){
5274         var o = action.options;
5275         
5276         // not really supported yet.. ??
5277         
5278         //if(this.waitMsgTarget === true){
5279             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5280         //}else if(this.waitMsgTarget){
5281         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5282         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5283         //}else {
5284         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5285        // }
5286          
5287     },
5288
5289     // private
5290     afterAction : function(action, success){
5291         this.activeAction = null;
5292         var o = action.options;
5293         
5294         //if(this.waitMsgTarget === true){
5295             this.el.unmask();
5296         //}else if(this.waitMsgTarget){
5297         //    this.waitMsgTarget.unmask();
5298         //}else{
5299         //    Roo.MessageBox.updateProgress(1);
5300         //    Roo.MessageBox.hide();
5301        // }
5302         // 
5303         if(success){
5304             if(o.reset){
5305                 this.reset();
5306             }
5307             Roo.callback(o.success, o.scope, [this, action]);
5308             this.fireEvent('actioncomplete', this, action);
5309             
5310         }else{
5311             
5312             // failure condition..
5313             // we have a scenario where updates need confirming.
5314             // eg. if a locking scenario exists..
5315             // we look for { errors : { needs_confirm : true }} in the response.
5316             if (
5317                 (typeof(action.result) != 'undefined')  &&
5318                 (typeof(action.result.errors) != 'undefined')  &&
5319                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5320            ){
5321                 var _t = this;
5322                 Roo.log("not supported yet");
5323                  /*
5324                 
5325                 Roo.MessageBox.confirm(
5326                     "Change requires confirmation",
5327                     action.result.errorMsg,
5328                     function(r) {
5329                         if (r != 'yes') {
5330                             return;
5331                         }
5332                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5333                     }
5334                     
5335                 );
5336                 */
5337                 
5338                 
5339                 return;
5340             }
5341             
5342             Roo.callback(o.failure, o.scope, [this, action]);
5343             // show an error message if no failed handler is set..
5344             if (!this.hasListener('actionfailed')) {
5345                 Roo.log("need to add dialog support");
5346                 /*
5347                 Roo.MessageBox.alert("Error",
5348                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5349                         action.result.errorMsg :
5350                         "Saving Failed, please check your entries or try again"
5351                 );
5352                 */
5353             }
5354             
5355             this.fireEvent('actionfailed', this, action);
5356         }
5357         
5358     },
5359     /**
5360      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5361      * @param {String} id The value to search for
5362      * @return Field
5363      */
5364     findField : function(id){
5365         var items = this.getItems();
5366         var field = items.get(id);
5367         if(!field){
5368              items.each(function(f){
5369                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5370                     field = f;
5371                     return false;
5372                 }
5373                 return true;
5374             });
5375         }
5376         return field || null;
5377     },
5378      /**
5379      * Mark fields in this form invalid in bulk.
5380      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5381      * @return {BasicForm} this
5382      */
5383     markInvalid : function(errors){
5384         if(errors instanceof Array){
5385             for(var i = 0, len = errors.length; i < len; i++){
5386                 var fieldError = errors[i];
5387                 var f = this.findField(fieldError.id);
5388                 if(f){
5389                     f.markInvalid(fieldError.msg);
5390                 }
5391             }
5392         }else{
5393             var field, id;
5394             for(id in errors){
5395                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5396                     field.markInvalid(errors[id]);
5397                 }
5398             }
5399         }
5400         //Roo.each(this.childForms || [], function (f) {
5401         //    f.markInvalid(errors);
5402         //});
5403         
5404         return this;
5405     },
5406
5407     /**
5408      * Set values for fields in this form in bulk.
5409      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5410      * @return {BasicForm} this
5411      */
5412     setValues : function(values){
5413         if(values instanceof Array){ // array of objects
5414             for(var i = 0, len = values.length; i < len; i++){
5415                 var v = values[i];
5416                 var f = this.findField(v.id);
5417                 if(f){
5418                     f.setValue(v.value);
5419                     if(this.trackResetOnLoad){
5420                         f.originalValue = f.getValue();
5421                     }
5422                 }
5423             }
5424         }else{ // object hash
5425             var field, id;
5426             for(id in values){
5427                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5428                     
5429                     if (field.setFromData && 
5430                         field.valueField && 
5431                         field.displayField &&
5432                         // combos' with local stores can 
5433                         // be queried via setValue()
5434                         // to set their value..
5435                         (field.store && !field.store.isLocal)
5436                         ) {
5437                         // it's a combo
5438                         var sd = { };
5439                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5440                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5441                         field.setFromData(sd);
5442                         
5443                     } else {
5444                         field.setValue(values[id]);
5445                     }
5446                     
5447                     
5448                     if(this.trackResetOnLoad){
5449                         field.originalValue = field.getValue();
5450                     }
5451                 }
5452             }
5453         }
5454          
5455         //Roo.each(this.childForms || [], function (f) {
5456         //    f.setValues(values);
5457         //});
5458                 
5459         return this;
5460     },
5461
5462     /**
5463      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5464      * they are returned as an array.
5465      * @param {Boolean} asString
5466      * @return {Object}
5467      */
5468     getValues : function(asString){
5469         //if (this.childForms) {
5470             // copy values from the child forms
5471         //    Roo.each(this.childForms, function (f) {
5472         //        this.setValues(f.getValues());
5473         //    }, this);
5474         //}
5475         
5476         
5477         
5478         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5479         if(asString === true){
5480             return fs;
5481         }
5482         return Roo.urlDecode(fs);
5483     },
5484     
5485     /**
5486      * Returns the fields in this form as an object with key/value pairs. 
5487      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5488      * @return {Object}
5489      */
5490     getFieldValues : function(with_hidden)
5491     {
5492         var items = this.getItems();
5493         var ret = {};
5494         items.each(function(f){
5495             if (!f.getName()) {
5496                 return;
5497             }
5498             var v = f.getValue();
5499             if (f.inputType =='radio') {
5500                 if (typeof(ret[f.getName()]) == 'undefined') {
5501                     ret[f.getName()] = ''; // empty..
5502                 }
5503                 
5504                 if (!f.el.dom.checked) {
5505                     return;
5506                     
5507                 }
5508                 v = f.el.dom.value;
5509                 
5510             }
5511             
5512             // not sure if this supported any more..
5513             if ((typeof(v) == 'object') && f.getRawValue) {
5514                 v = f.getRawValue() ; // dates..
5515             }
5516             // combo boxes where name != hiddenName...
5517             if (f.name != f.getName()) {
5518                 ret[f.name] = f.getRawValue();
5519             }
5520             ret[f.getName()] = v;
5521         });
5522         
5523         return ret;
5524     },
5525
5526     /**
5527      * Clears all invalid messages in this form.
5528      * @return {BasicForm} this
5529      */
5530     clearInvalid : function(){
5531         var items = this.getItems();
5532         
5533         items.each(function(f){
5534            f.clearInvalid();
5535         });
5536         
5537         
5538         
5539         return this;
5540     },
5541
5542     /**
5543      * Resets this form.
5544      * @return {BasicForm} this
5545      */
5546     reset : function(){
5547         var items = this.getItems();
5548         items.each(function(f){
5549             f.reset();
5550         });
5551         
5552         Roo.each(this.childForms || [], function (f) {
5553             f.reset();
5554         });
5555        
5556         
5557         return this;
5558     },
5559     getItems : function()
5560     {
5561         var r=new Roo.util.MixedCollection(false, function(o){
5562             return o.id || (o.id = Roo.id());
5563         });
5564         var iter = function(el) {
5565             if (el.inputEl) {
5566                 r.add(el);
5567             }
5568             if (!el.items) {
5569                 return;
5570             }
5571             Roo.each(el.items,function(e) {
5572                 iter(e);
5573             });
5574             
5575             
5576         };
5577         iter(this);
5578         return r;
5579         
5580         
5581         
5582         
5583     }
5584     
5585 });
5586
5587  
5588 /*
5589  * Based on:
5590  * Ext JS Library 1.1.1
5591  * Copyright(c) 2006-2007, Ext JS, LLC.
5592  *
5593  * Originally Released Under LGPL - original licence link has changed is not relivant.
5594  *
5595  * Fork - LGPL
5596  * <script type="text/javascript">
5597  */
5598 /**
5599  * @class Roo.form.VTypes
5600  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5601  * @singleton
5602  */
5603 Roo.form.VTypes = function(){
5604     // closure these in so they are only created once.
5605     var alpha = /^[a-zA-Z_]+$/;
5606     var alphanum = /^[a-zA-Z0-9_]+$/;
5607     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5608     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5609
5610     // All these messages and functions are configurable
5611     return {
5612         /**
5613          * The function used to validate email addresses
5614          * @param {String} value The email address
5615          */
5616         'email' : function(v){
5617             return email.test(v);
5618         },
5619         /**
5620          * The error text to display when the email validation function returns false
5621          * @type String
5622          */
5623         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5624         /**
5625          * The keystroke filter mask to be applied on email input
5626          * @type RegExp
5627          */
5628         'emailMask' : /[a-z0-9_\.\-@]/i,
5629
5630         /**
5631          * The function used to validate URLs
5632          * @param {String} value The URL
5633          */
5634         'url' : function(v){
5635             return url.test(v);
5636         },
5637         /**
5638          * The error text to display when the url validation function returns false
5639          * @type String
5640          */
5641         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5642         
5643         /**
5644          * The function used to validate alpha values
5645          * @param {String} value The value
5646          */
5647         'alpha' : function(v){
5648             return alpha.test(v);
5649         },
5650         /**
5651          * The error text to display when the alpha validation function returns false
5652          * @type String
5653          */
5654         'alphaText' : 'This field should only contain letters and _',
5655         /**
5656          * The keystroke filter mask to be applied on alpha input
5657          * @type RegExp
5658          */
5659         'alphaMask' : /[a-z_]/i,
5660
5661         /**
5662          * The function used to validate alphanumeric values
5663          * @param {String} value The value
5664          */
5665         'alphanum' : function(v){
5666             return alphanum.test(v);
5667         },
5668         /**
5669          * The error text to display when the alphanumeric validation function returns false
5670          * @type String
5671          */
5672         'alphanumText' : 'This field should only contain letters, numbers and _',
5673         /**
5674          * The keystroke filter mask to be applied on alphanumeric input
5675          * @type RegExp
5676          */
5677         'alphanumMask' : /[a-z0-9_]/i
5678     };
5679 }();/*
5680  * - LGPL
5681  *
5682  * Input
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.Input
5688  * @extends Roo.bootstrap.Component
5689  * Bootstrap Input class
5690  * @cfg {Boolean} disabled is it disabled
5691  * @cfg {String} fieldLabel - the label associated
5692  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5693  * @cfg {String} name name of the input
5694  * @cfg {string} fieldLabel - the label associated
5695  * @cfg {string}  inputType - input / file submit ...
5696  * @cfg {string} placeholder - placeholder to put in text.
5697  * @cfg {string}  before - input group add on before
5698  * @cfg {string} after - input group add on after
5699  * @cfg {string} size - (lg|sm) or leave empty..
5700  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5701  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5702  * @cfg {Number} md colspan out of 12 for computer-sized screens
5703  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5704  * @cfg {string} value default value of the input
5705  * @cfg {Number} labelWidth set the width of label (0-12)
5706  * @cfg {String} labelAlign (top|left)
5707  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5708  * 
5709  * 
5710  * @constructor
5711  * Create a new Input
5712  * @param {Object} config The config object
5713  */
5714
5715 Roo.bootstrap.Input = function(config){
5716     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5717    
5718         this.addEvents({
5719             /**
5720              * @event focus
5721              * Fires when this field receives input focus.
5722              * @param {Roo.form.Field} this
5723              */
5724             focus : true,
5725             /**
5726              * @event blur
5727              * Fires when this field loses input focus.
5728              * @param {Roo.form.Field} this
5729              */
5730             blur : true,
5731             /**
5732              * @event specialkey
5733              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5734              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5735              * @param {Roo.form.Field} this
5736              * @param {Roo.EventObject} e The event object
5737              */
5738             specialkey : true,
5739             /**
5740              * @event change
5741              * Fires just before the field blurs if the field value has changed.
5742              * @param {Roo.form.Field} this
5743              * @param {Mixed} newValue The new value
5744              * @param {Mixed} oldValue The original value
5745              */
5746             change : true,
5747             /**
5748              * @event invalid
5749              * Fires after the field has been marked as invalid.
5750              * @param {Roo.form.Field} this
5751              * @param {String} msg The validation message
5752              */
5753             invalid : true,
5754             /**
5755              * @event valid
5756              * Fires after the field has been validated with no errors.
5757              * @param {Roo.form.Field} this
5758              */
5759             valid : true,
5760              /**
5761              * @event keyup
5762              * Fires after the key up
5763              * @param {Roo.form.Field} this
5764              * @param {Roo.EventObject}  e The event Object
5765              */
5766             keyup : true
5767         });
5768 };
5769
5770 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5771      /**
5772      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5773       automatic validation (defaults to "keyup").
5774      */
5775     validationEvent : "keyup",
5776      /**
5777      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5778      */
5779     validateOnBlur : true,
5780     /**
5781      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5782      */
5783     validationDelay : 250,
5784      /**
5785      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5786      */
5787     focusClass : "x-form-focus",  // not needed???
5788     
5789        
5790     /**
5791      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5792      */
5793     invalidClass : "has-error",
5794     
5795     /**
5796      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5797      */
5798     selectOnFocus : false,
5799     
5800      /**
5801      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5802      */
5803     maskRe : null,
5804        /**
5805      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5806      */
5807     vtype : null,
5808     
5809       /**
5810      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5811      */
5812     disableKeyFilter : false,
5813     
5814        /**
5815      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5816      */
5817     disabled : false,
5818      /**
5819      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5820      */
5821     allowBlank : true,
5822     /**
5823      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5824      */
5825     blankText : "This field is required",
5826     
5827      /**
5828      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5829      */
5830     minLength : 0,
5831     /**
5832      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5833      */
5834     maxLength : Number.MAX_VALUE,
5835     /**
5836      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5837      */
5838     minLengthText : "The minimum length for this field is {0}",
5839     /**
5840      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5841      */
5842     maxLengthText : "The maximum length for this field is {0}",
5843   
5844     
5845     /**
5846      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5847      * If available, this function will be called only after the basic validators all return true, and will be passed the
5848      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5849      */
5850     validator : null,
5851     /**
5852      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5853      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5854      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5855      */
5856     regex : null,
5857     /**
5858      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5859      */
5860     regexText : "",
5861     
5862     
5863     
5864     fieldLabel : '',
5865     inputType : 'text',
5866     
5867     name : false,
5868     placeholder: false,
5869     before : false,
5870     after : false,
5871     size : false,
5872     // private
5873     hasFocus : false,
5874     preventMark: false,
5875     isFormField : true,
5876     value : '',
5877     labelWidth : 2,
5878     labelAlign : false,
5879     readOnly : false,
5880     
5881     parentLabelAlign : function()
5882     {
5883         var parent = this;
5884         while (parent.parent()) {
5885             parent = parent.parent();
5886             if (typeof(parent.labelAlign) !='undefined') {
5887                 return parent.labelAlign;
5888             }
5889         }
5890         return 'left';
5891         
5892     },
5893     
5894     getAutoCreate : function(){
5895         
5896         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5897         
5898         var id = Roo.id();
5899         
5900         var cfg = {};
5901         
5902         if(this.inputType != 'hidden'){
5903             cfg.cls = 'form-group' //input-group
5904         }
5905         
5906         var input =  {
5907             tag: 'input',
5908             id : id,
5909             type : this.inputType,
5910             value : this.value,
5911             cls : 'form-control',
5912             placeholder : this.placeholder || ''
5913             
5914         };
5915         
5916         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5917             input.maxLength = this.maxLength;
5918         }
5919         
5920         if (this.disabled) {
5921             input.disabled=true;
5922         }
5923         
5924         if (this.readOnly) {
5925             input.readonly=true;
5926         }
5927         
5928         if (this.name) {
5929             input.name = this.name;
5930         }
5931         if (this.size) {
5932             input.cls += ' input-' + this.size;
5933         }
5934         var settings=this;
5935         ['xs','sm','md','lg'].map(function(size){
5936             if (settings[size]) {
5937                 cfg.cls += ' col-' + size + '-' + settings[size];
5938             }
5939         });
5940         
5941         var inputblock = input;
5942         
5943         if (this.before || this.after) {
5944             
5945             inputblock = {
5946                 cls : 'input-group',
5947                 cn :  [] 
5948             };
5949             if (this.before && typeof(this.before) == 'string') {
5950                 
5951                 inputblock.cn.push({
5952                     tag :'span',
5953                     cls : 'roo-input-before input-group-addon',
5954                     html : this.before
5955                 });
5956             }
5957             if (this.before && typeof(this.before) == 'object') {
5958                 this.before = Roo.factory(this.before);
5959                 Roo.log(this.before);
5960                 inputblock.cn.push({
5961                     tag :'span',
5962                     cls : 'roo-input-before input-group-' +
5963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5964                 });
5965             }
5966             
5967             inputblock.cn.push(input);
5968             
5969             if (this.after && typeof(this.after) == 'string') {
5970                 inputblock.cn.push({
5971                     tag :'span',
5972                     cls : 'roo-input-after input-group-addon',
5973                     html : this.after
5974                 });
5975             }
5976             if (this.after && typeof(this.after) == 'object') {
5977                 this.after = Roo.factory(this.after);
5978                 Roo.log(this.after);
5979                 inputblock.cn.push({
5980                     tag :'span',
5981                     cls : 'roo-input-after input-group-' +
5982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5983                 });
5984             }
5985         };
5986         
5987         if (align ==='left' && this.fieldLabel.length) {
5988                 Roo.log("left and has label");
5989                 cfg.cn = [
5990                     
5991                     {
5992                         tag: 'label',
5993                         'for' :  id,
5994                         cls : 'control-label col-sm-' + this.labelWidth,
5995                         html : this.fieldLabel
5996                         
5997                     },
5998                     {
5999                         cls : "col-sm-" + (12 - this.labelWidth), 
6000                         cn: [
6001                             inputblock
6002                         ]
6003                     }
6004                     
6005                 ];
6006         } else if ( this.fieldLabel.length) {
6007                 Roo.log(" label");
6008                  cfg.cn = [
6009                    
6010                     {
6011                         tag: 'label',
6012                         //cls : 'input-group-addon',
6013                         html : this.fieldLabel
6014                         
6015                     },
6016                     
6017                     inputblock
6018                     
6019                 ];
6020
6021         } else {
6022             
6023                 Roo.log(" no label && no align");
6024                 cfg.cn = [
6025                     
6026                         inputblock
6027                     
6028                 ];
6029                 
6030                 
6031         };
6032         Roo.log('input-parentType: ' + this.parentType);
6033         
6034         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6035            cfg.cls += ' navbar-form';
6036            Roo.log(cfg);
6037         }
6038         
6039         return cfg;
6040         
6041     },
6042     /**
6043      * return the real input element.
6044      */
6045     inputEl: function ()
6046     {
6047         return this.el.select('input.form-control',true).first();
6048     },
6049     setDisabled : function(v)
6050     {
6051         var i  = this.inputEl().dom;
6052         if (!v) {
6053             i.removeAttribute('disabled');
6054             return;
6055             
6056         }
6057         i.setAttribute('disabled','true');
6058     },
6059     initEvents : function()
6060     {
6061         
6062         this.inputEl().on("keydown" , this.fireKey,  this);
6063         this.inputEl().on("focus", this.onFocus,  this);
6064         this.inputEl().on("blur", this.onBlur,  this);
6065         
6066         this.inputEl().relayEvent('keyup', this);
6067
6068         // reference to original value for reset
6069         this.originalValue = this.getValue();
6070         //Roo.form.TextField.superclass.initEvents.call(this);
6071         if(this.validationEvent == 'keyup'){
6072             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6073             this.inputEl().on('keyup', this.filterValidation, this);
6074         }
6075         else if(this.validationEvent !== false){
6076             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6077         }
6078         
6079         if(this.selectOnFocus){
6080             this.on("focus", this.preFocus, this);
6081             
6082         }
6083         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6084             this.inputEl().on("keypress", this.filterKeys, this);
6085         }
6086        /* if(this.grow){
6087             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6088             this.el.on("click", this.autoSize,  this);
6089         }
6090         */
6091         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6092             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6093         }
6094         
6095         if (typeof(this.before) == 'object') {
6096             this.before.render(this.el.select('.roo-input-before',true).first());
6097         }
6098         if (typeof(this.after) == 'object') {
6099             this.after.render(this.el.select('.roo-input-after',true).first());
6100         }
6101         
6102         
6103     },
6104     filterValidation : function(e){
6105         if(!e.isNavKeyPress()){
6106             this.validationTask.delay(this.validationDelay);
6107         }
6108     },
6109      /**
6110      * Validates the field value
6111      * @return {Boolean} True if the value is valid, else false
6112      */
6113     validate : function(){
6114         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6115         if(this.disabled || this.validateValue(this.getRawValue())){
6116             this.clearInvalid();
6117             return true;
6118         }
6119         return false;
6120     },
6121     
6122     
6123     /**
6124      * Validates a value according to the field's validation rules and marks the field as invalid
6125      * if the validation fails
6126      * @param {Mixed} value The value to validate
6127      * @return {Boolean} True if the value is valid, else false
6128      */
6129     validateValue : function(value){
6130         if(value.length < 1)  { // if it's blank
6131              if(this.allowBlank){
6132                 this.clearInvalid();
6133                 return true;
6134              }else{
6135                 this.markInvalid(this.blankText);
6136                 return false;
6137              }
6138         }
6139         if(value.length < this.minLength){
6140             this.markInvalid(String.format(this.minLengthText, this.minLength));
6141             return false;
6142         }
6143         if(value.length > this.maxLength){
6144             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6145             return false;
6146         }
6147         if(this.vtype){
6148             var vt = Roo.form.VTypes;
6149             if(!vt[this.vtype](value, this)){
6150                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6151                 return false;
6152             }
6153         }
6154         if(typeof this.validator == "function"){
6155             var msg = this.validator(value);
6156             if(msg !== true){
6157                 this.markInvalid(msg);
6158                 return false;
6159             }
6160         }
6161         if(this.regex && !this.regex.test(value)){
6162             this.markInvalid(this.regexText);
6163             return false;
6164         }
6165         return true;
6166     },
6167
6168     
6169     
6170      // private
6171     fireKey : function(e){
6172         //Roo.log('field ' + e.getKey());
6173         if(e.isNavKeyPress()){
6174             this.fireEvent("specialkey", this, e);
6175         }
6176     },
6177     focus : function (selectText){
6178         if(this.rendered){
6179             this.inputEl().focus();
6180             if(selectText === true){
6181                 this.inputEl().dom.select();
6182             }
6183         }
6184         return this;
6185     } ,
6186     
6187     onFocus : function(){
6188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6189            // this.el.addClass(this.focusClass);
6190         }
6191         if(!this.hasFocus){
6192             this.hasFocus = true;
6193             this.startValue = this.getValue();
6194             this.fireEvent("focus", this);
6195         }
6196     },
6197     
6198     beforeBlur : Roo.emptyFn,
6199
6200     
6201     // private
6202     onBlur : function(){
6203         this.beforeBlur();
6204         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6205             //this.el.removeClass(this.focusClass);
6206         }
6207         this.hasFocus = false;
6208         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6209             this.validate();
6210         }
6211         var v = this.getValue();
6212         if(String(v) !== String(this.startValue)){
6213             this.fireEvent('change', this, v, this.startValue);
6214         }
6215         this.fireEvent("blur", this);
6216     },
6217     
6218     /**
6219      * Resets the current field value to the originally loaded value and clears any validation messages
6220      */
6221     reset : function(){
6222         this.setValue(this.originalValue);
6223         this.clearInvalid();
6224     },
6225      /**
6226      * Returns the name of the field
6227      * @return {Mixed} name The name field
6228      */
6229     getName: function(){
6230         return this.name;
6231     },
6232      /**
6233      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6234      * @return {Mixed} value The field value
6235      */
6236     getValue : function(){
6237         return this.inputEl().getValue();
6238     },
6239     /**
6240      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6241      * @return {Mixed} value The field value
6242      */
6243     getRawValue : function(){
6244         var v = this.inputEl().getValue();
6245         
6246         return v;
6247     },
6248     
6249     /**
6250      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6251      * @param {Mixed} value The value to set
6252      */
6253     setRawValue : function(v){
6254         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6255     },
6256     
6257     selectText : function(start, end){
6258         var v = this.getRawValue();
6259         if(v.length > 0){
6260             start = start === undefined ? 0 : start;
6261             end = end === undefined ? v.length : end;
6262             var d = this.inputEl().dom;
6263             if(d.setSelectionRange){
6264                 d.setSelectionRange(start, end);
6265             }else if(d.createTextRange){
6266                 var range = d.createTextRange();
6267                 range.moveStart("character", start);
6268                 range.moveEnd("character", v.length-end);
6269                 range.select();
6270             }
6271         }
6272     },
6273     
6274     /**
6275      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6276      * @param {Mixed} value The value to set
6277      */
6278     setValue : function(v){
6279         this.value = v;
6280         if(this.rendered){
6281             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6282             this.validate();
6283         }
6284     },
6285     
6286     /*
6287     processValue : function(value){
6288         if(this.stripCharsRe){
6289             var newValue = value.replace(this.stripCharsRe, '');
6290             if(newValue !== value){
6291                 this.setRawValue(newValue);
6292                 return newValue;
6293             }
6294         }
6295         return value;
6296     },
6297   */
6298     preFocus : function(){
6299         
6300         if(this.selectOnFocus){
6301             this.inputEl().dom.select();
6302         }
6303     },
6304     filterKeys : function(e){
6305         var k = e.getKey();
6306         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6307             return;
6308         }
6309         var c = e.getCharCode(), cc = String.fromCharCode(c);
6310         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6311             return;
6312         }
6313         if(!this.maskRe.test(cc)){
6314             e.stopEvent();
6315         }
6316     },
6317      /**
6318      * Clear any invalid styles/messages for this field
6319      */
6320     clearInvalid : function(){
6321         
6322         if(!this.el || this.preventMark){ // not rendered
6323             return;
6324         }
6325         this.el.removeClass(this.invalidClass);
6326         /*
6327         switch(this.msgTarget){
6328             case 'qtip':
6329                 this.el.dom.qtip = '';
6330                 break;
6331             case 'title':
6332                 this.el.dom.title = '';
6333                 break;
6334             case 'under':
6335                 if(this.errorEl){
6336                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6337                 }
6338                 break;
6339             case 'side':
6340                 if(this.errorIcon){
6341                     this.errorIcon.dom.qtip = '';
6342                     this.errorIcon.hide();
6343                     this.un('resize', this.alignErrorIcon, this);
6344                 }
6345                 break;
6346             default:
6347                 var t = Roo.getDom(this.msgTarget);
6348                 t.innerHTML = '';
6349                 t.style.display = 'none';
6350                 break;
6351         }
6352         */
6353         this.fireEvent('valid', this);
6354     },
6355      /**
6356      * Mark this field as invalid
6357      * @param {String} msg The validation message
6358      */
6359     markInvalid : function(msg){
6360         if(!this.el  || this.preventMark){ // not rendered
6361             return;
6362         }
6363         this.el.addClass(this.invalidClass);
6364         /*
6365         msg = msg || this.invalidText;
6366         switch(this.msgTarget){
6367             case 'qtip':
6368                 this.el.dom.qtip = msg;
6369                 this.el.dom.qclass = 'x-form-invalid-tip';
6370                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6371                     Roo.QuickTips.enable();
6372                 }
6373                 break;
6374             case 'title':
6375                 this.el.dom.title = msg;
6376                 break;
6377             case 'under':
6378                 if(!this.errorEl){
6379                     var elp = this.el.findParent('.x-form-element', 5, true);
6380                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6381                     this.errorEl.setWidth(elp.getWidth(true)-20);
6382                 }
6383                 this.errorEl.update(msg);
6384                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6385                 break;
6386             case 'side':
6387                 if(!this.errorIcon){
6388                     var elp = this.el.findParent('.x-form-element', 5, true);
6389                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6390                 }
6391                 this.alignErrorIcon();
6392                 this.errorIcon.dom.qtip = msg;
6393                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6394                 this.errorIcon.show();
6395                 this.on('resize', this.alignErrorIcon, this);
6396                 break;
6397             default:
6398                 var t = Roo.getDom(this.msgTarget);
6399                 t.innerHTML = msg;
6400                 t.style.display = this.msgDisplay;
6401                 break;
6402         }
6403         */
6404         this.fireEvent('invalid', this, msg);
6405     },
6406     // private
6407     SafariOnKeyDown : function(event)
6408     {
6409         // this is a workaround for a password hang bug on chrome/ webkit.
6410         
6411         var isSelectAll = false;
6412         
6413         if(this.inputEl().dom.selectionEnd > 0){
6414             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6415         }
6416         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6417             event.preventDefault();
6418             this.setValue('');
6419             return;
6420         }
6421         
6422         if(isSelectAll){ // backspace and delete key
6423             
6424             event.preventDefault();
6425             // this is very hacky as keydown always get's upper case.
6426             //
6427             var cc = String.fromCharCode(event.getCharCode());
6428             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6429             
6430         }
6431     },
6432     adjustWidth : function(tag, w){
6433         tag = tag.toLowerCase();
6434         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6435             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6436                 if(tag == 'input'){
6437                     return w + 2;
6438                 }
6439                 if(tag == 'textarea'){
6440                     return w-2;
6441                 }
6442             }else if(Roo.isOpera){
6443                 if(tag == 'input'){
6444                     return w + 2;
6445                 }
6446                 if(tag == 'textarea'){
6447                     return w-2;
6448                 }
6449             }
6450         }
6451         return w;
6452     }
6453     
6454 });
6455
6456  
6457 /*
6458  * - LGPL
6459  *
6460  * Input
6461  * 
6462  */
6463
6464 /**
6465  * @class Roo.bootstrap.TextArea
6466  * @extends Roo.bootstrap.Input
6467  * Bootstrap TextArea class
6468  * @cfg {Number} cols Specifies the visible width of a text area
6469  * @cfg {Number} rows Specifies the visible number of lines in a text area
6470  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6471  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6472  * @cfg {string} html text
6473  * 
6474  * @constructor
6475  * Create a new TextArea
6476  * @param {Object} config The config object
6477  */
6478
6479 Roo.bootstrap.TextArea = function(config){
6480     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6481    
6482 };
6483
6484 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6485      
6486     cols : false,
6487     rows : 5,
6488     readOnly : false,
6489     warp : 'soft',
6490     resize : false,
6491     value: false,
6492     html: false,
6493     
6494     getAutoCreate : function(){
6495         
6496         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6497         
6498         var id = Roo.id();
6499         
6500         var cfg = {};
6501         
6502         var input =  {
6503             tag: 'textarea',
6504             id : id,
6505             warp : this.warp,
6506             rows : this.rows,
6507             value : this.value || '',
6508             html: this.html || '',
6509             cls : 'form-control',
6510             placeholder : this.placeholder || '' 
6511             
6512         };
6513         
6514         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6515             input.maxLength = this.maxLength;
6516         }
6517         
6518         if(this.resize){
6519             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6520         }
6521         
6522         if(this.cols){
6523             input.cols = this.cols;
6524         }
6525         
6526         if (this.readOnly) {
6527             input.readonly = true;
6528         }
6529         
6530         if (this.name) {
6531             input.name = this.name;
6532         }
6533         
6534         if (this.size) {
6535             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6536         }
6537         
6538         var settings=this;
6539         ['xs','sm','md','lg'].map(function(size){
6540             if (settings[size]) {
6541                 cfg.cls += ' col-' + size + '-' + settings[size];
6542             }
6543         });
6544         
6545         var inputblock = input;
6546         
6547         if (this.before || this.after) {
6548             
6549             inputblock = {
6550                 cls : 'input-group',
6551                 cn :  [] 
6552             };
6553             if (this.before) {
6554                 inputblock.cn.push({
6555                     tag :'span',
6556                     cls : 'input-group-addon',
6557                     html : this.before
6558                 });
6559             }
6560             inputblock.cn.push(input);
6561             if (this.after) {
6562                 inputblock.cn.push({
6563                     tag :'span',
6564                     cls : 'input-group-addon',
6565                     html : this.after
6566                 });
6567             }
6568             
6569         }
6570         
6571         if (align ==='left' && this.fieldLabel.length) {
6572                 Roo.log("left and has label");
6573                 cfg.cn = [
6574                     
6575                     {
6576                         tag: 'label',
6577                         'for' :  id,
6578                         cls : 'control-label col-sm-' + this.labelWidth,
6579                         html : this.fieldLabel
6580                         
6581                     },
6582                     {
6583                         cls : "col-sm-" + (12 - this.labelWidth), 
6584                         cn: [
6585                             inputblock
6586                         ]
6587                     }
6588                     
6589                 ];
6590         } else if ( this.fieldLabel.length) {
6591                 Roo.log(" label");
6592                  cfg.cn = [
6593                    
6594                     {
6595                         tag: 'label',
6596                         //cls : 'input-group-addon',
6597                         html : this.fieldLabel
6598                         
6599                     },
6600                     
6601                     inputblock
6602                     
6603                 ];
6604
6605         } else {
6606             
6607                    Roo.log(" no label && no align");
6608                 cfg.cn = [
6609                     
6610                         inputblock
6611                     
6612                 ];
6613                 
6614                 
6615         }
6616         
6617         if (this.disabled) {
6618             input.disabled=true;
6619         }
6620         
6621         return cfg;
6622         
6623     },
6624     /**
6625      * return the real textarea element.
6626      */
6627     inputEl: function ()
6628     {
6629         return this.el.select('textarea.form-control',true).first();
6630     }
6631 });
6632
6633  
6634 /*
6635  * - LGPL
6636  *
6637  * trigger field - base class for combo..
6638  * 
6639  */
6640  
6641 /**
6642  * @class Roo.bootstrap.TriggerField
6643  * @extends Roo.bootstrap.Input
6644  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6645  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6646  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6647  * for which you can provide a custom implementation.  For example:
6648  * <pre><code>
6649 var trigger = new Roo.bootstrap.TriggerField();
6650 trigger.onTriggerClick = myTriggerFn;
6651 trigger.applyTo('my-field');
6652 </code></pre>
6653  *
6654  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6655  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6656  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6657  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6658  * @constructor
6659  * Create a new TriggerField.
6660  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6661  * to the base TextField)
6662  */
6663 Roo.bootstrap.TriggerField = function(config){
6664     this.mimicing = false;
6665     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6666 };
6667
6668 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6669     /**
6670      * @cfg {String} triggerClass A CSS class to apply to the trigger
6671      */
6672      /**
6673      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6674      */
6675     hideTrigger:false,
6676
6677     /** @cfg {Boolean} grow @hide */
6678     /** @cfg {Number} growMin @hide */
6679     /** @cfg {Number} growMax @hide */
6680
6681     /**
6682      * @hide 
6683      * @method
6684      */
6685     autoSize: Roo.emptyFn,
6686     // private
6687     monitorTab : true,
6688     // private
6689     deferHeight : true,
6690
6691     
6692     actionMode : 'wrap',
6693     
6694     
6695     
6696     getAutoCreate : function(){
6697        
6698         var parent = this.parent();
6699         
6700         var align = this.labelAlign || this.parentLabelAlign();
6701         
6702         var id = Roo.id();
6703         
6704         var cfg = {
6705             cls: 'form-group' //input-group
6706         };
6707         
6708         
6709         var input =  {
6710             tag: 'input',
6711             id : id,
6712             type : this.inputType,
6713             cls : 'form-control',
6714             autocomplete: 'off',
6715             placeholder : this.placeholder || '' 
6716             
6717         };
6718         if (this.name) {
6719             input.name = this.name;
6720         }
6721         if (this.size) {
6722             input.cls += ' input-' + this.size;
6723         }
6724         
6725         if (this.disabled) {
6726             input.disabled=true;
6727         }
6728         
6729         var inputblock = input;
6730         
6731         if (this.before || this.after) {
6732             
6733             inputblock = {
6734                 cls : 'input-group',
6735                 cn :  [] 
6736             };
6737             if (this.before) {
6738                 inputblock.cn.push({
6739                     tag :'span',
6740                     cls : 'input-group-addon',
6741                     html : this.before
6742                 });
6743             }
6744             inputblock.cn.push(input);
6745             if (this.after) {
6746                 inputblock.cn.push({
6747                     tag :'span',
6748                     cls : 'input-group-addon',
6749                     html : this.after
6750                 });
6751             }
6752             
6753         };
6754         
6755         var box = {
6756             tag: 'div',
6757             cn: [
6758                 {
6759                     tag: 'input',
6760                     type : 'hidden',
6761                     cls: 'form-hidden-field'
6762                 },
6763                 inputblock
6764             ]
6765             
6766         };
6767         
6768         if(this.multiple){
6769             Roo.log('multiple');
6770             
6771             box = {
6772                 tag: 'div',
6773                 cn: [
6774                     {
6775                         tag: 'input',
6776                         type : 'hidden',
6777                         cls: 'form-hidden-field'
6778                     },
6779                     {
6780                         tag: 'ul',
6781                         cls: 'select2-choices',
6782                         cn:[
6783                             {
6784                                 tag: 'li',
6785                                 cls: 'select2-search-field',
6786                                 cn: [
6787
6788                                     inputblock
6789                                 ]
6790                             }
6791                         ]
6792                     }
6793                 ]
6794             }
6795         };
6796         
6797         var combobox = {
6798             cls: 'select2-container input-group',
6799             cn: [
6800                 box,
6801                 {
6802                     tag: 'ul',
6803                     cls: 'typeahead typeahead-long dropdown-menu',
6804                     style: 'display:none'
6805                 }
6806             ]
6807         };
6808         
6809         if(!this.multiple){
6810             combobox.cn.push({
6811                 tag :'span',
6812                 cls : 'input-group-addon btn dropdown-toggle',
6813                 cn : [
6814                     {
6815                         tag: 'span',
6816                         cls: 'caret'
6817                     },
6818                     {
6819                         tag: 'span',
6820                         cls: 'combobox-clear',
6821                         cn  : [
6822                             {
6823                                 tag : 'i',
6824                                 cls: 'icon-remove'
6825                             }
6826                         ]
6827                     }
6828                 ]
6829
6830             })
6831         }
6832         
6833         if(this.multiple){
6834             combobox.cls += ' select2-container-multi';
6835         }
6836         
6837         if (align ==='left' && this.fieldLabel.length) {
6838             
6839                 Roo.log("left and has label");
6840                 cfg.cn = [
6841                     
6842                     {
6843                         tag: 'label',
6844                         'for' :  id,
6845                         cls : 'control-label col-sm-' + this.labelWidth,
6846                         html : this.fieldLabel
6847                         
6848                     },
6849                     {
6850                         cls : "col-sm-" + (12 - this.labelWidth), 
6851                         cn: [
6852                             combobox
6853                         ]
6854                     }
6855                     
6856                 ];
6857         } else if ( this.fieldLabel.length) {
6858                 Roo.log(" label");
6859                  cfg.cn = [
6860                    
6861                     {
6862                         tag: 'label',
6863                         //cls : 'input-group-addon',
6864                         html : this.fieldLabel
6865                         
6866                     },
6867                     
6868                     combobox
6869                     
6870                 ];
6871
6872         } else {
6873             
6874                 Roo.log(" no label && no align");
6875                 cfg = combobox
6876                      
6877                 
6878         }
6879          
6880         var settings=this;
6881         ['xs','sm','md','lg'].map(function(size){
6882             if (settings[size]) {
6883                 cfg.cls += ' col-' + size + '-' + settings[size];
6884             }
6885         });
6886         
6887         return cfg;
6888         
6889     },
6890     
6891     
6892     
6893     // private
6894     onResize : function(w, h){
6895 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6896 //        if(typeof w == 'number'){
6897 //            var x = w - this.trigger.getWidth();
6898 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6899 //            this.trigger.setStyle('left', x+'px');
6900 //        }
6901     },
6902
6903     // private
6904     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6905
6906     // private
6907     getResizeEl : function(){
6908         return this.inputEl();
6909     },
6910
6911     // private
6912     getPositionEl : function(){
6913         return this.inputEl();
6914     },
6915
6916     // private
6917     alignErrorIcon : function(){
6918         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6919     },
6920
6921     // private
6922     initEvents : function(){
6923         
6924         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6925         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6926         if(!this.multiple){
6927             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6928             if(this.hideTrigger){
6929                 this.trigger.setDisplayed(false);
6930             }
6931             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6932         }
6933         
6934         if(this.multiple){
6935             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6936         }
6937         
6938         //this.trigger.addClassOnOver('x-form-trigger-over');
6939         //this.trigger.addClassOnClick('x-form-trigger-click');
6940         
6941         //if(!this.width){
6942         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6943         //}
6944     },
6945
6946     // private
6947     initTrigger : function(){
6948        
6949     },
6950
6951     // private
6952     onDestroy : function(){
6953         if(this.trigger){
6954             this.trigger.removeAllListeners();
6955           //  this.trigger.remove();
6956         }
6957         //if(this.wrap){
6958         //    this.wrap.remove();
6959         //}
6960         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6961     },
6962
6963     // private
6964     onFocus : function(){
6965         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6966         /*
6967         if(!this.mimicing){
6968             this.wrap.addClass('x-trigger-wrap-focus');
6969             this.mimicing = true;
6970             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6971             if(this.monitorTab){
6972                 this.el.on("keydown", this.checkTab, this);
6973             }
6974         }
6975         */
6976     },
6977
6978     // private
6979     checkTab : function(e){
6980         if(e.getKey() == e.TAB){
6981             this.triggerBlur();
6982         }
6983     },
6984
6985     // private
6986     onBlur : function(){
6987         // do nothing
6988     },
6989
6990     // private
6991     mimicBlur : function(e, t){
6992         /*
6993         if(!this.wrap.contains(t) && this.validateBlur()){
6994             this.triggerBlur();
6995         }
6996         */
6997     },
6998
6999     // private
7000     triggerBlur : function(){
7001         this.mimicing = false;
7002         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7003         if(this.monitorTab){
7004             this.el.un("keydown", this.checkTab, this);
7005         }
7006         //this.wrap.removeClass('x-trigger-wrap-focus');
7007         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7008     },
7009
7010     // private
7011     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7012     validateBlur : function(e, t){
7013         return true;
7014     },
7015
7016     // private
7017     onDisable : function(){
7018         this.inputEl().dom.disabled = true;
7019         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7020         //if(this.wrap){
7021         //    this.wrap.addClass('x-item-disabled');
7022         //}
7023     },
7024
7025     // private
7026     onEnable : function(){
7027         this.inputEl().dom.disabled = false;
7028         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7029         //if(this.wrap){
7030         //    this.el.removeClass('x-item-disabled');
7031         //}
7032     },
7033
7034     // private
7035     onShow : function(){
7036         var ae = this.getActionEl();
7037         
7038         if(ae){
7039             ae.dom.style.display = '';
7040             ae.dom.style.visibility = 'visible';
7041         }
7042     },
7043
7044     // private
7045     
7046     onHide : function(){
7047         var ae = this.getActionEl();
7048         ae.dom.style.display = 'none';
7049     },
7050
7051     /**
7052      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7053      * by an implementing function.
7054      * @method
7055      * @param {EventObject} e
7056      */
7057     onTriggerClick : Roo.emptyFn
7058 });
7059  /*
7060  * Based on:
7061  * Ext JS Library 1.1.1
7062  * Copyright(c) 2006-2007, Ext JS, LLC.
7063  *
7064  * Originally Released Under LGPL - original licence link has changed is not relivant.
7065  *
7066  * Fork - LGPL
7067  * <script type="text/javascript">
7068  */
7069
7070
7071 /**
7072  * @class Roo.data.SortTypes
7073  * @singleton
7074  * Defines the default sorting (casting?) comparison functions used when sorting data.
7075  */
7076 Roo.data.SortTypes = {
7077     /**
7078      * Default sort that does nothing
7079      * @param {Mixed} s The value being converted
7080      * @return {Mixed} The comparison value
7081      */
7082     none : function(s){
7083         return s;
7084     },
7085     
7086     /**
7087      * The regular expression used to strip tags
7088      * @type {RegExp}
7089      * @property
7090      */
7091     stripTagsRE : /<\/?[^>]+>/gi,
7092     
7093     /**
7094      * Strips all HTML tags to sort on text only
7095      * @param {Mixed} s The value being converted
7096      * @return {String} The comparison value
7097      */
7098     asText : function(s){
7099         return String(s).replace(this.stripTagsRE, "");
7100     },
7101     
7102     /**
7103      * Strips all HTML tags to sort on text only - Case insensitive
7104      * @param {Mixed} s The value being converted
7105      * @return {String} The comparison value
7106      */
7107     asUCText : function(s){
7108         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7109     },
7110     
7111     /**
7112      * Case insensitive string
7113      * @param {Mixed} s The value being converted
7114      * @return {String} The comparison value
7115      */
7116     asUCString : function(s) {
7117         return String(s).toUpperCase();
7118     },
7119     
7120     /**
7121      * Date sorting
7122      * @param {Mixed} s The value being converted
7123      * @return {Number} The comparison value
7124      */
7125     asDate : function(s) {
7126         if(!s){
7127             return 0;
7128         }
7129         if(s instanceof Date){
7130             return s.getTime();
7131         }
7132         return Date.parse(String(s));
7133     },
7134     
7135     /**
7136      * Float sorting
7137      * @param {Mixed} s The value being converted
7138      * @return {Float} The comparison value
7139      */
7140     asFloat : function(s) {
7141         var val = parseFloat(String(s).replace(/,/g, ""));
7142         if(isNaN(val)) val = 0;
7143         return val;
7144     },
7145     
7146     /**
7147      * Integer sorting
7148      * @param {Mixed} s The value being converted
7149      * @return {Number} The comparison value
7150      */
7151     asInt : function(s) {
7152         var val = parseInt(String(s).replace(/,/g, ""));
7153         if(isNaN(val)) val = 0;
7154         return val;
7155     }
7156 };/*
7157  * Based on:
7158  * Ext JS Library 1.1.1
7159  * Copyright(c) 2006-2007, Ext JS, LLC.
7160  *
7161  * Originally Released Under LGPL - original licence link has changed is not relivant.
7162  *
7163  * Fork - LGPL
7164  * <script type="text/javascript">
7165  */
7166
7167 /**
7168 * @class Roo.data.Record
7169  * Instances of this class encapsulate both record <em>definition</em> information, and record
7170  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7171  * to access Records cached in an {@link Roo.data.Store} object.<br>
7172  * <p>
7173  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7174  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7175  * objects.<br>
7176  * <p>
7177  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7178  * @constructor
7179  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7180  * {@link #create}. The parameters are the same.
7181  * @param {Array} data An associative Array of data values keyed by the field name.
7182  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7183  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7184  * not specified an integer id is generated.
7185  */
7186 Roo.data.Record = function(data, id){
7187     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7188     this.data = data;
7189 };
7190
7191 /**
7192  * Generate a constructor for a specific record layout.
7193  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7194  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7195  * Each field definition object may contain the following properties: <ul>
7196  * <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,
7197  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7198  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7199  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7200  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7201  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7202  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7203  * this may be omitted.</p></li>
7204  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7205  * <ul><li>auto (Default, implies no conversion)</li>
7206  * <li>string</li>
7207  * <li>int</li>
7208  * <li>float</li>
7209  * <li>boolean</li>
7210  * <li>date</li></ul></p></li>
7211  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7212  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7213  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7214  * by the Reader into an object that will be stored in the Record. It is passed the
7215  * following parameters:<ul>
7216  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7217  * </ul></p></li>
7218  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7219  * </ul>
7220  * <br>usage:<br><pre><code>
7221 var TopicRecord = Roo.data.Record.create(
7222     {name: 'title', mapping: 'topic_title'},
7223     {name: 'author', mapping: 'username'},
7224     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7225     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7226     {name: 'lastPoster', mapping: 'user2'},
7227     {name: 'excerpt', mapping: 'post_text'}
7228 );
7229
7230 var myNewRecord = new TopicRecord({
7231     title: 'Do my job please',
7232     author: 'noobie',
7233     totalPosts: 1,
7234     lastPost: new Date(),
7235     lastPoster: 'Animal',
7236     excerpt: 'No way dude!'
7237 });
7238 myStore.add(myNewRecord);
7239 </code></pre>
7240  * @method create
7241  * @static
7242  */
7243 Roo.data.Record.create = function(o){
7244     var f = function(){
7245         f.superclass.constructor.apply(this, arguments);
7246     };
7247     Roo.extend(f, Roo.data.Record);
7248     var p = f.prototype;
7249     p.fields = new Roo.util.MixedCollection(false, function(field){
7250         return field.name;
7251     });
7252     for(var i = 0, len = o.length; i < len; i++){
7253         p.fields.add(new Roo.data.Field(o[i]));
7254     }
7255     f.getField = function(name){
7256         return p.fields.get(name);  
7257     };
7258     return f;
7259 };
7260
7261 Roo.data.Record.AUTO_ID = 1000;
7262 Roo.data.Record.EDIT = 'edit';
7263 Roo.data.Record.REJECT = 'reject';
7264 Roo.data.Record.COMMIT = 'commit';
7265
7266 Roo.data.Record.prototype = {
7267     /**
7268      * Readonly flag - true if this record has been modified.
7269      * @type Boolean
7270      */
7271     dirty : false,
7272     editing : false,
7273     error: null,
7274     modified: null,
7275
7276     // private
7277     join : function(store){
7278         this.store = store;
7279     },
7280
7281     /**
7282      * Set the named field to the specified value.
7283      * @param {String} name The name of the field to set.
7284      * @param {Object} value The value to set the field to.
7285      */
7286     set : function(name, value){
7287         if(this.data[name] == value){
7288             return;
7289         }
7290         this.dirty = true;
7291         if(!this.modified){
7292             this.modified = {};
7293         }
7294         if(typeof this.modified[name] == 'undefined'){
7295             this.modified[name] = this.data[name];
7296         }
7297         this.data[name] = value;
7298         if(!this.editing && this.store){
7299             this.store.afterEdit(this);
7300         }       
7301     },
7302
7303     /**
7304      * Get the value of the named field.
7305      * @param {String} name The name of the field to get the value of.
7306      * @return {Object} The value of the field.
7307      */
7308     get : function(name){
7309         return this.data[name]; 
7310     },
7311
7312     // private
7313     beginEdit : function(){
7314         this.editing = true;
7315         this.modified = {}; 
7316     },
7317
7318     // private
7319     cancelEdit : function(){
7320         this.editing = false;
7321         delete this.modified;
7322     },
7323
7324     // private
7325     endEdit : function(){
7326         this.editing = false;
7327         if(this.dirty && this.store){
7328             this.store.afterEdit(this);
7329         }
7330     },
7331
7332     /**
7333      * Usually called by the {@link Roo.data.Store} which owns the Record.
7334      * Rejects all changes made to the Record since either creation, or the last commit operation.
7335      * Modified fields are reverted to their original values.
7336      * <p>
7337      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7338      * of reject operations.
7339      */
7340     reject : function(){
7341         var m = this.modified;
7342         for(var n in m){
7343             if(typeof m[n] != "function"){
7344                 this.data[n] = m[n];
7345             }
7346         }
7347         this.dirty = false;
7348         delete this.modified;
7349         this.editing = false;
7350         if(this.store){
7351             this.store.afterReject(this);
7352         }
7353     },
7354
7355     /**
7356      * Usually called by the {@link Roo.data.Store} which owns the Record.
7357      * Commits all changes made to the Record since either creation, or the last commit operation.
7358      * <p>
7359      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7360      * of commit operations.
7361      */
7362     commit : function(){
7363         this.dirty = false;
7364         delete this.modified;
7365         this.editing = false;
7366         if(this.store){
7367             this.store.afterCommit(this);
7368         }
7369     },
7370
7371     // private
7372     hasError : function(){
7373         return this.error != null;
7374     },
7375
7376     // private
7377     clearError : function(){
7378         this.error = null;
7379     },
7380
7381     /**
7382      * Creates a copy of this record.
7383      * @param {String} id (optional) A new record id if you don't want to use this record's id
7384      * @return {Record}
7385      */
7386     copy : function(newId) {
7387         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7388     }
7389 };/*
7390  * Based on:
7391  * Ext JS Library 1.1.1
7392  * Copyright(c) 2006-2007, Ext JS, LLC.
7393  *
7394  * Originally Released Under LGPL - original licence link has changed is not relivant.
7395  *
7396  * Fork - LGPL
7397  * <script type="text/javascript">
7398  */
7399
7400
7401
7402 /**
7403  * @class Roo.data.Store
7404  * @extends Roo.util.Observable
7405  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7406  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7407  * <p>
7408  * 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
7409  * has no knowledge of the format of the data returned by the Proxy.<br>
7410  * <p>
7411  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7412  * instances from the data object. These records are cached and made available through accessor functions.
7413  * @constructor
7414  * Creates a new Store.
7415  * @param {Object} config A config object containing the objects needed for the Store to access data,
7416  * and read the data into Records.
7417  */
7418 Roo.data.Store = function(config){
7419     this.data = new Roo.util.MixedCollection(false);
7420     this.data.getKey = function(o){
7421         return o.id;
7422     };
7423     this.baseParams = {};
7424     // private
7425     this.paramNames = {
7426         "start" : "start",
7427         "limit" : "limit",
7428         "sort" : "sort",
7429         "dir" : "dir",
7430         "multisort" : "_multisort"
7431     };
7432
7433     if(config && config.data){
7434         this.inlineData = config.data;
7435         delete config.data;
7436     }
7437
7438     Roo.apply(this, config);
7439     
7440     if(this.reader){ // reader passed
7441         this.reader = Roo.factory(this.reader, Roo.data);
7442         this.reader.xmodule = this.xmodule || false;
7443         if(!this.recordType){
7444             this.recordType = this.reader.recordType;
7445         }
7446         if(this.reader.onMetaChange){
7447             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7448         }
7449     }
7450
7451     if(this.recordType){
7452         this.fields = this.recordType.prototype.fields;
7453     }
7454     this.modified = [];
7455
7456     this.addEvents({
7457         /**
7458          * @event datachanged
7459          * Fires when the data cache has changed, and a widget which is using this Store
7460          * as a Record cache should refresh its view.
7461          * @param {Store} this
7462          */
7463         datachanged : true,
7464         /**
7465          * @event metachange
7466          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7467          * @param {Store} this
7468          * @param {Object} meta The JSON metadata
7469          */
7470         metachange : true,
7471         /**
7472          * @event add
7473          * Fires when Records have been added to the Store
7474          * @param {Store} this
7475          * @param {Roo.data.Record[]} records The array of Records added
7476          * @param {Number} index The index at which the record(s) were added
7477          */
7478         add : true,
7479         /**
7480          * @event remove
7481          * Fires when a Record has been removed from the Store
7482          * @param {Store} this
7483          * @param {Roo.data.Record} record The Record that was removed
7484          * @param {Number} index The index at which the record was removed
7485          */
7486         remove : true,
7487         /**
7488          * @event update
7489          * Fires when a Record has been updated
7490          * @param {Store} this
7491          * @param {Roo.data.Record} record The Record that was updated
7492          * @param {String} operation The update operation being performed.  Value may be one of:
7493          * <pre><code>
7494  Roo.data.Record.EDIT
7495  Roo.data.Record.REJECT
7496  Roo.data.Record.COMMIT
7497          * </code></pre>
7498          */
7499         update : true,
7500         /**
7501          * @event clear
7502          * Fires when the data cache has been cleared.
7503          * @param {Store} this
7504          */
7505         clear : true,
7506         /**
7507          * @event beforeload
7508          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7509          * the load action will be canceled.
7510          * @param {Store} this
7511          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7512          */
7513         beforeload : true,
7514         /**
7515          * @event beforeloadadd
7516          * Fires after a new set of Records has been loaded.
7517          * @param {Store} this
7518          * @param {Roo.data.Record[]} records The Records that were loaded
7519          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7520          */
7521         beforeloadadd : true,
7522         /**
7523          * @event load
7524          * Fires after a new set of Records has been loaded, before they are added to the store.
7525          * @param {Store} this
7526          * @param {Roo.data.Record[]} records The Records that were loaded
7527          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7528          * @params {Object} return from reader
7529          */
7530         load : true,
7531         /**
7532          * @event loadexception
7533          * Fires if an exception occurs in the Proxy during loading.
7534          * Called with the signature of the Proxy's "loadexception" event.
7535          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7536          * 
7537          * @param {Proxy} 
7538          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7539          * @param {Object} load options 
7540          * @param {Object} jsonData from your request (normally this contains the Exception)
7541          */
7542         loadexception : true
7543     });
7544     
7545     if(this.proxy){
7546         this.proxy = Roo.factory(this.proxy, Roo.data);
7547         this.proxy.xmodule = this.xmodule || false;
7548         this.relayEvents(this.proxy,  ["loadexception"]);
7549     }
7550     this.sortToggle = {};
7551     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7552
7553     Roo.data.Store.superclass.constructor.call(this);
7554
7555     if(this.inlineData){
7556         this.loadData(this.inlineData);
7557         delete this.inlineData;
7558     }
7559 };
7560
7561 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7562      /**
7563     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7564     * without a remote query - used by combo/forms at present.
7565     */
7566     
7567     /**
7568     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7569     */
7570     /**
7571     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7572     */
7573     /**
7574     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7575     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7576     */
7577     /**
7578     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7579     * on any HTTP request
7580     */
7581     /**
7582     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7583     */
7584     /**
7585     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7586     */
7587     multiSort: false,
7588     /**
7589     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7590     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7591     */
7592     remoteSort : false,
7593
7594     /**
7595     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7596      * loaded or when a record is removed. (defaults to false).
7597     */
7598     pruneModifiedRecords : false,
7599
7600     // private
7601     lastOptions : null,
7602
7603     /**
7604      * Add Records to the Store and fires the add event.
7605      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7606      */
7607     add : function(records){
7608         records = [].concat(records);
7609         for(var i = 0, len = records.length; i < len; i++){
7610             records[i].join(this);
7611         }
7612         var index = this.data.length;
7613         this.data.addAll(records);
7614         this.fireEvent("add", this, records, index);
7615     },
7616
7617     /**
7618      * Remove a Record from the Store and fires the remove event.
7619      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7620      */
7621     remove : function(record){
7622         var index = this.data.indexOf(record);
7623         this.data.removeAt(index);
7624         if(this.pruneModifiedRecords){
7625             this.modified.remove(record);
7626         }
7627         this.fireEvent("remove", this, record, index);
7628     },
7629
7630     /**
7631      * Remove all Records from the Store and fires the clear event.
7632      */
7633     removeAll : function(){
7634         this.data.clear();
7635         if(this.pruneModifiedRecords){
7636             this.modified = [];
7637         }
7638         this.fireEvent("clear", this);
7639     },
7640
7641     /**
7642      * Inserts Records to the Store at the given index and fires the add event.
7643      * @param {Number} index The start index at which to insert the passed Records.
7644      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7645      */
7646     insert : function(index, records){
7647         records = [].concat(records);
7648         for(var i = 0, len = records.length; i < len; i++){
7649             this.data.insert(index, records[i]);
7650             records[i].join(this);
7651         }
7652         this.fireEvent("add", this, records, index);
7653     },
7654
7655     /**
7656      * Get the index within the cache of the passed Record.
7657      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7658      * @return {Number} The index of the passed Record. Returns -1 if not found.
7659      */
7660     indexOf : function(record){
7661         return this.data.indexOf(record);
7662     },
7663
7664     /**
7665      * Get the index within the cache of the Record with the passed id.
7666      * @param {String} id The id of the Record to find.
7667      * @return {Number} The index of the Record. Returns -1 if not found.
7668      */
7669     indexOfId : function(id){
7670         return this.data.indexOfKey(id);
7671     },
7672
7673     /**
7674      * Get the Record with the specified id.
7675      * @param {String} id The id of the Record to find.
7676      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7677      */
7678     getById : function(id){
7679         return this.data.key(id);
7680     },
7681
7682     /**
7683      * Get the Record at the specified index.
7684      * @param {Number} index The index of the Record to find.
7685      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7686      */
7687     getAt : function(index){
7688         return this.data.itemAt(index);
7689     },
7690
7691     /**
7692      * Returns a range of Records between specified indices.
7693      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7694      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7695      * @return {Roo.data.Record[]} An array of Records
7696      */
7697     getRange : function(start, end){
7698         return this.data.getRange(start, end);
7699     },
7700
7701     // private
7702     storeOptions : function(o){
7703         o = Roo.apply({}, o);
7704         delete o.callback;
7705         delete o.scope;
7706         this.lastOptions = o;
7707     },
7708
7709     /**
7710      * Loads the Record cache from the configured Proxy using the configured Reader.
7711      * <p>
7712      * If using remote paging, then the first load call must specify the <em>start</em>
7713      * and <em>limit</em> properties in the options.params property to establish the initial
7714      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7715      * <p>
7716      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7717      * and this call will return before the new data has been loaded. Perform any post-processing
7718      * in a callback function, or in a "load" event handler.</strong>
7719      * <p>
7720      * @param {Object} options An object containing properties which control loading options:<ul>
7721      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7722      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7723      * passed the following arguments:<ul>
7724      * <li>r : Roo.data.Record[]</li>
7725      * <li>options: Options object from the load call</li>
7726      * <li>success: Boolean success indicator</li></ul></li>
7727      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7728      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7729      * </ul>
7730      */
7731     load : function(options){
7732         options = options || {};
7733         if(this.fireEvent("beforeload", this, options) !== false){
7734             this.storeOptions(options);
7735             var p = Roo.apply(options.params || {}, this.baseParams);
7736             // if meta was not loaded from remote source.. try requesting it.
7737             if (!this.reader.metaFromRemote) {
7738                 p._requestMeta = 1;
7739             }
7740             if(this.sortInfo && this.remoteSort){
7741                 var pn = this.paramNames;
7742                 p[pn["sort"]] = this.sortInfo.field;
7743                 p[pn["dir"]] = this.sortInfo.direction;
7744             }
7745             if (this.multiSort) {
7746                 var pn = this.paramNames;
7747                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7748             }
7749             
7750             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7751         }
7752     },
7753
7754     /**
7755      * Reloads the Record cache from the configured Proxy using the configured Reader and
7756      * the options from the last load operation performed.
7757      * @param {Object} options (optional) An object containing properties which may override the options
7758      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7759      * the most recently used options are reused).
7760      */
7761     reload : function(options){
7762         this.load(Roo.applyIf(options||{}, this.lastOptions));
7763     },
7764
7765     // private
7766     // Called as a callback by the Reader during a load operation.
7767     loadRecords : function(o, options, success){
7768         if(!o || success === false){
7769             if(success !== false){
7770                 this.fireEvent("load", this, [], options, o);
7771             }
7772             if(options.callback){
7773                 options.callback.call(options.scope || this, [], options, false);
7774             }
7775             return;
7776         }
7777         // if data returned failure - throw an exception.
7778         if (o.success === false) {
7779             // show a message if no listener is registered.
7780             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7781                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7782             }
7783             // loadmask wil be hooked into this..
7784             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7785             return;
7786         }
7787         var r = o.records, t = o.totalRecords || r.length;
7788         
7789         this.fireEvent("beforeloadadd", this, r, options, o);
7790         
7791         if(!options || options.add !== true){
7792             if(this.pruneModifiedRecords){
7793                 this.modified = [];
7794             }
7795             for(var i = 0, len = r.length; i < len; i++){
7796                 r[i].join(this);
7797             }
7798             if(this.snapshot){
7799                 this.data = this.snapshot;
7800                 delete this.snapshot;
7801             }
7802             this.data.clear();
7803             this.data.addAll(r);
7804             this.totalLength = t;
7805             this.applySort();
7806             this.fireEvent("datachanged", this);
7807         }else{
7808             this.totalLength = Math.max(t, this.data.length+r.length);
7809             this.add(r);
7810         }
7811         this.fireEvent("load", this, r, options, o);
7812         if(options.callback){
7813             options.callback.call(options.scope || this, r, options, true);
7814         }
7815     },
7816
7817
7818     /**
7819      * Loads data from a passed data block. A Reader which understands the format of the data
7820      * must have been configured in the constructor.
7821      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7822      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7823      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7824      */
7825     loadData : function(o, append){
7826         var r = this.reader.readRecords(o);
7827         this.loadRecords(r, {add: append}, true);
7828     },
7829
7830     /**
7831      * Gets the number of cached records.
7832      * <p>
7833      * <em>If using paging, this may not be the total size of the dataset. If the data object
7834      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7835      * the data set size</em>
7836      */
7837     getCount : function(){
7838         return this.data.length || 0;
7839     },
7840
7841     /**
7842      * Gets the total number of records in the dataset as returned by the server.
7843      * <p>
7844      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7845      * the dataset size</em>
7846      */
7847     getTotalCount : function(){
7848         return this.totalLength || 0;
7849     },
7850
7851     /**
7852      * Returns the sort state of the Store as an object with two properties:
7853      * <pre><code>
7854  field {String} The name of the field by which the Records are sorted
7855  direction {String} The sort order, "ASC" or "DESC"
7856      * </code></pre>
7857      */
7858     getSortState : function(){
7859         return this.sortInfo;
7860     },
7861
7862     // private
7863     applySort : function(){
7864         if(this.sortInfo && !this.remoteSort){
7865             var s = this.sortInfo, f = s.field;
7866             var st = this.fields.get(f).sortType;
7867             var fn = function(r1, r2){
7868                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7869                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7870             };
7871             this.data.sort(s.direction, fn);
7872             if(this.snapshot && this.snapshot != this.data){
7873                 this.snapshot.sort(s.direction, fn);
7874             }
7875         }
7876     },
7877
7878     /**
7879      * Sets the default sort column and order to be used by the next load operation.
7880      * @param {String} fieldName The name of the field to sort by.
7881      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7882      */
7883     setDefaultSort : function(field, dir){
7884         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7885     },
7886
7887     /**
7888      * Sort the Records.
7889      * If remote sorting is used, the sort is performed on the server, and the cache is
7890      * reloaded. If local sorting is used, the cache is sorted internally.
7891      * @param {String} fieldName The name of the field to sort by.
7892      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7893      */
7894     sort : function(fieldName, dir){
7895         var f = this.fields.get(fieldName);
7896         if(!dir){
7897             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7898             
7899             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7900                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7901             }else{
7902                 dir = f.sortDir;
7903             }
7904         }
7905         this.sortToggle[f.name] = dir;
7906         this.sortInfo = {field: f.name, direction: dir};
7907         if(!this.remoteSort){
7908             this.applySort();
7909             this.fireEvent("datachanged", this);
7910         }else{
7911             this.load(this.lastOptions);
7912         }
7913     },
7914
7915     /**
7916      * Calls the specified function for each of the Records in the cache.
7917      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7918      * Returning <em>false</em> aborts and exits the iteration.
7919      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7920      */
7921     each : function(fn, scope){
7922         this.data.each(fn, scope);
7923     },
7924
7925     /**
7926      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7927      * (e.g., during paging).
7928      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7929      */
7930     getModifiedRecords : function(){
7931         return this.modified;
7932     },
7933
7934     // private
7935     createFilterFn : function(property, value, anyMatch){
7936         if(!value.exec){ // not a regex
7937             value = String(value);
7938             if(value.length == 0){
7939                 return false;
7940             }
7941             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7942         }
7943         return function(r){
7944             return value.test(r.data[property]);
7945         };
7946     },
7947
7948     /**
7949      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7950      * @param {String} property A field on your records
7951      * @param {Number} start The record index to start at (defaults to 0)
7952      * @param {Number} end The last record index to include (defaults to length - 1)
7953      * @return {Number} The sum
7954      */
7955     sum : function(property, start, end){
7956         var rs = this.data.items, v = 0;
7957         start = start || 0;
7958         end = (end || end === 0) ? end : rs.length-1;
7959
7960         for(var i = start; i <= end; i++){
7961             v += (rs[i].data[property] || 0);
7962         }
7963         return v;
7964     },
7965
7966     /**
7967      * Filter the records by a specified property.
7968      * @param {String} field A field on your records
7969      * @param {String/RegExp} value Either a string that the field
7970      * should start with or a RegExp to test against the field
7971      * @param {Boolean} anyMatch True to match any part not just the beginning
7972      */
7973     filter : function(property, value, anyMatch){
7974         var fn = this.createFilterFn(property, value, anyMatch);
7975         return fn ? this.filterBy(fn) : this.clearFilter();
7976     },
7977
7978     /**
7979      * Filter by a function. The specified function will be called with each
7980      * record in this data source. If the function returns true the record is included,
7981      * otherwise it is filtered.
7982      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7983      * @param {Object} scope (optional) The scope of the function (defaults to this)
7984      */
7985     filterBy : function(fn, scope){
7986         this.snapshot = this.snapshot || this.data;
7987         this.data = this.queryBy(fn, scope||this);
7988         this.fireEvent("datachanged", this);
7989     },
7990
7991     /**
7992      * Query the records by a specified property.
7993      * @param {String} field A field on your records
7994      * @param {String/RegExp} value Either a string that the field
7995      * should start with or a RegExp to test against the field
7996      * @param {Boolean} anyMatch True to match any part not just the beginning
7997      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7998      */
7999     query : function(property, value, anyMatch){
8000         var fn = this.createFilterFn(property, value, anyMatch);
8001         return fn ? this.queryBy(fn) : this.data.clone();
8002     },
8003
8004     /**
8005      * Query by a function. The specified function will be called with each
8006      * record in this data source. If the function returns true the record is included
8007      * in the results.
8008      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8009      * @param {Object} scope (optional) The scope of the function (defaults to this)
8010       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8011      **/
8012     queryBy : function(fn, scope){
8013         var data = this.snapshot || this.data;
8014         return data.filterBy(fn, scope||this);
8015     },
8016
8017     /**
8018      * Collects unique values for a particular dataIndex from this store.
8019      * @param {String} dataIndex The property to collect
8020      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8021      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8022      * @return {Array} An array of the unique values
8023      **/
8024     collect : function(dataIndex, allowNull, bypassFilter){
8025         var d = (bypassFilter === true && this.snapshot) ?
8026                 this.snapshot.items : this.data.items;
8027         var v, sv, r = [], l = {};
8028         for(var i = 0, len = d.length; i < len; i++){
8029             v = d[i].data[dataIndex];
8030             sv = String(v);
8031             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8032                 l[sv] = true;
8033                 r[r.length] = v;
8034             }
8035         }
8036         return r;
8037     },
8038
8039     /**
8040      * Revert to a view of the Record cache with no filtering applied.
8041      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8042      */
8043     clearFilter : function(suppressEvent){
8044         if(this.snapshot && this.snapshot != this.data){
8045             this.data = this.snapshot;
8046             delete this.snapshot;
8047             if(suppressEvent !== true){
8048                 this.fireEvent("datachanged", this);
8049             }
8050         }
8051     },
8052
8053     // private
8054     afterEdit : function(record){
8055         if(this.modified.indexOf(record) == -1){
8056             this.modified.push(record);
8057         }
8058         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8059     },
8060     
8061     // private
8062     afterReject : function(record){
8063         this.modified.remove(record);
8064         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8065     },
8066
8067     // private
8068     afterCommit : function(record){
8069         this.modified.remove(record);
8070         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8071     },
8072
8073     /**
8074      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8075      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8076      */
8077     commitChanges : function(){
8078         var m = this.modified.slice(0);
8079         this.modified = [];
8080         for(var i = 0, len = m.length; i < len; i++){
8081             m[i].commit();
8082         }
8083     },
8084
8085     /**
8086      * Cancel outstanding changes on all changed records.
8087      */
8088     rejectChanges : function(){
8089         var m = this.modified.slice(0);
8090         this.modified = [];
8091         for(var i = 0, len = m.length; i < len; i++){
8092             m[i].reject();
8093         }
8094     },
8095
8096     onMetaChange : function(meta, rtype, o){
8097         this.recordType = rtype;
8098         this.fields = rtype.prototype.fields;
8099         delete this.snapshot;
8100         this.sortInfo = meta.sortInfo || this.sortInfo;
8101         this.modified = [];
8102         this.fireEvent('metachange', this, this.reader.meta);
8103     },
8104     
8105     moveIndex : function(data, type)
8106     {
8107         var index = this.indexOf(data);
8108         
8109         var newIndex = index + type;
8110         
8111         this.remove(data);
8112         
8113         this.insert(newIndex, data);
8114         
8115     }
8116 });/*
8117  * Based on:
8118  * Ext JS Library 1.1.1
8119  * Copyright(c) 2006-2007, Ext JS, LLC.
8120  *
8121  * Originally Released Under LGPL - original licence link has changed is not relivant.
8122  *
8123  * Fork - LGPL
8124  * <script type="text/javascript">
8125  */
8126
8127 /**
8128  * @class Roo.data.SimpleStore
8129  * @extends Roo.data.Store
8130  * Small helper class to make creating Stores from Array data easier.
8131  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8132  * @cfg {Array} fields An array of field definition objects, or field name strings.
8133  * @cfg {Array} data The multi-dimensional array of data
8134  * @constructor
8135  * @param {Object} config
8136  */
8137 Roo.data.SimpleStore = function(config){
8138     Roo.data.SimpleStore.superclass.constructor.call(this, {
8139         isLocal : true,
8140         reader: new Roo.data.ArrayReader({
8141                 id: config.id
8142             },
8143             Roo.data.Record.create(config.fields)
8144         ),
8145         proxy : new Roo.data.MemoryProxy(config.data)
8146     });
8147     this.load();
8148 };
8149 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8150  * Based on:
8151  * Ext JS Library 1.1.1
8152  * Copyright(c) 2006-2007, Ext JS, LLC.
8153  *
8154  * Originally Released Under LGPL - original licence link has changed is not relivant.
8155  *
8156  * Fork - LGPL
8157  * <script type="text/javascript">
8158  */
8159
8160 /**
8161 /**
8162  * @extends Roo.data.Store
8163  * @class Roo.data.JsonStore
8164  * Small helper class to make creating Stores for JSON data easier. <br/>
8165 <pre><code>
8166 var store = new Roo.data.JsonStore({
8167     url: 'get-images.php',
8168     root: 'images',
8169     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8170 });
8171 </code></pre>
8172  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8173  * JsonReader and HttpProxy (unless inline data is provided).</b>
8174  * @cfg {Array} fields An array of field definition objects, or field name strings.
8175  * @constructor
8176  * @param {Object} config
8177  */
8178 Roo.data.JsonStore = function(c){
8179     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8180         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8181         reader: new Roo.data.JsonReader(c, c.fields)
8182     }));
8183 };
8184 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8185  * Based on:
8186  * Ext JS Library 1.1.1
8187  * Copyright(c) 2006-2007, Ext JS, LLC.
8188  *
8189  * Originally Released Under LGPL - original licence link has changed is not relivant.
8190  *
8191  * Fork - LGPL
8192  * <script type="text/javascript">
8193  */
8194
8195  
8196 Roo.data.Field = function(config){
8197     if(typeof config == "string"){
8198         config = {name: config};
8199     }
8200     Roo.apply(this, config);
8201     
8202     if(!this.type){
8203         this.type = "auto";
8204     }
8205     
8206     var st = Roo.data.SortTypes;
8207     // named sortTypes are supported, here we look them up
8208     if(typeof this.sortType == "string"){
8209         this.sortType = st[this.sortType];
8210     }
8211     
8212     // set default sortType for strings and dates
8213     if(!this.sortType){
8214         switch(this.type){
8215             case "string":
8216                 this.sortType = st.asUCString;
8217                 break;
8218             case "date":
8219                 this.sortType = st.asDate;
8220                 break;
8221             default:
8222                 this.sortType = st.none;
8223         }
8224     }
8225
8226     // define once
8227     var stripRe = /[\$,%]/g;
8228
8229     // prebuilt conversion function for this field, instead of
8230     // switching every time we're reading a value
8231     if(!this.convert){
8232         var cv, dateFormat = this.dateFormat;
8233         switch(this.type){
8234             case "":
8235             case "auto":
8236             case undefined:
8237                 cv = function(v){ return v; };
8238                 break;
8239             case "string":
8240                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8241                 break;
8242             case "int":
8243                 cv = function(v){
8244                     return v !== undefined && v !== null && v !== '' ?
8245                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8246                     };
8247                 break;
8248             case "float":
8249                 cv = function(v){
8250                     return v !== undefined && v !== null && v !== '' ?
8251                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8252                     };
8253                 break;
8254             case "bool":
8255             case "boolean":
8256                 cv = function(v){ return v === true || v === "true" || v == 1; };
8257                 break;
8258             case "date":
8259                 cv = function(v){
8260                     if(!v){
8261                         return '';
8262                     }
8263                     if(v instanceof Date){
8264                         return v;
8265                     }
8266                     if(dateFormat){
8267                         if(dateFormat == "timestamp"){
8268                             return new Date(v*1000);
8269                         }
8270                         return Date.parseDate(v, dateFormat);
8271                     }
8272                     var parsed = Date.parse(v);
8273                     return parsed ? new Date(parsed) : null;
8274                 };
8275              break;
8276             
8277         }
8278         this.convert = cv;
8279     }
8280 };
8281
8282 Roo.data.Field.prototype = {
8283     dateFormat: null,
8284     defaultValue: "",
8285     mapping: null,
8286     sortType : null,
8287     sortDir : "ASC"
8288 };/*
8289  * Based on:
8290  * Ext JS Library 1.1.1
8291  * Copyright(c) 2006-2007, Ext JS, LLC.
8292  *
8293  * Originally Released Under LGPL - original licence link has changed is not relivant.
8294  *
8295  * Fork - LGPL
8296  * <script type="text/javascript">
8297  */
8298  
8299 // Base class for reading structured data from a data source.  This class is intended to be
8300 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8301
8302 /**
8303  * @class Roo.data.DataReader
8304  * Base class for reading structured data from a data source.  This class is intended to be
8305  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8306  */
8307
8308 Roo.data.DataReader = function(meta, recordType){
8309     
8310     this.meta = meta;
8311     
8312     this.recordType = recordType instanceof Array ? 
8313         Roo.data.Record.create(recordType) : recordType;
8314 };
8315
8316 Roo.data.DataReader.prototype = {
8317      /**
8318      * Create an empty record
8319      * @param {Object} data (optional) - overlay some values
8320      * @return {Roo.data.Record} record created.
8321      */
8322     newRow :  function(d) {
8323         var da =  {};
8324         this.recordType.prototype.fields.each(function(c) {
8325             switch( c.type) {
8326                 case 'int' : da[c.name] = 0; break;
8327                 case 'date' : da[c.name] = new Date(); break;
8328                 case 'float' : da[c.name] = 0.0; break;
8329                 case 'boolean' : da[c.name] = false; break;
8330                 default : da[c.name] = ""; break;
8331             }
8332             
8333         });
8334         return new this.recordType(Roo.apply(da, d));
8335     }
8336     
8337 };/*
8338  * Based on:
8339  * Ext JS Library 1.1.1
8340  * Copyright(c) 2006-2007, Ext JS, LLC.
8341  *
8342  * Originally Released Under LGPL - original licence link has changed is not relivant.
8343  *
8344  * Fork - LGPL
8345  * <script type="text/javascript">
8346  */
8347
8348 /**
8349  * @class Roo.data.DataProxy
8350  * @extends Roo.data.Observable
8351  * This class is an abstract base class for implementations which provide retrieval of
8352  * unformatted data objects.<br>
8353  * <p>
8354  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8355  * (of the appropriate type which knows how to parse the data object) to provide a block of
8356  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8357  * <p>
8358  * Custom implementations must implement the load method as described in
8359  * {@link Roo.data.HttpProxy#load}.
8360  */
8361 Roo.data.DataProxy = function(){
8362     this.addEvents({
8363         /**
8364          * @event beforeload
8365          * Fires before a network request is made to retrieve a data object.
8366          * @param {Object} This DataProxy object.
8367          * @param {Object} params The params parameter to the load function.
8368          */
8369         beforeload : true,
8370         /**
8371          * @event load
8372          * Fires before the load method's callback is called.
8373          * @param {Object} This DataProxy object.
8374          * @param {Object} o The data object.
8375          * @param {Object} arg The callback argument object passed to the load function.
8376          */
8377         load : true,
8378         /**
8379          * @event loadexception
8380          * Fires if an Exception occurs during data retrieval.
8381          * @param {Object} This DataProxy object.
8382          * @param {Object} o The data object.
8383          * @param {Object} arg The callback argument object passed to the load function.
8384          * @param {Object} e The Exception.
8385          */
8386         loadexception : true
8387     });
8388     Roo.data.DataProxy.superclass.constructor.call(this);
8389 };
8390
8391 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8392
8393     /**
8394      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8395      */
8396 /*
8397  * Based on:
8398  * Ext JS Library 1.1.1
8399  * Copyright(c) 2006-2007, Ext JS, LLC.
8400  *
8401  * Originally Released Under LGPL - original licence link has changed is not relivant.
8402  *
8403  * Fork - LGPL
8404  * <script type="text/javascript">
8405  */
8406 /**
8407  * @class Roo.data.MemoryProxy
8408  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8409  * to the Reader when its load method is called.
8410  * @constructor
8411  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8412  */
8413 Roo.data.MemoryProxy = function(data){
8414     if (data.data) {
8415         data = data.data;
8416     }
8417     Roo.data.MemoryProxy.superclass.constructor.call(this);
8418     this.data = data;
8419 };
8420
8421 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8422     /**
8423      * Load data from the requested source (in this case an in-memory
8424      * data object passed to the constructor), read the data object into
8425      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8426      * process that block using the passed callback.
8427      * @param {Object} params This parameter is not used by the MemoryProxy class.
8428      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8429      * object into a block of Roo.data.Records.
8430      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8431      * The function must be passed <ul>
8432      * <li>The Record block object</li>
8433      * <li>The "arg" argument from the load function</li>
8434      * <li>A boolean success indicator</li>
8435      * </ul>
8436      * @param {Object} scope The scope in which to call the callback
8437      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8438      */
8439     load : function(params, reader, callback, scope, arg){
8440         params = params || {};
8441         var result;
8442         try {
8443             result = reader.readRecords(this.data);
8444         }catch(e){
8445             this.fireEvent("loadexception", this, arg, null, e);
8446             callback.call(scope, null, arg, false);
8447             return;
8448         }
8449         callback.call(scope, result, arg, true);
8450     },
8451     
8452     // private
8453     update : function(params, records){
8454         
8455     }
8456 });/*
8457  * Based on:
8458  * Ext JS Library 1.1.1
8459  * Copyright(c) 2006-2007, Ext JS, LLC.
8460  *
8461  * Originally Released Under LGPL - original licence link has changed is not relivant.
8462  *
8463  * Fork - LGPL
8464  * <script type="text/javascript">
8465  */
8466 /**
8467  * @class Roo.data.HttpProxy
8468  * @extends Roo.data.DataProxy
8469  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8470  * configured to reference a certain URL.<br><br>
8471  * <p>
8472  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8473  * from which the running page was served.<br><br>
8474  * <p>
8475  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8476  * <p>
8477  * Be aware that to enable the browser to parse an XML document, the server must set
8478  * the Content-Type header in the HTTP response to "text/xml".
8479  * @constructor
8480  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8481  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8482  * will be used to make the request.
8483  */
8484 Roo.data.HttpProxy = function(conn){
8485     Roo.data.HttpProxy.superclass.constructor.call(this);
8486     // is conn a conn config or a real conn?
8487     this.conn = conn;
8488     this.useAjax = !conn || !conn.events;
8489   
8490 };
8491
8492 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8493     // thse are take from connection...
8494     
8495     /**
8496      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8497      */
8498     /**
8499      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8500      * extra parameters to each request made by this object. (defaults to undefined)
8501      */
8502     /**
8503      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8504      *  to each request made by this object. (defaults to undefined)
8505      */
8506     /**
8507      * @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)
8508      */
8509     /**
8510      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8511      */
8512      /**
8513      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8514      * @type Boolean
8515      */
8516   
8517
8518     /**
8519      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8520      * @type Boolean
8521      */
8522     /**
8523      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8524      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8525      * a finer-grained basis than the DataProxy events.
8526      */
8527     getConnection : function(){
8528         return this.useAjax ? Roo.Ajax : this.conn;
8529     },
8530
8531     /**
8532      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8533      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8534      * process that block using the passed callback.
8535      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8536      * for the request to the remote server.
8537      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8538      * object into a block of Roo.data.Records.
8539      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8540      * The function must be passed <ul>
8541      * <li>The Record block object</li>
8542      * <li>The "arg" argument from the load function</li>
8543      * <li>A boolean success indicator</li>
8544      * </ul>
8545      * @param {Object} scope The scope in which to call the callback
8546      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8547      */
8548     load : function(params, reader, callback, scope, arg){
8549         if(this.fireEvent("beforeload", this, params) !== false){
8550             var  o = {
8551                 params : params || {},
8552                 request: {
8553                     callback : callback,
8554                     scope : scope,
8555                     arg : arg
8556                 },
8557                 reader: reader,
8558                 callback : this.loadResponse,
8559                 scope: this
8560             };
8561             if(this.useAjax){
8562                 Roo.applyIf(o, this.conn);
8563                 if(this.activeRequest){
8564                     Roo.Ajax.abort(this.activeRequest);
8565                 }
8566                 this.activeRequest = Roo.Ajax.request(o);
8567             }else{
8568                 this.conn.request(o);
8569             }
8570         }else{
8571             callback.call(scope||this, null, arg, false);
8572         }
8573     },
8574
8575     // private
8576     loadResponse : function(o, success, response){
8577         delete this.activeRequest;
8578         if(!success){
8579             this.fireEvent("loadexception", this, o, response);
8580             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8581             return;
8582         }
8583         var result;
8584         try {
8585             result = o.reader.read(response);
8586         }catch(e){
8587             this.fireEvent("loadexception", this, o, response, e);
8588             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8589             return;
8590         }
8591         
8592         this.fireEvent("load", this, o, o.request.arg);
8593         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8594     },
8595
8596     // private
8597     update : function(dataSet){
8598
8599     },
8600
8601     // private
8602     updateResponse : function(dataSet){
8603
8604     }
8605 });/*
8606  * Based on:
8607  * Ext JS Library 1.1.1
8608  * Copyright(c) 2006-2007, Ext JS, LLC.
8609  *
8610  * Originally Released Under LGPL - original licence link has changed is not relivant.
8611  *
8612  * Fork - LGPL
8613  * <script type="text/javascript">
8614  */
8615
8616 /**
8617  * @class Roo.data.ScriptTagProxy
8618  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8619  * other than the originating domain of the running page.<br><br>
8620  * <p>
8621  * <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
8622  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8623  * <p>
8624  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8625  * source code that is used as the source inside a &lt;script> tag.<br><br>
8626  * <p>
8627  * In order for the browser to process the returned data, the server must wrap the data object
8628  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8629  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8630  * depending on whether the callback name was passed:
8631  * <p>
8632  * <pre><code>
8633 boolean scriptTag = false;
8634 String cb = request.getParameter("callback");
8635 if (cb != null) {
8636     scriptTag = true;
8637     response.setContentType("text/javascript");
8638 } else {
8639     response.setContentType("application/x-json");
8640 }
8641 Writer out = response.getWriter();
8642 if (scriptTag) {
8643     out.write(cb + "(");
8644 }
8645 out.print(dataBlock.toJsonString());
8646 if (scriptTag) {
8647     out.write(");");
8648 }
8649 </pre></code>
8650  *
8651  * @constructor
8652  * @param {Object} config A configuration object.
8653  */
8654 Roo.data.ScriptTagProxy = function(config){
8655     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8656     Roo.apply(this, config);
8657     this.head = document.getElementsByTagName("head")[0];
8658 };
8659
8660 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8661
8662 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8663     /**
8664      * @cfg {String} url The URL from which to request the data object.
8665      */
8666     /**
8667      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8668      */
8669     timeout : 30000,
8670     /**
8671      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8672      * the server the name of the callback function set up by the load call to process the returned data object.
8673      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8674      * javascript output which calls this named function passing the data object as its only parameter.
8675      */
8676     callbackParam : "callback",
8677     /**
8678      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8679      * name to the request.
8680      */
8681     nocache : true,
8682
8683     /**
8684      * Load data from the configured URL, read the data object into
8685      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8686      * process that block using the passed callback.
8687      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8688      * for the request to the remote server.
8689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8690      * object into a block of Roo.data.Records.
8691      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8692      * The function must be passed <ul>
8693      * <li>The Record block object</li>
8694      * <li>The "arg" argument from the load function</li>
8695      * <li>A boolean success indicator</li>
8696      * </ul>
8697      * @param {Object} scope The scope in which to call the callback
8698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8699      */
8700     load : function(params, reader, callback, scope, arg){
8701         if(this.fireEvent("beforeload", this, params) !== false){
8702
8703             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8704
8705             var url = this.url;
8706             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8707             if(this.nocache){
8708                 url += "&_dc=" + (new Date().getTime());
8709             }
8710             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8711             var trans = {
8712                 id : transId,
8713                 cb : "stcCallback"+transId,
8714                 scriptId : "stcScript"+transId,
8715                 params : params,
8716                 arg : arg,
8717                 url : url,
8718                 callback : callback,
8719                 scope : scope,
8720                 reader : reader
8721             };
8722             var conn = this;
8723
8724             window[trans.cb] = function(o){
8725                 conn.handleResponse(o, trans);
8726             };
8727
8728             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8729
8730             if(this.autoAbort !== false){
8731                 this.abort();
8732             }
8733
8734             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8735
8736             var script = document.createElement("script");
8737             script.setAttribute("src", url);
8738             script.setAttribute("type", "text/javascript");
8739             script.setAttribute("id", trans.scriptId);
8740             this.head.appendChild(script);
8741
8742             this.trans = trans;
8743         }else{
8744             callback.call(scope||this, null, arg, false);
8745         }
8746     },
8747
8748     // private
8749     isLoading : function(){
8750         return this.trans ? true : false;
8751     },
8752
8753     /**
8754      * Abort the current server request.
8755      */
8756     abort : function(){
8757         if(this.isLoading()){
8758             this.destroyTrans(this.trans);
8759         }
8760     },
8761
8762     // private
8763     destroyTrans : function(trans, isLoaded){
8764         this.head.removeChild(document.getElementById(trans.scriptId));
8765         clearTimeout(trans.timeoutId);
8766         if(isLoaded){
8767             window[trans.cb] = undefined;
8768             try{
8769                 delete window[trans.cb];
8770             }catch(e){}
8771         }else{
8772             // if hasn't been loaded, wait for load to remove it to prevent script error
8773             window[trans.cb] = function(){
8774                 window[trans.cb] = undefined;
8775                 try{
8776                     delete window[trans.cb];
8777                 }catch(e){}
8778             };
8779         }
8780     },
8781
8782     // private
8783     handleResponse : function(o, trans){
8784         this.trans = false;
8785         this.destroyTrans(trans, true);
8786         var result;
8787         try {
8788             result = trans.reader.readRecords(o);
8789         }catch(e){
8790             this.fireEvent("loadexception", this, o, trans.arg, e);
8791             trans.callback.call(trans.scope||window, null, trans.arg, false);
8792             return;
8793         }
8794         this.fireEvent("load", this, o, trans.arg);
8795         trans.callback.call(trans.scope||window, result, trans.arg, true);
8796     },
8797
8798     // private
8799     handleFailure : function(trans){
8800         this.trans = false;
8801         this.destroyTrans(trans, false);
8802         this.fireEvent("loadexception", this, null, trans.arg);
8803         trans.callback.call(trans.scope||window, null, trans.arg, false);
8804     }
8805 });/*
8806  * Based on:
8807  * Ext JS Library 1.1.1
8808  * Copyright(c) 2006-2007, Ext JS, LLC.
8809  *
8810  * Originally Released Under LGPL - original licence link has changed is not relivant.
8811  *
8812  * Fork - LGPL
8813  * <script type="text/javascript">
8814  */
8815
8816 /**
8817  * @class Roo.data.JsonReader
8818  * @extends Roo.data.DataReader
8819  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8820  * based on mappings in a provided Roo.data.Record constructor.
8821  * 
8822  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8823  * in the reply previously. 
8824  * 
8825  * <p>
8826  * Example code:
8827  * <pre><code>
8828 var RecordDef = Roo.data.Record.create([
8829     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8830     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8831 ]);
8832 var myReader = new Roo.data.JsonReader({
8833     totalProperty: "results",    // The property which contains the total dataset size (optional)
8834     root: "rows",                // The property which contains an Array of row objects
8835     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8836 }, RecordDef);
8837 </code></pre>
8838  * <p>
8839  * This would consume a JSON file like this:
8840  * <pre><code>
8841 { 'results': 2, 'rows': [
8842     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8843     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8844 }
8845 </code></pre>
8846  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8847  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8848  * paged from the remote server.
8849  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8850  * @cfg {String} root name of the property which contains the Array of row objects.
8851  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8852  * @constructor
8853  * Create a new JsonReader
8854  * @param {Object} meta Metadata configuration options
8855  * @param {Object} recordType Either an Array of field definition objects,
8856  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8857  */
8858 Roo.data.JsonReader = function(meta, recordType){
8859     
8860     meta = meta || {};
8861     // set some defaults:
8862     Roo.applyIf(meta, {
8863         totalProperty: 'total',
8864         successProperty : 'success',
8865         root : 'data',
8866         id : 'id'
8867     });
8868     
8869     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8870 };
8871 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8872     
8873     /**
8874      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8875      * Used by Store query builder to append _requestMeta to params.
8876      * 
8877      */
8878     metaFromRemote : false,
8879     /**
8880      * This method is only used by a DataProxy which has retrieved data from a remote server.
8881      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8882      * @return {Object} data A data block which is used by an Roo.data.Store object as
8883      * a cache of Roo.data.Records.
8884      */
8885     read : function(response){
8886         var json = response.responseText;
8887        
8888         var o = /* eval:var:o */ eval("("+json+")");
8889         if(!o) {
8890             throw {message: "JsonReader.read: Json object not found"};
8891         }
8892         
8893         if(o.metaData){
8894             
8895             delete this.ef;
8896             this.metaFromRemote = true;
8897             this.meta = o.metaData;
8898             this.recordType = Roo.data.Record.create(o.metaData.fields);
8899             this.onMetaChange(this.meta, this.recordType, o);
8900         }
8901         return this.readRecords(o);
8902     },
8903
8904     // private function a store will implement
8905     onMetaChange : function(meta, recordType, o){
8906
8907     },
8908
8909     /**
8910          * @ignore
8911          */
8912     simpleAccess: function(obj, subsc) {
8913         return obj[subsc];
8914     },
8915
8916         /**
8917          * @ignore
8918          */
8919     getJsonAccessor: function(){
8920         var re = /[\[\.]/;
8921         return function(expr) {
8922             try {
8923                 return(re.test(expr))
8924                     ? new Function("obj", "return obj." + expr)
8925                     : function(obj){
8926                         return obj[expr];
8927                     };
8928             } catch(e){}
8929             return Roo.emptyFn;
8930         };
8931     }(),
8932
8933     /**
8934      * Create a data block containing Roo.data.Records from an XML document.
8935      * @param {Object} o An object which contains an Array of row objects in the property specified
8936      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8937      * which contains the total size of the dataset.
8938      * @return {Object} data A data block which is used by an Roo.data.Store object as
8939      * a cache of Roo.data.Records.
8940      */
8941     readRecords : function(o){
8942         /**
8943          * After any data loads, the raw JSON data is available for further custom processing.
8944          * @type Object
8945          */
8946         this.o = o;
8947         var s = this.meta, Record = this.recordType,
8948             f = Record.prototype.fields, fi = f.items, fl = f.length;
8949
8950 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8951         if (!this.ef) {
8952             if(s.totalProperty) {
8953                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8954                 }
8955                 if(s.successProperty) {
8956                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8957                 }
8958                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8959                 if (s.id) {
8960                         var g = this.getJsonAccessor(s.id);
8961                         this.getId = function(rec) {
8962                                 var r = g(rec);
8963                                 return (r === undefined || r === "") ? null : r;
8964                         };
8965                 } else {
8966                         this.getId = function(){return null;};
8967                 }
8968             this.ef = [];
8969             for(var jj = 0; jj < fl; jj++){
8970                 f = fi[jj];
8971                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8972                 this.ef[jj] = this.getJsonAccessor(map);
8973             }
8974         }
8975
8976         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8977         if(s.totalProperty){
8978             var vt = parseInt(this.getTotal(o), 10);
8979             if(!isNaN(vt)){
8980                 totalRecords = vt;
8981             }
8982         }
8983         if(s.successProperty){
8984             var vs = this.getSuccess(o);
8985             if(vs === false || vs === 'false'){
8986                 success = false;
8987             }
8988         }
8989         var records = [];
8990             for(var i = 0; i < c; i++){
8991                     var n = root[i];
8992                 var values = {};
8993                 var id = this.getId(n);
8994                 for(var j = 0; j < fl; j++){
8995                     f = fi[j];
8996                 var v = this.ef[j](n);
8997                 if (!f.convert) {
8998                     Roo.log('missing convert for ' + f.name);
8999                     Roo.log(f);
9000                     continue;
9001                 }
9002                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9003                 }
9004                 var record = new Record(values, id);
9005                 record.json = n;
9006                 records[i] = record;
9007             }
9008             return {
9009             raw : o,
9010                 success : success,
9011                 records : records,
9012                 totalRecords : totalRecords
9013             };
9014     }
9015 });/*
9016  * Based on:
9017  * Ext JS Library 1.1.1
9018  * Copyright(c) 2006-2007, Ext JS, LLC.
9019  *
9020  * Originally Released Under LGPL - original licence link has changed is not relivant.
9021  *
9022  * Fork - LGPL
9023  * <script type="text/javascript">
9024  */
9025
9026 /**
9027  * @class Roo.data.ArrayReader
9028  * @extends Roo.data.DataReader
9029  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9030  * Each element of that Array represents a row of data fields. The
9031  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9032  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9033  * <p>
9034  * Example code:.
9035  * <pre><code>
9036 var RecordDef = Roo.data.Record.create([
9037     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9038     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9039 ]);
9040 var myReader = new Roo.data.ArrayReader({
9041     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9042 }, RecordDef);
9043 </code></pre>
9044  * <p>
9045  * This would consume an Array like this:
9046  * <pre><code>
9047 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9048   </code></pre>
9049  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9050  * @constructor
9051  * Create a new JsonReader
9052  * @param {Object} meta Metadata configuration options.
9053  * @param {Object} recordType Either an Array of field definition objects
9054  * as specified to {@link Roo.data.Record#create},
9055  * or an {@link Roo.data.Record} object
9056  * created using {@link Roo.data.Record#create}.
9057  */
9058 Roo.data.ArrayReader = function(meta, recordType){
9059     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9060 };
9061
9062 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9063     /**
9064      * Create a data block containing Roo.data.Records from an XML document.
9065      * @param {Object} o An Array of row objects which represents the dataset.
9066      * @return {Object} data A data block which is used by an Roo.data.Store object as
9067      * a cache of Roo.data.Records.
9068      */
9069     readRecords : function(o){
9070         var sid = this.meta ? this.meta.id : null;
9071         var recordType = this.recordType, fields = recordType.prototype.fields;
9072         var records = [];
9073         var root = o;
9074             for(var i = 0; i < root.length; i++){
9075                     var n = root[i];
9076                 var values = {};
9077                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9078                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9079                 var f = fields.items[j];
9080                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9081                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9082                 v = f.convert(v);
9083                 values[f.name] = v;
9084             }
9085                 var record = new recordType(values, id);
9086                 record.json = n;
9087                 records[records.length] = record;
9088             }
9089             return {
9090                 records : records,
9091                 totalRecords : records.length
9092             };
9093     }
9094 });/*
9095  * - LGPL
9096  * * 
9097  */
9098
9099 /**
9100  * @class Roo.bootstrap.ComboBox
9101  * @extends Roo.bootstrap.TriggerField
9102  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9103  * @cfg {Boolean} append (true|false) default false
9104  * @constructor
9105  * Create a new ComboBox.
9106  * @param {Object} config Configuration options
9107  */
9108 Roo.bootstrap.ComboBox = function(config){
9109     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9110     this.addEvents({
9111         /**
9112          * @event expand
9113          * Fires when the dropdown list is expanded
9114              * @param {Roo.bootstrap.ComboBox} combo This combo box
9115              */
9116         'expand' : true,
9117         /**
9118          * @event collapse
9119          * Fires when the dropdown list is collapsed
9120              * @param {Roo.bootstrap.ComboBox} combo This combo box
9121              */
9122         'collapse' : true,
9123         /**
9124          * @event beforeselect
9125          * Fires before a list item is selected. Return false to cancel the selection.
9126              * @param {Roo.bootstrap.ComboBox} combo This combo box
9127              * @param {Roo.data.Record} record The data record returned from the underlying store
9128              * @param {Number} index The index of the selected item in the dropdown list
9129              */
9130         'beforeselect' : true,
9131         /**
9132          * @event select
9133          * Fires when a list item is selected
9134              * @param {Roo.bootstrap.ComboBox} combo This combo box
9135              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9136              * @param {Number} index The index of the selected item in the dropdown list
9137              */
9138         'select' : true,
9139         /**
9140          * @event beforequery
9141          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9142          * The event object passed has these properties:
9143              * @param {Roo.bootstrap.ComboBox} combo This combo box
9144              * @param {String} query The query
9145              * @param {Boolean} forceAll true to force "all" query
9146              * @param {Boolean} cancel true to cancel the query
9147              * @param {Object} e The query event object
9148              */
9149         'beforequery': true,
9150          /**
9151          * @event add
9152          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9153              * @param {Roo.bootstrap.ComboBox} combo This combo box
9154              */
9155         'add' : true,
9156         /**
9157          * @event edit
9158          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9159              * @param {Roo.bootstrap.ComboBox} combo This combo box
9160              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9161              */
9162         'edit' : true,
9163         /**
9164          * @event remove
9165          * Fires when the remove value from the combobox array
9166              * @param {Roo.bootstrap.ComboBox} combo This combo box
9167              */
9168         'remove' : true
9169         
9170     });
9171     
9172     
9173     this.selectedIndex = -1;
9174     if(this.mode == 'local'){
9175         if(config.queryDelay === undefined){
9176             this.queryDelay = 10;
9177         }
9178         if(config.minChars === undefined){
9179             this.minChars = 0;
9180         }
9181     }
9182 };
9183
9184 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9185      
9186     /**
9187      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9188      * rendering into an Roo.Editor, defaults to false)
9189      */
9190     /**
9191      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9192      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9193      */
9194     /**
9195      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9196      */
9197     /**
9198      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9199      * the dropdown list (defaults to undefined, with no header element)
9200      */
9201
9202      /**
9203      * @cfg {String/Roo.Template} tpl The template to use to render the output
9204      */
9205      
9206      /**
9207      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9208      */
9209     listWidth: undefined,
9210     /**
9211      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9212      * mode = 'remote' or 'text' if mode = 'local')
9213      */
9214     displayField: undefined,
9215     /**
9216      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9217      * mode = 'remote' or 'value' if mode = 'local'). 
9218      * Note: use of a valueField requires the user make a selection
9219      * in order for a value to be mapped.
9220      */
9221     valueField: undefined,
9222     
9223     
9224     /**
9225      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9226      * field's data value (defaults to the underlying DOM element's name)
9227      */
9228     hiddenName: undefined,
9229     /**
9230      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9231      */
9232     listClass: '',
9233     /**
9234      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9235      */
9236     selectedClass: 'active',
9237     
9238     /**
9239      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9240      */
9241     shadow:'sides',
9242     /**
9243      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9244      * anchor positions (defaults to 'tl-bl')
9245      */
9246     listAlign: 'tl-bl?',
9247     /**
9248      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9249      */
9250     maxHeight: 300,
9251     /**
9252      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9253      * query specified by the allQuery config option (defaults to 'query')
9254      */
9255     triggerAction: 'query',
9256     /**
9257      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9258      * (defaults to 4, does not apply if editable = false)
9259      */
9260     minChars : 4,
9261     /**
9262      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9263      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9264      */
9265     typeAhead: false,
9266     /**
9267      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9268      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9269      */
9270     queryDelay: 500,
9271     /**
9272      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9273      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9274      */
9275     pageSize: 0,
9276     /**
9277      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9278      * when editable = true (defaults to false)
9279      */
9280     selectOnFocus:false,
9281     /**
9282      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9283      */
9284     queryParam: 'query',
9285     /**
9286      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9287      * when mode = 'remote' (defaults to 'Loading...')
9288      */
9289     loadingText: 'Loading...',
9290     /**
9291      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9292      */
9293     resizable: false,
9294     /**
9295      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9296      */
9297     handleHeight : 8,
9298     /**
9299      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9300      * traditional select (defaults to true)
9301      */
9302     editable: true,
9303     /**
9304      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9305      */
9306     allQuery: '',
9307     /**
9308      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9309      */
9310     mode: 'remote',
9311     /**
9312      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9313      * listWidth has a higher value)
9314      */
9315     minListWidth : 70,
9316     /**
9317      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9318      * allow the user to set arbitrary text into the field (defaults to false)
9319      */
9320     forceSelection:false,
9321     /**
9322      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9323      * if typeAhead = true (defaults to 250)
9324      */
9325     typeAheadDelay : 250,
9326     /**
9327      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9328      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9329      */
9330     valueNotFoundText : undefined,
9331     /**
9332      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9333      */
9334     blockFocus : false,
9335     
9336     /**
9337      * @cfg {Boolean} disableClear Disable showing of clear button.
9338      */
9339     disableClear : false,
9340     /**
9341      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9342      */
9343     alwaysQuery : false,
9344     
9345     /**
9346      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9347      */
9348     multiple : false,
9349     
9350     //private
9351     addicon : false,
9352     editicon: false,
9353     
9354     page: 0,
9355     hasQuery: false,
9356     append: false,
9357     loadNext: false,
9358     item: [],
9359     
9360     // element that contains real text value.. (when hidden is used..)
9361      
9362     // private
9363     initEvents: function(){
9364         
9365         if (!this.store) {
9366             throw "can not find store for combo";
9367         }
9368         this.store = Roo.factory(this.store, Roo.data);
9369         
9370         
9371         
9372         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9373         
9374         
9375         if(this.hiddenName){
9376             
9377             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9378             
9379             this.hiddenField.dom.value =
9380                 this.hiddenValue !== undefined ? this.hiddenValue :
9381                 this.value !== undefined ? this.value : '';
9382
9383             // prevent input submission
9384             this.el.dom.removeAttribute('name');
9385             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9386              
9387              
9388         }
9389         //if(Roo.isGecko){
9390         //    this.el.dom.setAttribute('autocomplete', 'off');
9391         //}
9392
9393         var cls = 'x-combo-list';
9394         this.list = this.el.select('ul.dropdown-menu',true).first();
9395
9396         //this.list = new Roo.Layer({
9397         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9398         //});
9399         
9400         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9401         this.list.setWidth(lw);
9402         
9403         this.list.on('mouseover', this.onViewOver, this);
9404         this.list.on('mousemove', this.onViewMove, this);
9405         
9406         this.list.on('scroll', this.onViewScroll, this);
9407         
9408         /*
9409         this.list.swallowEvent('mousewheel');
9410         this.assetHeight = 0;
9411
9412         if(this.title){
9413             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9414             this.assetHeight += this.header.getHeight();
9415         }
9416
9417         this.innerList = this.list.createChild({cls:cls+'-inner'});
9418         this.innerList.on('mouseover', this.onViewOver, this);
9419         this.innerList.on('mousemove', this.onViewMove, this);
9420         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9421         
9422         if(this.allowBlank && !this.pageSize && !this.disableClear){
9423             this.footer = this.list.createChild({cls:cls+'-ft'});
9424             this.pageTb = new Roo.Toolbar(this.footer);
9425            
9426         }
9427         if(this.pageSize){
9428             this.footer = this.list.createChild({cls:cls+'-ft'});
9429             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9430                     {pageSize: this.pageSize});
9431             
9432         }
9433         
9434         if (this.pageTb && this.allowBlank && !this.disableClear) {
9435             var _this = this;
9436             this.pageTb.add(new Roo.Toolbar.Fill(), {
9437                 cls: 'x-btn-icon x-btn-clear',
9438                 text: '&#160;',
9439                 handler: function()
9440                 {
9441                     _this.collapse();
9442                     _this.clearValue();
9443                     _this.onSelect(false, -1);
9444                 }
9445             });
9446         }
9447         if (this.footer) {
9448             this.assetHeight += this.footer.getHeight();
9449         }
9450         */
9451             
9452         if(!this.tpl){
9453             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9454         }
9455
9456         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9457             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9458         });
9459         //this.view.wrapEl.setDisplayed(false);
9460         this.view.on('click', this.onViewClick, this);
9461         
9462         
9463         
9464         this.store.on('beforeload', this.onBeforeLoad, this);
9465         this.store.on('load', this.onLoad, this);
9466         this.store.on('loadexception', this.onLoadException, this);
9467         /*
9468         if(this.resizable){
9469             this.resizer = new Roo.Resizable(this.list,  {
9470                pinned:true, handles:'se'
9471             });
9472             this.resizer.on('resize', function(r, w, h){
9473                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9474                 this.listWidth = w;
9475                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9476                 this.restrictHeight();
9477             }, this);
9478             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9479         }
9480         */
9481         if(!this.editable){
9482             this.editable = true;
9483             this.setEditable(false);
9484         }
9485         
9486         /*
9487         
9488         if (typeof(this.events.add.listeners) != 'undefined') {
9489             
9490             this.addicon = this.wrap.createChild(
9491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9492        
9493             this.addicon.on('click', function(e) {
9494                 this.fireEvent('add', this);
9495             }, this);
9496         }
9497         if (typeof(this.events.edit.listeners) != 'undefined') {
9498             
9499             this.editicon = this.wrap.createChild(
9500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9501             if (this.addicon) {
9502                 this.editicon.setStyle('margin-left', '40px');
9503             }
9504             this.editicon.on('click', function(e) {
9505                 
9506                 // we fire even  if inothing is selected..
9507                 this.fireEvent('edit', this, this.lastData );
9508                 
9509             }, this);
9510         }
9511         */
9512         
9513         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9514             "up" : function(e){
9515                 this.inKeyMode = true;
9516                 this.selectPrev();
9517             },
9518
9519             "down" : function(e){
9520                 if(!this.isExpanded()){
9521                     this.onTriggerClick();
9522                 }else{
9523                     this.inKeyMode = true;
9524                     this.selectNext();
9525                 }
9526             },
9527
9528             "enter" : function(e){
9529                 this.onViewClick();
9530                 //return true;
9531             },
9532
9533             "esc" : function(e){
9534                 this.collapse();
9535             },
9536
9537             "tab" : function(e){
9538                 this.collapse();
9539                 
9540                 if(this.fireEvent("specialkey", this, e)){
9541                     this.onViewClick(false);
9542                 }
9543                 
9544                 return true;
9545             },
9546
9547             scope : this,
9548
9549             doRelay : function(foo, bar, hname){
9550                 if(hname == 'down' || this.scope.isExpanded()){
9551                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9552                 }
9553                 return true;
9554             },
9555
9556             forceKeyDown: true
9557         });
9558         
9559         
9560         this.queryDelay = Math.max(this.queryDelay || 10,
9561                 this.mode == 'local' ? 10 : 250);
9562         
9563         
9564         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9565         
9566         if(this.typeAhead){
9567             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9568         }
9569         if(this.editable !== false){
9570             this.inputEl().on("keyup", this.onKeyUp, this);
9571         }
9572         if(this.forceSelection){
9573             this.inputEl().on('blur', this.doForce, this);
9574         }
9575         
9576         if(this.multiple){
9577             this.choices = this.el.select('ul.select2-choices', true).first();
9578             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9579         }
9580     },
9581
9582     onDestroy : function(){
9583         if(this.view){
9584             this.view.setStore(null);
9585             this.view.el.removeAllListeners();
9586             this.view.el.remove();
9587             this.view.purgeListeners();
9588         }
9589         if(this.list){
9590             this.list.dom.innerHTML  = '';
9591         }
9592         if(this.store){
9593             this.store.un('beforeload', this.onBeforeLoad, this);
9594             this.store.un('load', this.onLoad, this);
9595             this.store.un('loadexception', this.onLoadException, this);
9596         }
9597         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9598     },
9599
9600     // private
9601     fireKey : function(e){
9602         if(e.isNavKeyPress() && !this.list.isVisible()){
9603             this.fireEvent("specialkey", this, e);
9604         }
9605     },
9606
9607     // private
9608     onResize: function(w, h){
9609 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9610 //        
9611 //        if(typeof w != 'number'){
9612 //            // we do not handle it!?!?
9613 //            return;
9614 //        }
9615 //        var tw = this.trigger.getWidth();
9616 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9617 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9618 //        var x = w - tw;
9619 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9620 //            
9621 //        //this.trigger.setStyle('left', x+'px');
9622 //        
9623 //        if(this.list && this.listWidth === undefined){
9624 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9625 //            this.list.setWidth(lw);
9626 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9627 //        }
9628         
9629     
9630         
9631     },
9632
9633     /**
9634      * Allow or prevent the user from directly editing the field text.  If false is passed,
9635      * the user will only be able to select from the items defined in the dropdown list.  This method
9636      * is the runtime equivalent of setting the 'editable' config option at config time.
9637      * @param {Boolean} value True to allow the user to directly edit the field text
9638      */
9639     setEditable : function(value){
9640         if(value == this.editable){
9641             return;
9642         }
9643         this.editable = value;
9644         if(!value){
9645             this.inputEl().dom.setAttribute('readOnly', true);
9646             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9647             this.inputEl().addClass('x-combo-noedit');
9648         }else{
9649             this.inputEl().dom.setAttribute('readOnly', false);
9650             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9651             this.inputEl().removeClass('x-combo-noedit');
9652         }
9653     },
9654
9655     // private
9656     
9657     onBeforeLoad : function(combo,opts){
9658         if(!this.hasFocus){
9659             return;
9660         }
9661          if (!opts.add) {
9662             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9663          }
9664         this.restrictHeight();
9665         this.selectedIndex = -1;
9666     },
9667
9668     // private
9669     onLoad : function(){
9670         
9671         this.hasQuery = false;
9672         
9673         if(!this.hasFocus){
9674             return;
9675         }
9676         
9677         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9678             this.loading.hide();
9679         }
9680         
9681         if(this.store.getCount() > 0){
9682             this.expand();
9683             this.restrictHeight();
9684             if(this.lastQuery == this.allQuery){
9685                 if(this.editable){
9686                     this.inputEl().dom.select();
9687                 }
9688                 if(!this.selectByValue(this.value, true)){
9689                     this.select(0, true);
9690                 }
9691             }else{
9692                 this.selectNext();
9693                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9694                     this.taTask.delay(this.typeAheadDelay);
9695                 }
9696             }
9697         }else{
9698             this.onEmptyResults();
9699         }
9700         
9701         //this.el.focus();
9702     },
9703     // private
9704     onLoadException : function()
9705     {
9706         this.hasQuery = false;
9707         
9708         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9709             this.loading.hide();
9710         }
9711         
9712         this.collapse();
9713         Roo.log(this.store.reader.jsonData);
9714         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9715             // fixme
9716             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9717         }
9718         
9719         
9720     },
9721     // private
9722     onTypeAhead : function(){
9723         if(this.store.getCount() > 0){
9724             var r = this.store.getAt(0);
9725             var newValue = r.data[this.displayField];
9726             var len = newValue.length;
9727             var selStart = this.getRawValue().length;
9728             
9729             if(selStart != len){
9730                 this.setRawValue(newValue);
9731                 this.selectText(selStart, newValue.length);
9732             }
9733         }
9734     },
9735
9736     // private
9737     onSelect : function(record, index){
9738         
9739         if(this.fireEvent('beforeselect', this, record, index) !== false){
9740         
9741             this.setFromData(index > -1 ? record.data : false);
9742             
9743             this.collapse();
9744             this.fireEvent('select', this, record, index);
9745         }
9746     },
9747
9748     /**
9749      * Returns the currently selected field value or empty string if no value is set.
9750      * @return {String} value The selected value
9751      */
9752     getValue : function(){
9753         
9754         if(this.multiple){
9755             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9756         }
9757         
9758         if(this.valueField){
9759             return typeof this.value != 'undefined' ? this.value : '';
9760         }else{
9761             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9762         }
9763     },
9764
9765     /**
9766      * Clears any text/value currently set in the field
9767      */
9768     clearValue : function(){
9769         if(this.hiddenField){
9770             this.hiddenField.dom.value = '';
9771         }
9772         this.value = '';
9773         this.setRawValue('');
9774         this.lastSelectionText = '';
9775         
9776     },
9777
9778     /**
9779      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9780      * will be displayed in the field.  If the value does not match the data value of an existing item,
9781      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9782      * Otherwise the field will be blank (although the value will still be set).
9783      * @param {String} value The value to match
9784      */
9785     setValue : function(v){
9786         if(this.multiple){
9787             this.syncValue();
9788             return;
9789         }
9790         
9791         var text = v;
9792         if(this.valueField){
9793             var r = this.findRecord(this.valueField, v);
9794             if(r){
9795                 text = r.data[this.displayField];
9796             }else if(this.valueNotFoundText !== undefined){
9797                 text = this.valueNotFoundText;
9798             }
9799         }
9800         this.lastSelectionText = text;
9801         if(this.hiddenField){
9802             this.hiddenField.dom.value = v;
9803         }
9804         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9805         this.value = v;
9806     },
9807     /**
9808      * @property {Object} the last set data for the element
9809      */
9810     
9811     lastData : false,
9812     /**
9813      * Sets the value of the field based on a object which is related to the record format for the store.
9814      * @param {Object} value the value to set as. or false on reset?
9815      */
9816     setFromData : function(o){
9817         
9818         if(this.multiple){
9819             this.addItem(o);
9820             return;
9821         }
9822             
9823         var dv = ''; // display value
9824         var vv = ''; // value value..
9825         this.lastData = o;
9826         if (this.displayField) {
9827             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9828         } else {
9829             // this is an error condition!!!
9830             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9831         }
9832         
9833         if(this.valueField){
9834             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9835         }
9836         
9837         if(this.hiddenField){
9838             this.hiddenField.dom.value = vv;
9839             
9840             this.lastSelectionText = dv;
9841             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9842             this.value = vv;
9843             return;
9844         }
9845         // no hidden field.. - we store the value in 'value', but still display
9846         // display field!!!!
9847         this.lastSelectionText = dv;
9848         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9849         this.value = vv;
9850         
9851         
9852     },
9853     // private
9854     reset : function(){
9855         // overridden so that last data is reset..
9856         this.setValue(this.originalValue);
9857         this.clearInvalid();
9858         this.lastData = false;
9859         if (this.view) {
9860             this.view.clearSelections();
9861         }
9862     },
9863     // private
9864     findRecord : function(prop, value){
9865         var record;
9866         if(this.store.getCount() > 0){
9867             this.store.each(function(r){
9868                 if(r.data[prop] == value){
9869                     record = r;
9870                     return false;
9871                 }
9872                 return true;
9873             });
9874         }
9875         return record;
9876     },
9877     
9878     getName: function()
9879     {
9880         // returns hidden if it's set..
9881         if (!this.rendered) {return ''};
9882         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9883         
9884     },
9885     // private
9886     onViewMove : function(e, t){
9887         this.inKeyMode = false;
9888     },
9889
9890     // private
9891     onViewOver : function(e, t){
9892         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9893             return;
9894         }
9895         var item = this.view.findItemFromChild(t);
9896         if(item){
9897             var index = this.view.indexOf(item);
9898             this.select(index, false);
9899         }
9900     },
9901
9902     // private
9903     onViewClick : function(doFocus)
9904     {
9905         var index = this.view.getSelectedIndexes()[0];
9906         var r = this.store.getAt(index);
9907         if(r){
9908             this.onSelect(r, index);
9909         }
9910         if(doFocus !== false && !this.blockFocus){
9911             this.inputEl().focus();
9912         }
9913     },
9914
9915     // private
9916     restrictHeight : function(){
9917         //this.innerList.dom.style.height = '';
9918         //var inner = this.innerList.dom;
9919         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9920         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9921         //this.list.beginUpdate();
9922         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9923         this.list.alignTo(this.inputEl(), this.listAlign);
9924         //this.list.endUpdate();
9925     },
9926
9927     // private
9928     onEmptyResults : function(){
9929         this.collapse();
9930     },
9931
9932     /**
9933      * Returns true if the dropdown list is expanded, else false.
9934      */
9935     isExpanded : function(){
9936         return this.list.isVisible();
9937     },
9938
9939     /**
9940      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9941      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9942      * @param {String} value The data value of the item to select
9943      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9944      * selected item if it is not currently in view (defaults to true)
9945      * @return {Boolean} True if the value matched an item in the list, else false
9946      */
9947     selectByValue : function(v, scrollIntoView){
9948         if(v !== undefined && v !== null){
9949             var r = this.findRecord(this.valueField || this.displayField, v);
9950             if(r){
9951                 this.select(this.store.indexOf(r), scrollIntoView);
9952                 return true;
9953             }
9954         }
9955         return false;
9956     },
9957
9958     /**
9959      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9960      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9961      * @param {Number} index The zero-based index of the list item to select
9962      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9963      * selected item if it is not currently in view (defaults to true)
9964      */
9965     select : function(index, scrollIntoView){
9966         this.selectedIndex = index;
9967         this.view.select(index);
9968         if(scrollIntoView !== false){
9969             var el = this.view.getNode(index);
9970             if(el){
9971                 //this.innerList.scrollChildIntoView(el, false);
9972                 
9973             }
9974         }
9975     },
9976
9977     // private
9978     selectNext : function(){
9979         var ct = this.store.getCount();
9980         if(ct > 0){
9981             if(this.selectedIndex == -1){
9982                 this.select(0);
9983             }else if(this.selectedIndex < ct-1){
9984                 this.select(this.selectedIndex+1);
9985             }
9986         }
9987     },
9988
9989     // private
9990     selectPrev : function(){
9991         var ct = this.store.getCount();
9992         if(ct > 0){
9993             if(this.selectedIndex == -1){
9994                 this.select(0);
9995             }else if(this.selectedIndex != 0){
9996                 this.select(this.selectedIndex-1);
9997             }
9998         }
9999     },
10000
10001     // private
10002     onKeyUp : function(e){
10003         if(this.editable !== false && !e.isSpecialKey()){
10004             this.lastKey = e.getKey();
10005             this.dqTask.delay(this.queryDelay);
10006         }
10007     },
10008
10009     // private
10010     validateBlur : function(){
10011         return !this.list || !this.list.isVisible();   
10012     },
10013
10014     // private
10015     initQuery : function(){
10016         this.doQuery(this.getRawValue());
10017     },
10018
10019     // private
10020     doForce : function(){
10021         if(this.inputEl().dom.value.length > 0){
10022             this.inputEl().dom.value =
10023                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10024              
10025         }
10026     },
10027
10028     /**
10029      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10030      * query allowing the query action to be canceled if needed.
10031      * @param {String} query The SQL query to execute
10032      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10033      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10034      * saved in the current store (defaults to false)
10035      */
10036     doQuery : function(q, forceAll){
10037         
10038         if(q === undefined || q === null){
10039             q = '';
10040         }
10041         var qe = {
10042             query: q,
10043             forceAll: forceAll,
10044             combo: this,
10045             cancel:false
10046         };
10047         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10048             return false;
10049         }
10050         q = qe.query;
10051         
10052         forceAll = qe.forceAll;
10053         if(forceAll === true || (q.length >= this.minChars)){
10054             
10055             this.hasQuery = true;
10056             
10057             if(this.lastQuery != q || this.alwaysQuery){
10058                 this.lastQuery = q;
10059                 if(this.mode == 'local'){
10060                     this.selectedIndex = -1;
10061                     if(forceAll){
10062                         this.store.clearFilter();
10063                     }else{
10064                         this.store.filter(this.displayField, q);
10065                     }
10066                     this.onLoad();
10067                 }else{
10068                     this.store.baseParams[this.queryParam] = q;
10069                     
10070                     var options = {params : this.getParams(q)};
10071                     
10072                     if(this.loadNext){
10073                         options.add = true;
10074                         options.params.start = this.page * this.pageSize;
10075                     }
10076                     
10077                     this.store.load(options);
10078                     this.expand();
10079                 }
10080             }else{
10081                 this.selectedIndex = -1;
10082                 this.onLoad();   
10083             }
10084         }
10085         
10086         this.loadNext = false;
10087     },
10088
10089     // private
10090     getParams : function(q){
10091         var p = {};
10092         //p[this.queryParam] = q;
10093         
10094         if(this.pageSize){
10095             p.start = 0;
10096             p.limit = this.pageSize;
10097         }
10098         return p;
10099     },
10100
10101     /**
10102      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10103      */
10104     collapse : function(){
10105         if(!this.isExpanded()){
10106             return;
10107         }
10108         
10109         this.list.hide();
10110         Roo.get(document).un('mousedown', this.collapseIf, this);
10111         Roo.get(document).un('mousewheel', this.collapseIf, this);
10112         if (!this.editable) {
10113             Roo.get(document).un('keydown', this.listKeyPress, this);
10114         }
10115         this.fireEvent('collapse', this);
10116     },
10117
10118     // private
10119     collapseIf : function(e){
10120         var in_combo  = e.within(this.el);
10121         var in_list =  e.within(this.list);
10122         
10123         if (in_combo || in_list) {
10124             //e.stopPropagation();
10125             return;
10126         }
10127
10128         this.collapse();
10129         
10130     },
10131
10132     /**
10133      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10134      */
10135     expand : function(){
10136        
10137         if(this.isExpanded() || !this.hasFocus){
10138             return;
10139         }
10140          Roo.log('expand');
10141         this.list.alignTo(this.inputEl(), this.listAlign);
10142         this.list.show();
10143         Roo.get(document).on('mousedown', this.collapseIf, this);
10144         Roo.get(document).on('mousewheel', this.collapseIf, this);
10145         if (!this.editable) {
10146             Roo.get(document).on('keydown', this.listKeyPress, this);
10147         }
10148         
10149         this.fireEvent('expand', this);
10150     },
10151
10152     // private
10153     // Implements the default empty TriggerField.onTriggerClick function
10154     onTriggerClick : function()
10155     {
10156         Roo.log('trigger click');
10157         
10158         if(this.disabled){
10159             return;
10160         }
10161         
10162         this.page = 0;
10163         this.loadNext = false;
10164         
10165         if(this.isExpanded()){
10166             this.collapse();
10167             if (!this.blockFocus) {
10168                 this.inputEl().focus();
10169             }
10170             
10171         }else {
10172             this.hasFocus = true;
10173             if(this.triggerAction == 'all') {
10174                 this.doQuery(this.allQuery, true);
10175             } else {
10176                 this.doQuery(this.getRawValue());
10177             }
10178             if (!this.blockFocus) {
10179                 this.inputEl().focus();
10180             }
10181         }
10182     },
10183     listKeyPress : function(e)
10184     {
10185         //Roo.log('listkeypress');
10186         // scroll to first matching element based on key pres..
10187         if (e.isSpecialKey()) {
10188             return false;
10189         }
10190         var k = String.fromCharCode(e.getKey()).toUpperCase();
10191         //Roo.log(k);
10192         var match  = false;
10193         var csel = this.view.getSelectedNodes();
10194         var cselitem = false;
10195         if (csel.length) {
10196             var ix = this.view.indexOf(csel[0]);
10197             cselitem  = this.store.getAt(ix);
10198             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10199                 cselitem = false;
10200             }
10201             
10202         }
10203         
10204         this.store.each(function(v) { 
10205             if (cselitem) {
10206                 // start at existing selection.
10207                 if (cselitem.id == v.id) {
10208                     cselitem = false;
10209                 }
10210                 return true;
10211             }
10212                 
10213             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10214                 match = this.store.indexOf(v);
10215                 return false;
10216             }
10217             return true;
10218         }, this);
10219         
10220         if (match === false) {
10221             return true; // no more action?
10222         }
10223         // scroll to?
10224         this.view.select(match);
10225         var sn = Roo.get(this.view.getSelectedNodes()[0])
10226         //sn.scrollIntoView(sn.dom.parentNode, false);
10227     },
10228     
10229     onViewScroll : function(e, t){
10230         
10231         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10232             return;
10233         }
10234         
10235         this.hasQuery = true;
10236         
10237         this.loading = this.list.select('.loading', true).first();
10238         
10239         if(this.loading === null){
10240             this.list.createChild({
10241                 tag: 'div',
10242                 cls: 'loading select2-more-results select2-active',
10243                 html: 'Loading more results...'
10244             })
10245             
10246             this.loading = this.list.select('.loading', true).first();
10247             
10248             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10249             
10250             this.loading.hide();
10251         }
10252         
10253         this.loading.show();
10254         
10255         var _combo = this;
10256         
10257         this.page++;
10258         this.loadNext = true;
10259         
10260         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10261         
10262         return;
10263     },
10264     
10265     addItem : function(o)
10266     {   
10267         var dv = ''; // display value
10268         
10269         if (this.displayField) {
10270             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10271         } else {
10272             // this is an error condition!!!
10273             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10274         }
10275         
10276         if(!dv.length){
10277             return;
10278         }
10279         
10280         var choice = this.choices.createChild({
10281             tag: 'li',
10282             cls: 'select2-search-choice',
10283             cn: [
10284                 {
10285                     tag: 'div',
10286                     html: dv
10287                 },
10288                 {
10289                     tag: 'a',
10290                     href: '#',
10291                     cls: 'select2-search-choice-close',
10292                     tabindex: '-1'
10293                 }
10294             ]
10295             
10296         }, this.searchField);
10297         
10298         var close = choice.select('a.select2-search-choice-close', true).first()
10299         
10300         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10301         
10302         this.item.push(o);
10303         this.lastData = o;
10304         
10305         this.syncValue();
10306         
10307         this.inputEl().dom.value = '';
10308         
10309     },
10310     
10311     onRemoveItem : function(e, _self, o)
10312     {
10313         e.preventDefault();
10314         var index = this.item.indexOf(o.data) * 1;
10315         
10316         if( index < 0){
10317             Roo.log('not this item?!');
10318             return;
10319         }
10320         
10321         this.item.splice(index, 1);
10322         o.item.remove();
10323         
10324         this.syncValue();
10325         
10326         this.fireEvent('remove', this, e);
10327         
10328     },
10329     
10330     syncValue : function()
10331     {
10332         if(!this.item.length){
10333             this.clearValue();
10334             return;
10335         }
10336             
10337         var value = [];
10338         var _this = this;
10339         Roo.each(this.item, function(i){
10340             if(_this.valueField){
10341                 value.push(i[_this.valueField]);
10342                 return;
10343             }
10344
10345             value.push(i);
10346         });
10347
10348         this.value = value.join(',');
10349
10350         if(this.hiddenField){
10351             this.hiddenField.dom.value = this.value;
10352         }
10353     },
10354     
10355     clearItem : function()
10356     {
10357         if(!this.multiple){
10358             return;
10359         }
10360         
10361         this.item = [];
10362         
10363         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10364            c.remove();
10365         });
10366         
10367         this.syncValue();
10368     }
10369     
10370     
10371
10372     /** 
10373     * @cfg {Boolean} grow 
10374     * @hide 
10375     */
10376     /** 
10377     * @cfg {Number} growMin 
10378     * @hide 
10379     */
10380     /** 
10381     * @cfg {Number} growMax 
10382     * @hide 
10383     */
10384     /**
10385      * @hide
10386      * @method autoSize
10387      */
10388 });
10389 /*
10390  * Based on:
10391  * Ext JS Library 1.1.1
10392  * Copyright(c) 2006-2007, Ext JS, LLC.
10393  *
10394  * Originally Released Under LGPL - original licence link has changed is not relivant.
10395  *
10396  * Fork - LGPL
10397  * <script type="text/javascript">
10398  */
10399
10400 /**
10401  * @class Roo.View
10402  * @extends Roo.util.Observable
10403  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10404  * This class also supports single and multi selection modes. <br>
10405  * Create a data model bound view:
10406  <pre><code>
10407  var store = new Roo.data.Store(...);
10408
10409  var view = new Roo.View({
10410     el : "my-element",
10411     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10412  
10413     singleSelect: true,
10414     selectedClass: "ydataview-selected",
10415     store: store
10416  });
10417
10418  // listen for node click?
10419  view.on("click", function(vw, index, node, e){
10420  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10421  });
10422
10423  // load XML data
10424  dataModel.load("foobar.xml");
10425  </code></pre>
10426  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10427  * <br><br>
10428  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10429  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10430  * 
10431  * Note: old style constructor is still suported (container, template, config)
10432  * 
10433  * @constructor
10434  * Create a new View
10435  * @param {Object} config The config object
10436  * 
10437  */
10438 Roo.View = function(config, depreciated_tpl, depreciated_config){
10439     
10440     if (typeof(depreciated_tpl) == 'undefined') {
10441         // new way.. - universal constructor.
10442         Roo.apply(this, config);
10443         this.el  = Roo.get(this.el);
10444     } else {
10445         // old format..
10446         this.el  = Roo.get(config);
10447         this.tpl = depreciated_tpl;
10448         Roo.apply(this, depreciated_config);
10449     }
10450     this.wrapEl  = this.el.wrap().wrap();
10451     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10452     
10453     
10454     if(typeof(this.tpl) == "string"){
10455         this.tpl = new Roo.Template(this.tpl);
10456     } else {
10457         // support xtype ctors..
10458         this.tpl = new Roo.factory(this.tpl, Roo);
10459     }
10460     
10461     
10462     this.tpl.compile();
10463    
10464   
10465     
10466      
10467     /** @private */
10468     this.addEvents({
10469         /**
10470          * @event beforeclick
10471          * Fires before a click is processed. Returns false to cancel the default action.
10472          * @param {Roo.View} this
10473          * @param {Number} index The index of the target node
10474          * @param {HTMLElement} node The target node
10475          * @param {Roo.EventObject} e The raw event object
10476          */
10477             "beforeclick" : true,
10478         /**
10479          * @event click
10480          * Fires when a template node is clicked.
10481          * @param {Roo.View} this
10482          * @param {Number} index The index of the target node
10483          * @param {HTMLElement} node The target node
10484          * @param {Roo.EventObject} e The raw event object
10485          */
10486             "click" : true,
10487         /**
10488          * @event dblclick
10489          * Fires when a template node is double clicked.
10490          * @param {Roo.View} this
10491          * @param {Number} index The index of the target node
10492          * @param {HTMLElement} node The target node
10493          * @param {Roo.EventObject} e The raw event object
10494          */
10495             "dblclick" : true,
10496         /**
10497          * @event contextmenu
10498          * Fires when a template node is right clicked.
10499          * @param {Roo.View} this
10500          * @param {Number} index The index of the target node
10501          * @param {HTMLElement} node The target node
10502          * @param {Roo.EventObject} e The raw event object
10503          */
10504             "contextmenu" : true,
10505         /**
10506          * @event selectionchange
10507          * Fires when the selected nodes change.
10508          * @param {Roo.View} this
10509          * @param {Array} selections Array of the selected nodes
10510          */
10511             "selectionchange" : true,
10512     
10513         /**
10514          * @event beforeselect
10515          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10516          * @param {Roo.View} this
10517          * @param {HTMLElement} node The node to be selected
10518          * @param {Array} selections Array of currently selected nodes
10519          */
10520             "beforeselect" : true,
10521         /**
10522          * @event preparedata
10523          * Fires on every row to render, to allow you to change the data.
10524          * @param {Roo.View} this
10525          * @param {Object} data to be rendered (change this)
10526          */
10527           "preparedata" : true
10528           
10529           
10530         });
10531
10532
10533
10534     this.el.on({
10535         "click": this.onClick,
10536         "dblclick": this.onDblClick,
10537         "contextmenu": this.onContextMenu,
10538         scope:this
10539     });
10540
10541     this.selections = [];
10542     this.nodes = [];
10543     this.cmp = new Roo.CompositeElementLite([]);
10544     if(this.store){
10545         this.store = Roo.factory(this.store, Roo.data);
10546         this.setStore(this.store, true);
10547     }
10548     
10549     if ( this.footer && this.footer.xtype) {
10550            
10551          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10552         
10553         this.footer.dataSource = this.store
10554         this.footer.container = fctr;
10555         this.footer = Roo.factory(this.footer, Roo);
10556         fctr.insertFirst(this.el);
10557         
10558         // this is a bit insane - as the paging toolbar seems to detach the el..
10559 //        dom.parentNode.parentNode.parentNode
10560          // they get detached?
10561     }
10562     
10563     
10564     Roo.View.superclass.constructor.call(this);
10565     
10566     
10567 };
10568
10569 Roo.extend(Roo.View, Roo.util.Observable, {
10570     
10571      /**
10572      * @cfg {Roo.data.Store} store Data store to load data from.
10573      */
10574     store : false,
10575     
10576     /**
10577      * @cfg {String|Roo.Element} el The container element.
10578      */
10579     el : '',
10580     
10581     /**
10582      * @cfg {String|Roo.Template} tpl The template used by this View 
10583      */
10584     tpl : false,
10585     /**
10586      * @cfg {String} dataName the named area of the template to use as the data area
10587      *                          Works with domtemplates roo-name="name"
10588      */
10589     dataName: false,
10590     /**
10591      * @cfg {String} selectedClass The css class to add to selected nodes
10592      */
10593     selectedClass : "x-view-selected",
10594      /**
10595      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10596      */
10597     emptyText : "",
10598     
10599     /**
10600      * @cfg {String} text to display on mask (default Loading)
10601      */
10602     mask : false,
10603     /**
10604      * @cfg {Boolean} multiSelect Allow multiple selection
10605      */
10606     multiSelect : false,
10607     /**
10608      * @cfg {Boolean} singleSelect Allow single selection
10609      */
10610     singleSelect:  false,
10611     
10612     /**
10613      * @cfg {Boolean} toggleSelect - selecting 
10614      */
10615     toggleSelect : false,
10616     
10617     /**
10618      * Returns the element this view is bound to.
10619      * @return {Roo.Element}
10620      */
10621     getEl : function(){
10622         return this.wrapEl;
10623     },
10624     
10625     
10626
10627     /**
10628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10629      */
10630     refresh : function(){
10631         Roo.log('refresh');
10632         var t = this.tpl;
10633         
10634         // if we are using something like 'domtemplate', then
10635         // the what gets used is:
10636         // t.applySubtemplate(NAME, data, wrapping data..)
10637         // the outer template then get' applied with
10638         //     the store 'extra data'
10639         // and the body get's added to the
10640         //      roo-name="data" node?
10641         //      <span class='roo-tpl-{name}'></span> ?????
10642         
10643         
10644         
10645         this.clearSelections();
10646         this.el.update("");
10647         var html = [];
10648         var records = this.store.getRange();
10649         if(records.length < 1) {
10650             
10651             // is this valid??  = should it render a template??
10652             
10653             this.el.update(this.emptyText);
10654             return;
10655         }
10656         var el = this.el;
10657         if (this.dataName) {
10658             this.el.update(t.apply(this.store.meta)); //????
10659             el = this.el.child('.roo-tpl-' + this.dataName);
10660         }
10661         
10662         for(var i = 0, len = records.length; i < len; i++){
10663             var data = this.prepareData(records[i].data, i, records[i]);
10664             this.fireEvent("preparedata", this, data, i, records[i]);
10665             html[html.length] = Roo.util.Format.trim(
10666                 this.dataName ?
10667                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10668                     t.apply(data)
10669             );
10670         }
10671         
10672         
10673         
10674         el.update(html.join(""));
10675         this.nodes = el.dom.childNodes;
10676         this.updateIndexes(0);
10677     },
10678     
10679
10680     /**
10681      * Function to override to reformat the data that is sent to
10682      * the template for each node.
10683      * DEPRICATED - use the preparedata event handler.
10684      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10685      * a JSON object for an UpdateManager bound view).
10686      */
10687     prepareData : function(data, index, record)
10688     {
10689         this.fireEvent("preparedata", this, data, index, record);
10690         return data;
10691     },
10692
10693     onUpdate : function(ds, record){
10694          Roo.log('on update');   
10695         this.clearSelections();
10696         var index = this.store.indexOf(record);
10697         var n = this.nodes[index];
10698         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10699         n.parentNode.removeChild(n);
10700         this.updateIndexes(index, index);
10701     },
10702
10703     
10704     
10705 // --------- FIXME     
10706     onAdd : function(ds, records, index)
10707     {
10708         Roo.log(['on Add', ds, records, index] );        
10709         this.clearSelections();
10710         if(this.nodes.length == 0){
10711             this.refresh();
10712             return;
10713         }
10714         var n = this.nodes[index];
10715         for(var i = 0, len = records.length; i < len; i++){
10716             var d = this.prepareData(records[i].data, i, records[i]);
10717             if(n){
10718                 this.tpl.insertBefore(n, d);
10719             }else{
10720                 
10721                 this.tpl.append(this.el, d);
10722             }
10723         }
10724         this.updateIndexes(index);
10725     },
10726
10727     onRemove : function(ds, record, index){
10728         Roo.log('onRemove');
10729         this.clearSelections();
10730         var el = this.dataName  ?
10731             this.el.child('.roo-tpl-' + this.dataName) :
10732             this.el; 
10733         
10734         el.dom.removeChild(this.nodes[index]);
10735         this.updateIndexes(index);
10736     },
10737
10738     /**
10739      * Refresh an individual node.
10740      * @param {Number} index
10741      */
10742     refreshNode : function(index){
10743         this.onUpdate(this.store, this.store.getAt(index));
10744     },
10745
10746     updateIndexes : function(startIndex, endIndex){
10747         var ns = this.nodes;
10748         startIndex = startIndex || 0;
10749         endIndex = endIndex || ns.length - 1;
10750         for(var i = startIndex; i <= endIndex; i++){
10751             ns[i].nodeIndex = i;
10752         }
10753     },
10754
10755     /**
10756      * Changes the data store this view uses and refresh the view.
10757      * @param {Store} store
10758      */
10759     setStore : function(store, initial){
10760         if(!initial && this.store){
10761             this.store.un("datachanged", this.refresh);
10762             this.store.un("add", this.onAdd);
10763             this.store.un("remove", this.onRemove);
10764             this.store.un("update", this.onUpdate);
10765             this.store.un("clear", this.refresh);
10766             this.store.un("beforeload", this.onBeforeLoad);
10767             this.store.un("load", this.onLoad);
10768             this.store.un("loadexception", this.onLoad);
10769         }
10770         if(store){
10771           
10772             store.on("datachanged", this.refresh, this);
10773             store.on("add", this.onAdd, this);
10774             store.on("remove", this.onRemove, this);
10775             store.on("update", this.onUpdate, this);
10776             store.on("clear", this.refresh, this);
10777             store.on("beforeload", this.onBeforeLoad, this);
10778             store.on("load", this.onLoad, this);
10779             store.on("loadexception", this.onLoad, this);
10780         }
10781         
10782         if(store){
10783             this.refresh();
10784         }
10785     },
10786     /**
10787      * onbeforeLoad - masks the loading area.
10788      *
10789      */
10790     onBeforeLoad : function(store,opts)
10791     {
10792          Roo.log('onBeforeLoad');   
10793         if (!opts.add) {
10794             this.el.update("");
10795         }
10796         this.el.mask(this.mask ? this.mask : "Loading" ); 
10797     },
10798     onLoad : function ()
10799     {
10800         this.el.unmask();
10801     },
10802     
10803
10804     /**
10805      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10806      * @param {HTMLElement} node
10807      * @return {HTMLElement} The template node
10808      */
10809     findItemFromChild : function(node){
10810         var el = this.dataName  ?
10811             this.el.child('.roo-tpl-' + this.dataName,true) :
10812             this.el.dom; 
10813         
10814         if(!node || node.parentNode == el){
10815                     return node;
10816             }
10817             var p = node.parentNode;
10818             while(p && p != el){
10819             if(p.parentNode == el){
10820                 return p;
10821             }
10822             p = p.parentNode;
10823         }
10824             return null;
10825     },
10826
10827     /** @ignore */
10828     onClick : function(e){
10829         var item = this.findItemFromChild(e.getTarget());
10830         if(item){
10831             var index = this.indexOf(item);
10832             if(this.onItemClick(item, index, e) !== false){
10833                 this.fireEvent("click", this, index, item, e);
10834             }
10835         }else{
10836             this.clearSelections();
10837         }
10838     },
10839
10840     /** @ignore */
10841     onContextMenu : function(e){
10842         var item = this.findItemFromChild(e.getTarget());
10843         if(item){
10844             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10845         }
10846     },
10847
10848     /** @ignore */
10849     onDblClick : function(e){
10850         var item = this.findItemFromChild(e.getTarget());
10851         if(item){
10852             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10853         }
10854     },
10855
10856     onItemClick : function(item, index, e)
10857     {
10858         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10859             return false;
10860         }
10861         if (this.toggleSelect) {
10862             var m = this.isSelected(item) ? 'unselect' : 'select';
10863             Roo.log(m);
10864             var _t = this;
10865             _t[m](item, true, false);
10866             return true;
10867         }
10868         if(this.multiSelect || this.singleSelect){
10869             if(this.multiSelect && e.shiftKey && this.lastSelection){
10870                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10871             }else{
10872                 this.select(item, this.multiSelect && e.ctrlKey);
10873                 this.lastSelection = item;
10874             }
10875             e.preventDefault();
10876         }
10877         return true;
10878     },
10879
10880     /**
10881      * Get the number of selected nodes.
10882      * @return {Number}
10883      */
10884     getSelectionCount : function(){
10885         return this.selections.length;
10886     },
10887
10888     /**
10889      * Get the currently selected nodes.
10890      * @return {Array} An array of HTMLElements
10891      */
10892     getSelectedNodes : function(){
10893         return this.selections;
10894     },
10895
10896     /**
10897      * Get the indexes of the selected nodes.
10898      * @return {Array}
10899      */
10900     getSelectedIndexes : function(){
10901         var indexes = [], s = this.selections;
10902         for(var i = 0, len = s.length; i < len; i++){
10903             indexes.push(s[i].nodeIndex);
10904         }
10905         return indexes;
10906     },
10907
10908     /**
10909      * Clear all selections
10910      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10911      */
10912     clearSelections : function(suppressEvent){
10913         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10914             this.cmp.elements = this.selections;
10915             this.cmp.removeClass(this.selectedClass);
10916             this.selections = [];
10917             if(!suppressEvent){
10918                 this.fireEvent("selectionchange", this, this.selections);
10919             }
10920         }
10921     },
10922
10923     /**
10924      * Returns true if the passed node is selected
10925      * @param {HTMLElement/Number} node The node or node index
10926      * @return {Boolean}
10927      */
10928     isSelected : function(node){
10929         var s = this.selections;
10930         if(s.length < 1){
10931             return false;
10932         }
10933         node = this.getNode(node);
10934         return s.indexOf(node) !== -1;
10935     },
10936
10937     /**
10938      * Selects nodes.
10939      * @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
10940      * @param {Boolean} keepExisting (optional) true to keep existing selections
10941      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10942      */
10943     select : function(nodeInfo, keepExisting, suppressEvent){
10944         if(nodeInfo instanceof Array){
10945             if(!keepExisting){
10946                 this.clearSelections(true);
10947             }
10948             for(var i = 0, len = nodeInfo.length; i < len; i++){
10949                 this.select(nodeInfo[i], true, true);
10950             }
10951             return;
10952         } 
10953         var node = this.getNode(nodeInfo);
10954         if(!node || this.isSelected(node)){
10955             return; // already selected.
10956         }
10957         if(!keepExisting){
10958             this.clearSelections(true);
10959         }
10960         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10961             Roo.fly(node).addClass(this.selectedClass);
10962             this.selections.push(node);
10963             if(!suppressEvent){
10964                 this.fireEvent("selectionchange", this, this.selections);
10965             }
10966         }
10967         
10968         
10969     },
10970       /**
10971      * Unselects nodes.
10972      * @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
10973      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10974      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10975      */
10976     unselect : function(nodeInfo, keepExisting, suppressEvent)
10977     {
10978         if(nodeInfo instanceof Array){
10979             Roo.each(this.selections, function(s) {
10980                 this.unselect(s, nodeInfo);
10981             }, this);
10982             return;
10983         }
10984         var node = this.getNode(nodeInfo);
10985         if(!node || !this.isSelected(node)){
10986             Roo.log("not selected");
10987             return; // not selected.
10988         }
10989         // fireevent???
10990         var ns = [];
10991         Roo.each(this.selections, function(s) {
10992             if (s == node ) {
10993                 Roo.fly(node).removeClass(this.selectedClass);
10994
10995                 return;
10996             }
10997             ns.push(s);
10998         },this);
10999         
11000         this.selections= ns;
11001         this.fireEvent("selectionchange", this, this.selections);
11002     },
11003
11004     /**
11005      * Gets a template 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 {HTMLElement} The node or null if it wasn't found
11008      */
11009     getNode : function(nodeInfo){
11010         if(typeof nodeInfo == "string"){
11011             return document.getElementById(nodeInfo);
11012         }else if(typeof nodeInfo == "number"){
11013             return this.nodes[nodeInfo];
11014         }
11015         return nodeInfo;
11016     },
11017
11018     /**
11019      * Gets a range template nodes.
11020      * @param {Number} startIndex
11021      * @param {Number} endIndex
11022      * @return {Array} An array of nodes
11023      */
11024     getNodes : function(start, end){
11025         var ns = this.nodes;
11026         start = start || 0;
11027         end = typeof end == "undefined" ? ns.length - 1 : end;
11028         var nodes = [];
11029         if(start <= end){
11030             for(var i = start; i <= end; i++){
11031                 nodes.push(ns[i]);
11032             }
11033         } else{
11034             for(var i = start; i >= end; i--){
11035                 nodes.push(ns[i]);
11036             }
11037         }
11038         return nodes;
11039     },
11040
11041     /**
11042      * Finds the index of the passed node
11043      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11044      * @return {Number} The index of the node or -1
11045      */
11046     indexOf : function(node){
11047         node = this.getNode(node);
11048         if(typeof node.nodeIndex == "number"){
11049             return node.nodeIndex;
11050         }
11051         var ns = this.nodes;
11052         for(var i = 0, len = ns.length; i < len; i++){
11053             if(ns[i] == node){
11054                 return i;
11055             }
11056         }
11057         return -1;
11058     }
11059 });
11060 /*
11061  * - LGPL
11062  *
11063  * based on jquery fullcalendar
11064  * 
11065  */
11066
11067 Roo.bootstrap = Roo.bootstrap || {};
11068 /**
11069  * @class Roo.bootstrap.Calendar
11070  * @extends Roo.bootstrap.Component
11071  * Bootstrap Calendar class
11072  * @cfg {Boolean} loadMask (true|false) default false
11073  * @cfg {Object} header generate the user specific header of the calendar, default false
11074
11075  * @constructor
11076  * Create a new Container
11077  * @param {Object} config The config object
11078  */
11079
11080
11081
11082 Roo.bootstrap.Calendar = function(config){
11083     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11084      this.addEvents({
11085         /**
11086              * @event select
11087              * Fires when a date is selected
11088              * @param {DatePicker} this
11089              * @param {Date} date The selected date
11090              */
11091         'select': true,
11092         /**
11093              * @event monthchange
11094              * Fires when the displayed month changes 
11095              * @param {DatePicker} this
11096              * @param {Date} date The selected month
11097              */
11098         'monthchange': true,
11099         /**
11100              * @event evententer
11101              * Fires when mouse over an event
11102              * @param {Calendar} this
11103              * @param {event} Event
11104              */
11105         'evententer': true,
11106         /**
11107              * @event eventleave
11108              * Fires when the mouse leaves an
11109              * @param {Calendar} this
11110              * @param {event}
11111              */
11112         'eventleave': true,
11113         /**
11114              * @event eventclick
11115              * Fires when the mouse click an
11116              * @param {Calendar} this
11117              * @param {event}
11118              */
11119         'eventclick': true
11120         
11121     });
11122
11123 };
11124
11125 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11126     
11127      /**
11128      * @cfg {Number} startDay
11129      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11130      */
11131     startDay : 0,
11132     
11133     loadMask : false,
11134     
11135     header : false,
11136       
11137     getAutoCreate : function(){
11138         
11139         
11140         var fc_button = function(name, corner, style, content ) {
11141             return Roo.apply({},{
11142                 tag : 'span',
11143                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11144                          (corner.length ?
11145                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11146                             ''
11147                         ),
11148                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11149                 unselectable: 'on'
11150             });
11151         };
11152         
11153         var header = {};
11154         
11155         if(!this.header){
11156             header = {
11157                 tag : 'table',
11158                 cls : 'fc-header',
11159                 style : 'width:100%',
11160                 cn : [
11161                     {
11162                         tag: 'tr',
11163                         cn : [
11164                             {
11165                                 tag : 'td',
11166                                 cls : 'fc-header-left',
11167                                 cn : [
11168                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11169                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11170                                     { tag: 'span', cls: 'fc-header-space' },
11171                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11172
11173
11174                                 ]
11175                             },
11176
11177                             {
11178                                 tag : 'td',
11179                                 cls : 'fc-header-center',
11180                                 cn : [
11181                                     {
11182                                         tag: 'span',
11183                                         cls: 'fc-header-title',
11184                                         cn : {
11185                                             tag: 'H2',
11186                                             html : 'month / year'
11187                                         }
11188                                     }
11189
11190                                 ]
11191                             },
11192                             {
11193                                 tag : 'td',
11194                                 cls : 'fc-header-right',
11195                                 cn : [
11196                               /*      fc_button('month', 'left', '', 'month' ),
11197                                     fc_button('week', '', '', 'week' ),
11198                                     fc_button('day', 'right', '', 'day' )
11199                                 */    
11200
11201                                 ]
11202                             }
11203
11204                         ]
11205                     }
11206                 ]
11207             };
11208         }
11209         
11210         header = this.header;
11211         
11212        
11213         var cal_heads = function() {
11214             var ret = [];
11215             // fixme - handle this.
11216             
11217             for (var i =0; i < Date.dayNames.length; i++) {
11218                 var d = Date.dayNames[i];
11219                 ret.push({
11220                     tag: 'th',
11221                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11222                     html : d.substring(0,3)
11223                 });
11224                 
11225             }
11226             ret[0].cls += ' fc-first';
11227             ret[6].cls += ' fc-last';
11228             return ret;
11229         };
11230         var cal_cell = function(n) {
11231             return  {
11232                 tag: 'td',
11233                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11234                 cn : [
11235                     {
11236                         cn : [
11237                             {
11238                                 cls: 'fc-day-number',
11239                                 html: 'D'
11240                             },
11241                             {
11242                                 cls: 'fc-day-content',
11243                              
11244                                 cn : [
11245                                      {
11246                                         style: 'position: relative;' // height: 17px;
11247                                     }
11248                                 ]
11249                             }
11250                             
11251                             
11252                         ]
11253                     }
11254                 ]
11255                 
11256             }
11257         };
11258         var cal_rows = function() {
11259             
11260             var ret = []
11261             for (var r = 0; r < 6; r++) {
11262                 var row= {
11263                     tag : 'tr',
11264                     cls : 'fc-week',
11265                     cn : []
11266                 };
11267                 
11268                 for (var i =0; i < Date.dayNames.length; i++) {
11269                     var d = Date.dayNames[i];
11270                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11271
11272                 }
11273                 row.cn[0].cls+=' fc-first';
11274                 row.cn[0].cn[0].style = 'min-height:90px';
11275                 row.cn[6].cls+=' fc-last';
11276                 ret.push(row);
11277                 
11278             }
11279             ret[0].cls += ' fc-first';
11280             ret[4].cls += ' fc-prev-last';
11281             ret[5].cls += ' fc-last';
11282             return ret;
11283             
11284         };
11285         
11286         var cal_table = {
11287             tag: 'table',
11288             cls: 'fc-border-separate',
11289             style : 'width:100%',
11290             cellspacing  : 0,
11291             cn : [
11292                 { 
11293                     tag: 'thead',
11294                     cn : [
11295                         { 
11296                             tag: 'tr',
11297                             cls : 'fc-first fc-last',
11298                             cn : cal_heads()
11299                         }
11300                     ]
11301                 },
11302                 { 
11303                     tag: 'tbody',
11304                     cn : cal_rows()
11305                 }
11306                   
11307             ]
11308         };
11309          
11310          var cfg = {
11311             cls : 'fc fc-ltr',
11312             cn : [
11313                 header,
11314                 {
11315                     cls : 'fc-content',
11316                     style : "position: relative;",
11317                     cn : [
11318                         {
11319                             cls : 'fc-view fc-view-month fc-grid',
11320                             style : 'position: relative',
11321                             unselectable : 'on',
11322                             cn : [
11323                                 {
11324                                     cls : 'fc-event-container',
11325                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11326                                 },
11327                                 cal_table
11328                             ]
11329                         }
11330                     ]
11331     
11332                 }
11333            ] 
11334             
11335         };
11336         
11337          
11338         
11339         return cfg;
11340     },
11341     
11342     
11343     initEvents : function()
11344     {
11345         if(!this.store){
11346             throw "can not find store for calendar";
11347         }
11348         
11349         var mark = {
11350             tag: "div",
11351             cls:"x-dlg-mask",
11352             style: "text-align:center",
11353             cn: [
11354                 {
11355                     tag: "div",
11356                     style: "background-color:white;width:50%;margin:250 auto",
11357                     cn: [
11358                         {
11359                             tag: "img",
11360                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11361                         },
11362                         {
11363                             tag: "span",
11364                             html: "Loading"
11365                         }
11366                         
11367                     ]
11368                 }
11369             ]
11370         }
11371         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11372         
11373         var size = this.el.select('.fc-content', true).first().getSize();
11374         this.maskEl.setSize(size.width, size.height);
11375         this.maskEl.enableDisplayMode("block");
11376         if(!this.loadMask){
11377             this.maskEl.hide();
11378         }
11379         
11380         this.store = Roo.factory(this.store, Roo.data);
11381         this.store.on('load', this.onLoad, this);
11382         this.store.on('beforeload', this.onBeforeLoad, this);
11383         
11384         this.resize();
11385         
11386         this.cells = this.el.select('.fc-day',true);
11387         //Roo.log(this.cells);
11388         this.textNodes = this.el.query('.fc-day-number');
11389         this.cells.addClassOnOver('fc-state-hover');
11390         
11391         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11392         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11393         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11394         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11395         
11396         this.on('monthchange', this.onMonthChange, this);
11397         
11398         this.update(new Date().clearTime());
11399     },
11400     
11401     resize : function() {
11402         var sz  = this.el.getSize();
11403         
11404         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11405         this.el.select('.fc-day-content div',true).setHeight(34);
11406     },
11407     
11408     
11409     // private
11410     showPrevMonth : function(e){
11411         this.update(this.activeDate.add("mo", -1));
11412     },
11413     showToday : function(e){
11414         this.update(new Date().clearTime());
11415     },
11416     // private
11417     showNextMonth : function(e){
11418         this.update(this.activeDate.add("mo", 1));
11419     },
11420
11421     // private
11422     showPrevYear : function(){
11423         this.update(this.activeDate.add("y", -1));
11424     },
11425
11426     // private
11427     showNextYear : function(){
11428         this.update(this.activeDate.add("y", 1));
11429     },
11430
11431     
11432    // private
11433     update : function(date)
11434     {
11435         var vd = this.activeDate;
11436         this.activeDate = date;
11437 //        if(vd && this.el){
11438 //            var t = date.getTime();
11439 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11440 //                Roo.log('using add remove');
11441 //                
11442 //                this.fireEvent('monthchange', this, date);
11443 //                
11444 //                this.cells.removeClass("fc-state-highlight");
11445 //                this.cells.each(function(c){
11446 //                   if(c.dateValue == t){
11447 //                       c.addClass("fc-state-highlight");
11448 //                       setTimeout(function(){
11449 //                            try{c.dom.firstChild.focus();}catch(e){}
11450 //                       }, 50);
11451 //                       return false;
11452 //                   }
11453 //                   return true;
11454 //                });
11455 //                return;
11456 //            }
11457 //        }
11458         
11459         var days = date.getDaysInMonth();
11460         
11461         var firstOfMonth = date.getFirstDateOfMonth();
11462         var startingPos = firstOfMonth.getDay()-this.startDay;
11463         
11464         if(startingPos < this.startDay){
11465             startingPos += 7;
11466         }
11467         
11468         var pm = date.add(Date.MONTH, -1);
11469         var prevStart = pm.getDaysInMonth()-startingPos;
11470 //        
11471         this.cells = this.el.select('.fc-day',true);
11472         this.textNodes = this.el.query('.fc-day-number');
11473         this.cells.addClassOnOver('fc-state-hover');
11474         
11475         var cells = this.cells.elements;
11476         var textEls = this.textNodes;
11477         
11478         Roo.each(cells, function(cell){
11479             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11480         });
11481         
11482         days += startingPos;
11483
11484         // convert everything to numbers so it's fast
11485         var day = 86400000;
11486         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11487         //Roo.log(d);
11488         //Roo.log(pm);
11489         //Roo.log(prevStart);
11490         
11491         var today = new Date().clearTime().getTime();
11492         var sel = date.clearTime().getTime();
11493         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11494         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11495         var ddMatch = this.disabledDatesRE;
11496         var ddText = this.disabledDatesText;
11497         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11498         var ddaysText = this.disabledDaysText;
11499         var format = this.format;
11500         
11501         var setCellClass = function(cal, cell){
11502             
11503             //Roo.log('set Cell Class');
11504             cell.title = "";
11505             var t = d.getTime();
11506             
11507             //Roo.log(d);
11508             
11509             cell.dateValue = t;
11510             if(t == today){
11511                 cell.className += " fc-today";
11512                 cell.className += " fc-state-highlight";
11513                 cell.title = cal.todayText;
11514             }
11515             if(t == sel){
11516                 // disable highlight in other month..
11517                 //cell.className += " fc-state-highlight";
11518                 
11519             }
11520             // disabling
11521             if(t < min) {
11522                 cell.className = " fc-state-disabled";
11523                 cell.title = cal.minText;
11524                 return;
11525             }
11526             if(t > max) {
11527                 cell.className = " fc-state-disabled";
11528                 cell.title = cal.maxText;
11529                 return;
11530             }
11531             if(ddays){
11532                 if(ddays.indexOf(d.getDay()) != -1){
11533                     cell.title = ddaysText;
11534                     cell.className = " fc-state-disabled";
11535                 }
11536             }
11537             if(ddMatch && format){
11538                 var fvalue = d.dateFormat(format);
11539                 if(ddMatch.test(fvalue)){
11540                     cell.title = ddText.replace("%0", fvalue);
11541                     cell.className = " fc-state-disabled";
11542                 }
11543             }
11544             
11545             if (!cell.initialClassName) {
11546                 cell.initialClassName = cell.dom.className;
11547             }
11548             
11549             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11550         };
11551
11552         var i = 0;
11553         
11554         for(; i < startingPos; i++) {
11555             textEls[i].innerHTML = (++prevStart);
11556             d.setDate(d.getDate()+1);
11557             
11558             cells[i].className = "fc-past fc-other-month";
11559             setCellClass(this, cells[i]);
11560         }
11561         
11562         var intDay = 0;
11563         
11564         for(; i < days; i++){
11565             intDay = i - startingPos + 1;
11566             textEls[i].innerHTML = (intDay);
11567             d.setDate(d.getDate()+1);
11568             
11569             cells[i].className = ''; // "x-date-active";
11570             setCellClass(this, cells[i]);
11571         }
11572         var extraDays = 0;
11573         
11574         for(; i < 42; i++) {
11575             textEls[i].innerHTML = (++extraDays);
11576             d.setDate(d.getDate()+1);
11577             
11578             cells[i].className = "fc-future fc-other-month";
11579             setCellClass(this, cells[i]);
11580         }
11581         
11582         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11583         
11584         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11585         
11586         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11587         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11588         
11589         if(totalRows != 6){
11590             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11591             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11592         }
11593         
11594         this.fireEvent('monthchange', this, date);
11595         
11596         
11597         /*
11598         if(!this.internalRender){
11599             var main = this.el.dom.firstChild;
11600             var w = main.offsetWidth;
11601             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11602             Roo.fly(main).setWidth(w);
11603             this.internalRender = true;
11604             // opera does not respect the auto grow header center column
11605             // then, after it gets a width opera refuses to recalculate
11606             // without a second pass
11607             if(Roo.isOpera && !this.secondPass){
11608                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11609                 this.secondPass = true;
11610                 this.update.defer(10, this, [date]);
11611             }
11612         }
11613         */
11614         
11615     },
11616     
11617     findCell : function(dt) {
11618         dt = dt.clearTime().getTime();
11619         var ret = false;
11620         this.cells.each(function(c){
11621             //Roo.log("check " +c.dateValue + '?=' + dt);
11622             if(c.dateValue == dt){
11623                 ret = c;
11624                 return false;
11625             }
11626             return true;
11627         });
11628         
11629         return ret;
11630     },
11631     
11632     findCells : function(ev) {
11633         var s = ev.start.clone().clearTime().getTime();
11634        // Roo.log(s);
11635         var e= ev.end.clone().clearTime().getTime();
11636        // Roo.log(e);
11637         var ret = [];
11638         this.cells.each(function(c){
11639              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11640             
11641             if(c.dateValue > e){
11642                 return ;
11643             }
11644             if(c.dateValue < s){
11645                 return ;
11646             }
11647             ret.push(c);
11648         });
11649         
11650         return ret;    
11651     },
11652     
11653 //    findBestRow: function(cells)
11654 //    {
11655 //        var ret = 0;
11656 //        
11657 //        for (var i =0 ; i < cells.length;i++) {
11658 //            ret  = Math.max(cells[i].rows || 0,ret);
11659 //        }
11660 //        return ret;
11661 //        
11662 //    },
11663     
11664     
11665     addItem : function(ev)
11666     {
11667         // look for vertical location slot in
11668         var cells = this.findCells(ev);
11669         
11670 //        ev.row = this.findBestRow(cells);
11671         
11672         // work out the location.
11673         
11674         var crow = false;
11675         var rows = [];
11676         for(var i =0; i < cells.length; i++) {
11677             if (!crow) {
11678                 crow = {
11679                     start : cells[i],
11680                     end :  cells[i]
11681                 };
11682                 continue;
11683             }
11684             if (crow.start.getY() == cells[i].getY()) {
11685                 // on same row.
11686                 crow.end = cells[i];
11687                 continue;
11688             }
11689             // different row.
11690             rows.push(crow);
11691             crow = {
11692                 start: cells[i],
11693                 end : cells[i]
11694             };
11695             
11696         }
11697         
11698         rows.push(crow);
11699         ev.els = [];
11700         ev.rows = rows;
11701         ev.cells = cells;
11702         ev.rendered = false;
11703 //        for (var i = 0; i < cells.length;i++) {
11704 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11705 //            
11706 //        }
11707         
11708         this.calevents.push(ev);
11709     },
11710     
11711     clearEvents: function() {
11712         
11713         if(!this.calevents){
11714             return;
11715         }
11716         
11717         Roo.each(this.cells.elements, function(c){
11718             c.rows = [];
11719             c.more = [];
11720         });
11721         
11722         Roo.each(this.calevents, function(e) {
11723             Roo.each(e.els, function(el) {
11724                 el.un('mouseenter' ,this.onEventEnter, this);
11725                 el.un('mouseleave' ,this.onEventLeave, this);
11726                 el.remove();
11727             },this);
11728         },this);
11729         
11730         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11731             e.remove();
11732         });
11733         
11734     },
11735     
11736     renderEvents: function()
11737     {   
11738         // first make sure there is enough space..
11739         this.cells.each(function(c) {
11740             c.rows = [];
11741             c.more = [];
11742         });
11743         
11744         for (var e = 0; e < this.calevents.length; e++) {
11745             
11746             var ev = this.calevents[e];
11747             var cells = ev.cells;
11748             var rows = ev.rows;
11749             
11750             for(var i = 0; i < cells.length; i++){
11751                 
11752                 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11753                 
11754                 if(cells.length < 2 && cbox.rows.length > 3){
11755                     cbox.more.push(ev);
11756                     continue;
11757                 }
11758                 
11759                 cbox.rows.push(ev);
11760             }
11761         }
11762         
11763         var _this = this;
11764         
11765         this.cells.each(function(c) {
11766             if(c.more.length && c.more.length == 1){
11767                 c.rows.push(c.more.pop());
11768             }
11769             
11770             var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11771             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11772             
11773             
11774             for (var e = 0; e < c.rows.length; e++){
11775                 var ev = c.rows[e];
11776                 
11777                 if(ev.rendered){
11778                     continue;
11779                 }
11780                 
11781                 var cells = ev.cells;
11782                 var rows = ev.rows;
11783                 
11784                 for(var i = 0; i < rows.length; i++) {
11785                 
11786                     // how many rows should it span..
11787
11788                     var  cfg = {
11789                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11790                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11791
11792                         unselectable : "on",
11793                         cn : [
11794                             {
11795                                 cls: 'fc-event-inner',
11796                                 cn : [
11797     //                                {
11798     //                                  tag:'span',
11799     //                                  cls: 'fc-event-time',
11800     //                                  html : cells.length > 1 ? '' : ev.time
11801     //                                },
11802                                     {
11803                                       tag:'span',
11804                                       cls: 'fc-event-title',
11805                                       html : String.format('{0}', ev.title)
11806                                     }
11807
11808
11809                                 ]
11810                             },
11811                             {
11812                                 cls: 'ui-resizable-handle ui-resizable-e',
11813                                 html : '&nbsp;&nbsp;&nbsp'
11814                             }
11815
11816                         ]
11817                     };
11818
11819                     if (i == 0) {
11820                         cfg.cls += ' fc-event-start';
11821                     }
11822                     if ((i+1) == rows.length) {
11823                         cfg.cls += ' fc-event-end';
11824                     }
11825
11826                     var ctr = _this.el.select('.fc-event-container',true).first();
11827                     var cg = ctr.createChild(cfg);
11828
11829                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11830                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11831
11832                     cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11833                     cg.setWidth(ebox.right - sbox.x -2);
11834
11835                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11836                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11837                     cg.on('click', _this.onEventClick, _this, ev);
11838
11839                     ev.els.push(cg);
11840                     
11841                     ev.rendered = true;
11842                 }
11843                 
11844             }
11845             
11846             
11847             if(c.more.length){
11848                 var  cfg = {
11849                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11850                     style : 'position: absolute',
11851                     unselectable : "on",
11852                     cn : [
11853                         {
11854                             cls: 'fc-event-inner',
11855                             cn : [
11856                                 {
11857                                   tag:'span',
11858                                   cls: 'fc-event-title',
11859                                   html : 'More'
11860                                 }
11861
11862
11863                             ]
11864                         },
11865                         {
11866                             cls: 'ui-resizable-handle ui-resizable-e',
11867                             html : '&nbsp;&nbsp;&nbsp'
11868                         }
11869
11870                     ]
11871                 };
11872
11873                 var ctr = _this.el.select('.fc-event-container',true).first();
11874                 var cg = ctr.createChild(cfg);
11875
11876                 var sbox = c.select('.fc-day-content',true).first().getBox();
11877                 var ebox = c.select('.fc-day-content',true).first().getBox();
11878                 //Roo.log(cg);
11879                 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);    
11880                 cg.setWidth(ebox.right - sbox.x -2);
11881
11882                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11883                 
11884             }
11885             
11886         });
11887         
11888         
11889         
11890     },
11891     
11892     onEventEnter: function (e, el,event,d) {
11893         this.fireEvent('evententer', this, el, event);
11894     },
11895     
11896     onEventLeave: function (e, el,event,d) {
11897         this.fireEvent('eventleave', this, el, event);
11898     },
11899     
11900     onEventClick: function (e, el,event,d) {
11901         this.fireEvent('eventclick', this, el, event);
11902     },
11903     
11904     onMonthChange: function () {
11905         this.store.load();
11906     },
11907     
11908     onMoreEventClick: function(e, el, more)
11909     {
11910         var _this = this;
11911         
11912         this.calpopover.placement = 'right';
11913         this.calpopover.setTitle('More');
11914         
11915         this.calpopover.setContent('');
11916         
11917         var ctr = this.calpopover.el.select('.popover-content', true).first();
11918         
11919         Roo.each(more, function(m){
11920             var cfg = {
11921                 cls : 'fc-event-hori fc-event-draggable',
11922                 html : m.title
11923             }
11924             var cg = ctr.createChild(cfg);
11925             
11926             cg.on('click', _this.onEventClick, _this, m);
11927         });
11928         
11929         this.calpopover.show(el);
11930         
11931         
11932     },
11933     
11934     onLoad: function () 
11935     {   
11936         this.calevents = [];
11937         var cal = this;
11938         
11939         if(this.store.getCount() > 0){
11940             this.store.data.each(function(d){
11941                cal.addItem({
11942                     id : d.data.id,
11943                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11944                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11945                     time : d.data.start_time,
11946                     title : d.data.title,
11947                     description : d.data.description,
11948                     venue : d.data.venue
11949                 });
11950             });
11951         }
11952         
11953         this.renderEvents();
11954         
11955         if(this.calevents.length && this.loadMask){
11956             this.maskEl.hide();
11957         }
11958     },
11959     
11960     onBeforeLoad: function()
11961     {
11962         this.clearEvents();
11963         if(this.loadMask){
11964             this.maskEl.show();
11965         }
11966     }
11967 });
11968
11969  
11970  /*
11971  * - LGPL
11972  *
11973  * element
11974  * 
11975  */
11976
11977 /**
11978  * @class Roo.bootstrap.Popover
11979  * @extends Roo.bootstrap.Component
11980  * Bootstrap Popover class
11981  * @cfg {String} html contents of the popover   (or false to use children..)
11982  * @cfg {String} title of popover (or false to hide)
11983  * @cfg {String} placement how it is placed
11984  * @cfg {String} trigger click || hover (or false to trigger manually)
11985  * @cfg {String} over what (parent or false to trigger manually.)
11986  * 
11987  * @constructor
11988  * Create a new Popover
11989  * @param {Object} config The config object
11990  */
11991
11992 Roo.bootstrap.Popover = function(config){
11993     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11994 };
11995
11996 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11997     
11998     title: 'Fill in a title',
11999     html: false,
12000     
12001     placement : 'right',
12002     trigger : 'hover', // hover
12003     
12004     over: 'parent',
12005     
12006     can_build_overlaid : false,
12007     
12008     getChildContainer : function()
12009     {
12010         return this.el.select('.popover-content',true).first();
12011     },
12012     
12013     getAutoCreate : function(){
12014          Roo.log('make popover?');
12015         var cfg = {
12016            cls : 'popover roo-dynamic',
12017            style: 'display:block',
12018            cn : [
12019                 {
12020                     cls : 'arrow'
12021                 },
12022                 {
12023                     cls : 'popover-inner',
12024                     cn : [
12025                         {
12026                             tag: 'h3',
12027                             cls: 'popover-title',
12028                             html : this.title
12029                         },
12030                         {
12031                             cls : 'popover-content',
12032                             html : this.html
12033                         }
12034                     ]
12035                     
12036                 }
12037            ]
12038         };
12039         
12040         return cfg;
12041     },
12042     setTitle: function(str)
12043     {
12044         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12045     },
12046     setContent: function(str)
12047     {
12048         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12049     },
12050     // as it get's added to the bottom of the page.
12051     onRender : function(ct, position)
12052     {
12053         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12054         if(!this.el){
12055             var cfg = Roo.apply({},  this.getAutoCreate());
12056             cfg.id = Roo.id();
12057             
12058             if (this.cls) {
12059                 cfg.cls += ' ' + this.cls;
12060             }
12061             if (this.style) {
12062                 cfg.style = this.style;
12063             }
12064             Roo.log("adding to ")
12065             this.el = Roo.get(document.body).createChild(cfg, position);
12066             Roo.log(this.el);
12067         }
12068         this.initEvents();
12069     },
12070     
12071     initEvents : function()
12072     {
12073         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12074         this.el.enableDisplayMode('block');
12075         this.el.hide();
12076         if (this.over === false) {
12077             return; 
12078         }
12079         if (this.triggers === false) {
12080             return;
12081         }
12082         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12083         var triggers = this.trigger ? this.trigger.split(' ') : [];
12084         Roo.each(triggers, function(trigger) {
12085         
12086             if (trigger == 'click') {
12087                 on_el.on('click', this.toggle, this);
12088             } else if (trigger != 'manual') {
12089                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12090                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12091       
12092                 on_el.on(eventIn  ,this.enter, this);
12093                 on_el.on(eventOut, this.leave, this);
12094             }
12095         }, this);
12096         
12097     },
12098     
12099     
12100     // private
12101     timeout : null,
12102     hoverState : null,
12103     
12104     toggle : function () {
12105         this.hoverState == 'in' ? this.leave() : this.enter();
12106     },
12107     
12108     enter : function () {
12109        
12110     
12111         clearTimeout(this.timeout);
12112     
12113         this.hoverState = 'in'
12114     
12115         if (!this.delay || !this.delay.show) {
12116             this.show();
12117             return 
12118         }
12119         var _t = this;
12120         this.timeout = setTimeout(function () {
12121             if (_t.hoverState == 'in') {
12122                 _t.show();
12123             }
12124         }, this.delay.show)
12125     },
12126     leave : function() {
12127         clearTimeout(this.timeout);
12128     
12129         this.hoverState = 'out'
12130     
12131         if (!this.delay || !this.delay.hide) {
12132             this.hide();
12133             return 
12134         }
12135         var _t = this;
12136         this.timeout = setTimeout(function () {
12137             if (_t.hoverState == 'out') {
12138                 _t.hide();
12139             }
12140         }, this.delay.hide)
12141     },
12142     
12143     show : function (on_el)
12144     {
12145         if (!on_el) {
12146             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12147         }
12148         // set content.
12149         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12150         if (this.html !== false) {
12151             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12152         }
12153         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12154         if (!this.title.length) {
12155             this.el.select('.popover-title',true).hide();
12156         }
12157         
12158         var placement = typeof this.placement == 'function' ?
12159             this.placement.call(this, this.el, on_el) :
12160             this.placement;
12161             
12162         var autoToken = /\s?auto?\s?/i;
12163         var autoPlace = autoToken.test(placement);
12164         if (autoPlace) {
12165             placement = placement.replace(autoToken, '') || 'top';
12166         }
12167         
12168         //this.el.detach()
12169         //this.el.setXY([0,0]);
12170         this.el.show();
12171         this.el.dom.style.display='block';
12172         this.el.addClass(placement);
12173         
12174         //this.el.appendTo(on_el);
12175         
12176         var p = this.getPosition();
12177         var box = this.el.getBox();
12178         
12179         if (autoPlace) {
12180             // fixme..
12181         }
12182         var align = Roo.bootstrap.Popover.alignment[placement]
12183         this.el.alignTo(on_el, align[0],align[1]);
12184         //var arrow = this.el.select('.arrow',true).first();
12185         //arrow.set(align[2], 
12186         
12187         this.el.addClass('in');
12188         this.hoverState = null;
12189         
12190         if (this.el.hasClass('fade')) {
12191             // fade it?
12192         }
12193         
12194     },
12195     hide : function()
12196     {
12197         this.el.setXY([0,0]);
12198         this.el.removeClass('in');
12199         this.el.hide();
12200         
12201     }
12202     
12203 });
12204
12205 Roo.bootstrap.Popover.alignment = {
12206     'left' : ['r-l', [-10,0], 'right'],
12207     'right' : ['l-r', [10,0], 'left'],
12208     'bottom' : ['t-b', [0,10], 'top'],
12209     'top' : [ 'b-t', [0,-10], 'bottom']
12210 };
12211
12212  /*
12213  * - LGPL
12214  *
12215  * Progress
12216  * 
12217  */
12218
12219 /**
12220  * @class Roo.bootstrap.Progress
12221  * @extends Roo.bootstrap.Component
12222  * Bootstrap Progress class
12223  * @cfg {Boolean} striped striped of the progress bar
12224  * @cfg {Boolean} active animated of the progress bar
12225  * 
12226  * 
12227  * @constructor
12228  * Create a new Progress
12229  * @param {Object} config The config object
12230  */
12231
12232 Roo.bootstrap.Progress = function(config){
12233     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12234 };
12235
12236 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12237     
12238     striped : false,
12239     active: false,
12240     
12241     getAutoCreate : function(){
12242         var cfg = {
12243             tag: 'div',
12244             cls: 'progress'
12245         };
12246         
12247         
12248         if(this.striped){
12249             cfg.cls += ' progress-striped';
12250         }
12251       
12252         if(this.active){
12253             cfg.cls += ' active';
12254         }
12255         
12256         
12257         return cfg;
12258     }
12259    
12260 });
12261
12262  
12263
12264  /*
12265  * - LGPL
12266  *
12267  * ProgressBar
12268  * 
12269  */
12270
12271 /**
12272  * @class Roo.bootstrap.ProgressBar
12273  * @extends Roo.bootstrap.Component
12274  * Bootstrap ProgressBar class
12275  * @cfg {Number} aria_valuenow aria-value now
12276  * @cfg {Number} aria_valuemin aria-value min
12277  * @cfg {Number} aria_valuemax aria-value max
12278  * @cfg {String} label label for the progress bar
12279  * @cfg {String} panel (success | info | warning | danger )
12280  * @cfg {String} role role of the progress bar
12281  * @cfg {String} sr_only text
12282  * 
12283  * 
12284  * @constructor
12285  * Create a new ProgressBar
12286  * @param {Object} config The config object
12287  */
12288
12289 Roo.bootstrap.ProgressBar = function(config){
12290     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12291 };
12292
12293 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12294     
12295     aria_valuenow : 0,
12296     aria_valuemin : 0,
12297     aria_valuemax : 100,
12298     label : false,
12299     panel : false,
12300     role : false,
12301     sr_only: false,
12302     
12303     getAutoCreate : function()
12304     {
12305         
12306         var cfg = {
12307             tag: 'div',
12308             cls: 'progress-bar',
12309             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12310         };
12311         
12312         if(this.sr_only){
12313             cfg.cn = {
12314                 tag: 'span',
12315                 cls: 'sr-only',
12316                 html: this.sr_only
12317             }
12318         }
12319         
12320         if(this.role){
12321             cfg.role = this.role;
12322         }
12323         
12324         if(this.aria_valuenow){
12325             cfg['aria-valuenow'] = this.aria_valuenow;
12326         }
12327         
12328         if(this.aria_valuemin){
12329             cfg['aria-valuemin'] = this.aria_valuemin;
12330         }
12331         
12332         if(this.aria_valuemax){
12333             cfg['aria-valuemax'] = this.aria_valuemax;
12334         }
12335         
12336         if(this.label && !this.sr_only){
12337             cfg.html = this.label;
12338         }
12339         
12340         if(this.panel){
12341             cfg.cls += ' progress-bar-' + this.panel;
12342         }
12343         
12344         return cfg;
12345     },
12346     
12347     update : function(aria_valuenow)
12348     {
12349         this.aria_valuenow = aria_valuenow;
12350         
12351         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12352     }
12353    
12354 });
12355
12356  
12357
12358  /*
12359  * - LGPL
12360  *
12361  * TabPanel
12362  * 
12363  */
12364
12365 /**
12366  * @class Roo.bootstrap.TabPanel
12367  * @extends Roo.bootstrap.Component
12368  * Bootstrap TabPanel class
12369  * @cfg {Boolean} active panel active
12370  * @cfg {String} html panel content
12371  * @cfg {String} tabId tab relate id
12372  * @cfg {String} navId The navbar which triggers show hide
12373  * 
12374  * 
12375  * @constructor
12376  * Create a new TabPanel
12377  * @param {Object} config The config object
12378  */
12379
12380 Roo.bootstrap.TabPanel = function(config){
12381     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12382      this.addEvents({
12383         /**
12384              * @event changed
12385              * Fires when the active status changes
12386              * @param {Roo.bootstrap.TabPanel} this
12387              * @param {Boolean} state the new state
12388             
12389          */
12390         'changed': true
12391      });
12392 };
12393
12394 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12395     
12396     active: false,
12397     html: false,
12398     tabId: false,
12399     navId : false,
12400     
12401     getAutoCreate : function(){
12402         var cfg = {
12403             tag: 'div',
12404             cls: 'tab-pane',
12405             html: this.html || ''
12406         };
12407         
12408         if(this.active){
12409             cfg.cls += ' active';
12410         }
12411         
12412         if(this.tabId){
12413             cfg.tabId = this.tabId;
12414         }
12415         
12416         return cfg;
12417     },
12418     onRender : function(ct, position)
12419     {
12420        // Roo.log("Call onRender: " + this.xtype);
12421         
12422         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12423         
12424         if (this.navId && this.tabId) {
12425             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12426             if (!item) {
12427                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12428             } else {
12429                 item.on('changed', function(item, state) {
12430                     this.setActive(state);
12431                 }, this);
12432             }
12433         }
12434         
12435     },
12436     setActive: function(state)
12437     {
12438         Roo.log("panel - set active " + this.tabId + "=" + state);
12439         
12440         this.active = state;
12441         if (!state) {
12442             this.el.removeClass('active');
12443             
12444         } else  if (!this.el.hasClass('active')) {
12445             this.el.addClass('active');
12446         }
12447         this.fireEvent('changed', this, state);
12448     }
12449     
12450     
12451 });
12452  
12453
12454  
12455
12456  /*
12457  * - LGPL
12458  *
12459  * DateField
12460  * 
12461  */
12462
12463 /**
12464  * @class Roo.bootstrap.DateField
12465  * @extends Roo.bootstrap.Input
12466  * Bootstrap DateField class
12467  * @cfg {Number} weekStart default 0
12468  * @cfg {Number} weekStart default 0
12469  * @cfg {Number} viewMode default empty, (months|years)
12470  * @cfg {Number} minViewMode default empty, (months|years)
12471  * @cfg {Number} startDate default -Infinity
12472  * @cfg {Number} endDate default Infinity
12473  * @cfg {Boolean} todayHighlight default false
12474  * @cfg {Boolean} todayBtn default false
12475  * @cfg {Boolean} calendarWeeks default false
12476  * @cfg {Object} daysOfWeekDisabled default empty
12477  * 
12478  * @cfg {Boolean} keyboardNavigation default true
12479  * @cfg {String} language default en
12480  * 
12481  * @constructor
12482  * Create a new DateField
12483  * @param {Object} config The config object
12484  */
12485
12486 Roo.bootstrap.DateField = function(config){
12487     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12488      this.addEvents({
12489             /**
12490              * @event show
12491              * Fires when this field show.
12492              * @param {Roo.bootstrap.DateField} this
12493              * @param {Mixed} date The date value
12494              */
12495             show : true,
12496             /**
12497              * @event show
12498              * Fires when this field hide.
12499              * @param {Roo.bootstrap.DateField} this
12500              * @param {Mixed} date The date value
12501              */
12502             hide : true,
12503             /**
12504              * @event select
12505              * Fires when select a date.
12506              * @param {Roo.bootstrap.DateField} this
12507              * @param {Mixed} date The date value
12508              */
12509             select : true
12510         });
12511 };
12512
12513 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12514     
12515     /**
12516      * @cfg {String} format
12517      * The default date format string which can be overriden for localization support.  The format must be
12518      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12519      */
12520     format : "m/d/y",
12521     /**
12522      * @cfg {String} altFormats
12523      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12524      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12525      */
12526     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12527     
12528     weekStart : 0,
12529     
12530     viewMode : '',
12531     
12532     minViewMode : '',
12533     
12534     todayHighlight : false,
12535     
12536     todayBtn: false,
12537     
12538     language: 'en',
12539     
12540     keyboardNavigation: true,
12541     
12542     calendarWeeks: false,
12543     
12544     startDate: -Infinity,
12545     
12546     endDate: Infinity,
12547     
12548     daysOfWeekDisabled: [],
12549     
12550     _events: [],
12551     
12552     UTCDate: function()
12553     {
12554         return new Date(Date.UTC.apply(Date, arguments));
12555     },
12556     
12557     UTCToday: function()
12558     {
12559         var today = new Date();
12560         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12561     },
12562     
12563     getDate: function() {
12564             var d = this.getUTCDate();
12565             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12566     },
12567     
12568     getUTCDate: function() {
12569             return this.date;
12570     },
12571     
12572     setDate: function(d) {
12573             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12574     },
12575     
12576     setUTCDate: function(d) {
12577             this.date = d;
12578             this.setValue(this.formatDate(this.date));
12579     },
12580         
12581     onRender: function(ct, position)
12582     {
12583         
12584         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12585         
12586         this.language = this.language || 'en';
12587         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12588         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12589         
12590         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12591         this.format = this.format || 'm/d/y';
12592         this.isInline = false;
12593         this.isInput = true;
12594         this.component = this.el.select('.add-on', true).first() || false;
12595         this.component = (this.component && this.component.length === 0) ? false : this.component;
12596         this.hasInput = this.component && this.inputEL().length;
12597         
12598         if (typeof(this.minViewMode === 'string')) {
12599             switch (this.minViewMode) {
12600                 case 'months':
12601                     this.minViewMode = 1;
12602                     break;
12603                 case 'years':
12604                     this.minViewMode = 2;
12605                     break;
12606                 default:
12607                     this.minViewMode = 0;
12608                     break;
12609             }
12610         }
12611         
12612         if (typeof(this.viewMode === 'string')) {
12613             switch (this.viewMode) {
12614                 case 'months':
12615                     this.viewMode = 1;
12616                     break;
12617                 case 'years':
12618                     this.viewMode = 2;
12619                     break;
12620                 default:
12621                     this.viewMode = 0;
12622                     break;
12623             }
12624         }
12625                 
12626         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12627         
12628         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12629         
12630         this.picker().on('mousedown', this.onMousedown, this);
12631         this.picker().on('click', this.onClick, this);
12632         
12633         this.picker().addClass('datepicker-dropdown');
12634         
12635         this.startViewMode = this.viewMode;
12636         
12637         
12638         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12639             if(!this.calendarWeeks){
12640                 v.remove();
12641                 return;
12642             };
12643             
12644             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12645             v.attr('colspan', function(i, val){
12646                 return parseInt(val) + 1;
12647             });
12648         })
12649                         
12650         
12651         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12652         
12653         this.setStartDate(this.startDate);
12654         this.setEndDate(this.endDate);
12655         
12656         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12657         
12658         this.fillDow();
12659         this.fillMonths();
12660         this.update();
12661         this.showMode();
12662         
12663         if(this.isInline) {
12664             this.show();
12665         }
12666     },
12667     
12668     picker : function()
12669     {
12670         return this.el.select('.datepicker', true).first();
12671     },
12672     
12673     fillDow: function()
12674     {
12675         var dowCnt = this.weekStart;
12676         
12677         var dow = {
12678             tag: 'tr',
12679             cn: [
12680                 
12681             ]
12682         };
12683         
12684         if(this.calendarWeeks){
12685             dow.cn.push({
12686                 tag: 'th',
12687                 cls: 'cw',
12688                 html: '&nbsp;'
12689             })
12690         }
12691         
12692         while (dowCnt < this.weekStart + 7) {
12693             dow.cn.push({
12694                 tag: 'th',
12695                 cls: 'dow',
12696                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12697             });
12698         }
12699         
12700         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12701     },
12702     
12703     fillMonths: function()
12704     {    
12705         var i = 0
12706         var months = this.picker().select('>.datepicker-months td', true).first();
12707         
12708         months.dom.innerHTML = '';
12709         
12710         while (i < 12) {
12711             var month = {
12712                 tag: 'span',
12713                 cls: 'month',
12714                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12715             }
12716             
12717             months.createChild(month);
12718         }
12719         
12720     },
12721     
12722     update: function(){
12723         
12724         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12725         
12726         if (this.date < this.startDate) {
12727             this.viewDate = new Date(this.startDate);
12728         } else if (this.date > this.endDate) {
12729             this.viewDate = new Date(this.endDate);
12730         } else {
12731             this.viewDate = new Date(this.date);
12732         }
12733         
12734         this.fill();
12735     },
12736     
12737     fill: function() {
12738         var d = new Date(this.viewDate),
12739                 year = d.getUTCFullYear(),
12740                 month = d.getUTCMonth(),
12741                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12742                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12743                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12744                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12745                 currentDate = this.date && this.date.valueOf(),
12746                 today = this.UTCToday();
12747         
12748         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12749         
12750 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12751         
12752 //        this.picker.select('>tfoot th.today').
12753 //                                              .text(dates[this.language].today)
12754 //                                              .toggle(this.todayBtn !== false);
12755     
12756         this.updateNavArrows();
12757         this.fillMonths();
12758                                                 
12759         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12760         
12761         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12762          
12763         prevMonth.setUTCDate(day);
12764         
12765         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12766         
12767         var nextMonth = new Date(prevMonth);
12768         
12769         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12770         
12771         nextMonth = nextMonth.valueOf();
12772         
12773         var fillMonths = false;
12774         
12775         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12776         
12777         while(prevMonth.valueOf() < nextMonth) {
12778             var clsName = '';
12779             
12780             if (prevMonth.getUTCDay() === this.weekStart) {
12781                 if(fillMonths){
12782                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12783                 }
12784                     
12785                 fillMonths = {
12786                     tag: 'tr',
12787                     cn: []
12788                 };
12789                 
12790                 if(this.calendarWeeks){
12791                     // ISO 8601: First week contains first thursday.
12792                     // ISO also states week starts on Monday, but we can be more abstract here.
12793                     var
12794                     // Start of current week: based on weekstart/current date
12795                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12796                     // Thursday of this week
12797                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12798                     // First Thursday of year, year from thursday
12799                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12800                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12801                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12802                     
12803                     fillMonths.cn.push({
12804                         tag: 'td',
12805                         cls: 'cw',
12806                         html: calWeek
12807                     });
12808                 }
12809             }
12810             
12811             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12812                 clsName += ' old';
12813             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12814                 clsName += ' new';
12815             }
12816             if (this.todayHighlight &&
12817                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12818                 prevMonth.getUTCMonth() == today.getMonth() &&
12819                 prevMonth.getUTCDate() == today.getDate()) {
12820                 clsName += ' today';
12821             }
12822             
12823             if (currentDate && prevMonth.valueOf() === currentDate) {
12824                 clsName += ' active';
12825             }
12826             
12827             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12828                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12829                     clsName += ' disabled';
12830             }
12831             
12832             fillMonths.cn.push({
12833                 tag: 'td',
12834                 cls: 'day ' + clsName,
12835                 html: prevMonth.getDate()
12836             })
12837             
12838             prevMonth.setDate(prevMonth.getDate()+1);
12839         }
12840           
12841         var currentYear = this.date && this.date.getUTCFullYear();
12842         var currentMonth = this.date && this.date.getUTCMonth();
12843         
12844         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12845         
12846         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12847             v.removeClass('active');
12848             
12849             if(currentYear === year && k === currentMonth){
12850                 v.addClass('active');
12851             }
12852             
12853             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12854                 v.addClass('disabled');
12855             }
12856             
12857         });
12858         
12859         
12860         year = parseInt(year/10, 10) * 10;
12861         
12862         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12863         
12864         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12865         
12866         year -= 1;
12867         for (var i = -1; i < 11; i++) {
12868             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12869                 tag: 'span',
12870                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12871                 html: year
12872             })
12873             
12874             year += 1;
12875         }
12876     },
12877     
12878     showMode: function(dir) {
12879         if (dir) {
12880             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12881         }
12882         Roo.each(this.picker().select('>div',true).elements, function(v){
12883             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12884             v.hide();
12885         });
12886         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12887     },
12888     
12889     place: function()
12890     {
12891         if(this.isInline) return;
12892         
12893         this.picker().removeClass(['bottom', 'top']);
12894         
12895         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12896             /*
12897              * place to the top of element!
12898              *
12899              */
12900             
12901             this.picker().addClass('top');
12902             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12903             
12904             return;
12905         }
12906         
12907         this.picker().addClass('bottom');
12908         
12909         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12910     },
12911     
12912     parseDate : function(value){
12913         if(!value || value instanceof Date){
12914             return value;
12915         }
12916         var v = Date.parseDate(value, this.format);
12917         if (!v && this.useIso) {
12918             v = Date.parseDate(value, 'Y-m-d');
12919         }
12920         if(!v && this.altFormats){
12921             if(!this.altFormatsArray){
12922                 this.altFormatsArray = this.altFormats.split("|");
12923             }
12924             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12925                 v = Date.parseDate(value, this.altFormatsArray[i]);
12926             }
12927         }
12928         return v;
12929     },
12930     
12931     formatDate : function(date, fmt){
12932         return (!date || !(date instanceof Date)) ?
12933         date : date.dateFormat(fmt || this.format);
12934     },
12935     
12936     onFocus : function()
12937     {
12938         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12939         this.show();
12940     },
12941     
12942     onBlur : function()
12943     {
12944         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12945         this.hide();
12946     },
12947     
12948     show : function()
12949     {
12950         this.picker().show();
12951         this.update();
12952         this.place();
12953         
12954         this.fireEvent('show', this, this.date);
12955     },
12956     
12957     hide : function()
12958     {
12959         if(this.isInline) return;
12960         this.picker().hide();
12961         this.viewMode = this.startViewMode;
12962         this.showMode();
12963         
12964         this.fireEvent('hide', this, this.date);
12965         
12966     },
12967     
12968     onMousedown: function(e){
12969         e.stopPropagation();
12970         e.preventDefault();
12971     },
12972     
12973     keyup: function(e){
12974         Roo.bootstrap.DateField.superclass.keyup.call(this);
12975         this.update();
12976         
12977     },
12978
12979     setValue: function(v){
12980         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12981         
12982         this.fireEvent('select', this, this.date);
12983         
12984     },
12985     
12986     fireKey: function(e){
12987         if (!this.picker().isVisible()){
12988             if (e.keyCode == 27) // allow escape to hide and re-show picker
12989                 this.show();
12990             return;
12991         }
12992         var dateChanged = false,
12993         dir, day, month,
12994         newDate, newViewDate;
12995         switch(e.keyCode){
12996             case 27: // escape
12997                 this.hide();
12998                 e.preventDefault();
12999                 break;
13000             case 37: // left
13001             case 39: // right
13002                 if (!this.keyboardNavigation) break;
13003                 dir = e.keyCode == 37 ? -1 : 1;
13004                 
13005                 if (e.ctrlKey){
13006                     newDate = this.moveYear(this.date, dir);
13007                     newViewDate = this.moveYear(this.viewDate, dir);
13008                 } else if (e.shiftKey){
13009                     newDate = this.moveMonth(this.date, dir);
13010                     newViewDate = this.moveMonth(this.viewDate, dir);
13011                 } else {
13012                     newDate = new Date(this.date);
13013                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13014                     newViewDate = new Date(this.viewDate);
13015                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13016                 }
13017                 if (this.dateWithinRange(newDate)){
13018                     this.date = newDate;
13019                     this.viewDate = newViewDate;
13020                     this.setValue(this.formatDate(this.date));
13021                     this.update();
13022                     e.preventDefault();
13023                     dateChanged = true;
13024                 }
13025                 break;
13026             case 38: // up
13027             case 40: // down
13028                 if (!this.keyboardNavigation) break;
13029                 dir = e.keyCode == 38 ? -1 : 1;
13030                 if (e.ctrlKey){
13031                     newDate = this.moveYear(this.date, dir);
13032                     newViewDate = this.moveYear(this.viewDate, dir);
13033                 } else if (e.shiftKey){
13034                     newDate = this.moveMonth(this.date, dir);
13035                     newViewDate = this.moveMonth(this.viewDate, dir);
13036                 } else {
13037                     newDate = new Date(this.date);
13038                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13039                     newViewDate = new Date(this.viewDate);
13040                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13041                 }
13042                 if (this.dateWithinRange(newDate)){
13043                     this.date = newDate;
13044                     this.viewDate = newViewDate;
13045                     this.setValue(this.formatDate(this.date));
13046                     this.update();
13047                     e.preventDefault();
13048                     dateChanged = true;
13049                 }
13050                 break;
13051             case 13: // enter
13052                 this.setValue(this.formatDate(this.date));
13053                 this.hide();
13054                 e.preventDefault();
13055                 break;
13056             case 9: // tab
13057                 this.setValue(this.formatDate(this.date));
13058                 this.hide();
13059                 break;
13060         }
13061     },
13062     
13063     
13064     onClick: function(e) {
13065         e.stopPropagation();
13066         e.preventDefault();
13067         
13068         var target = e.getTarget();
13069         
13070         if(target.nodeName.toLowerCase() === 'i'){
13071             target = Roo.get(target).dom.parentNode;
13072         }
13073         
13074         var nodeName = target.nodeName;
13075         var className = target.className;
13076         var html = target.innerHTML;
13077         
13078         switch(nodeName.toLowerCase()) {
13079             case 'th':
13080                 switch(className) {
13081                     case 'switch':
13082                         this.showMode(1);
13083                         break;
13084                     case 'prev':
13085                     case 'next':
13086                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13087                         switch(this.viewMode){
13088                                 case 0:
13089                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13090                                         break;
13091                                 case 1:
13092                                 case 2:
13093                                         this.viewDate = this.moveYear(this.viewDate, dir);
13094                                         break;
13095                         }
13096                         this.fill();
13097                         break;
13098                     case 'today':
13099                         var date = new Date();
13100                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13101                         this.fill()
13102                         this.setValue(this.formatDate(this.date));
13103                         this.hide();
13104                         break;
13105                 }
13106                 break;
13107             case 'span':
13108                 if (className.indexOf('disabled') === -1) {
13109                     this.viewDate.setUTCDate(1);
13110                     if (className.indexOf('month') !== -1) {
13111                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13112                     } else {
13113                         var year = parseInt(html, 10) || 0;
13114                         this.viewDate.setUTCFullYear(year);
13115                         
13116                     }
13117                     this.showMode(-1);
13118                     this.fill();
13119                 }
13120                 break;
13121                 
13122             case 'td':
13123                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13124                     var day = parseInt(html, 10) || 1;
13125                     var year = this.viewDate.getUTCFullYear(),
13126                         month = this.viewDate.getUTCMonth();
13127
13128                     if (className.indexOf('old') !== -1) {
13129                         if(month === 0 ){
13130                             month = 11;
13131                             year -= 1;
13132                         }else{
13133                             month -= 1;
13134                         }
13135                     } else if (className.indexOf('new') !== -1) {
13136                         if (month == 11) {
13137                             month = 0;
13138                             year += 1;
13139                         } else {
13140                             month += 1;
13141                         }
13142                     }
13143                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13144                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13145                     this.fill();
13146                     this.setValue(this.formatDate(this.date));
13147                     this.hide();
13148                 }
13149                 break;
13150         }
13151     },
13152     
13153     setStartDate: function(startDate){
13154         this.startDate = startDate || -Infinity;
13155         if (this.startDate !== -Infinity) {
13156             this.startDate = this.parseDate(this.startDate);
13157         }
13158         this.update();
13159         this.updateNavArrows();
13160     },
13161
13162     setEndDate: function(endDate){
13163         this.endDate = endDate || Infinity;
13164         if (this.endDate !== Infinity) {
13165             this.endDate = this.parseDate(this.endDate);
13166         }
13167         this.update();
13168         this.updateNavArrows();
13169     },
13170     
13171     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13172         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13173         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13174             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13175         }
13176         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13177             return parseInt(d, 10);
13178         });
13179         this.update();
13180         this.updateNavArrows();
13181     },
13182     
13183     updateNavArrows: function() {
13184         var d = new Date(this.viewDate),
13185         year = d.getUTCFullYear(),
13186         month = d.getUTCMonth();
13187         
13188         Roo.each(this.picker().select('.prev', true).elements, function(v){
13189             v.show();
13190             switch (this.viewMode) {
13191                 case 0:
13192
13193                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13194                         v.hide();
13195                     }
13196                     break;
13197                 case 1:
13198                 case 2:
13199                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13200                         v.hide();
13201                     }
13202                     break;
13203             }
13204         });
13205         
13206         Roo.each(this.picker().select('.next', true).elements, function(v){
13207             v.show();
13208             switch (this.viewMode) {
13209                 case 0:
13210
13211                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13212                         v.hide();
13213                     }
13214                     break;
13215                 case 1:
13216                 case 2:
13217                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13218                         v.hide();
13219                     }
13220                     break;
13221             }
13222         })
13223     },
13224     
13225     moveMonth: function(date, dir){
13226         if (!dir) return date;
13227         var new_date = new Date(date.valueOf()),
13228         day = new_date.getUTCDate(),
13229         month = new_date.getUTCMonth(),
13230         mag = Math.abs(dir),
13231         new_month, test;
13232         dir = dir > 0 ? 1 : -1;
13233         if (mag == 1){
13234             test = dir == -1
13235             // If going back one month, make sure month is not current month
13236             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13237             ? function(){
13238                 return new_date.getUTCMonth() == month;
13239             }
13240             // If going forward one month, make sure month is as expected
13241             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13242             : function(){
13243                 return new_date.getUTCMonth() != new_month;
13244             };
13245             new_month = month + dir;
13246             new_date.setUTCMonth(new_month);
13247             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13248             if (new_month < 0 || new_month > 11)
13249                 new_month = (new_month + 12) % 12;
13250         } else {
13251             // For magnitudes >1, move one month at a time...
13252             for (var i=0; i<mag; i++)
13253                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13254                 new_date = this.moveMonth(new_date, dir);
13255             // ...then reset the day, keeping it in the new month
13256             new_month = new_date.getUTCMonth();
13257             new_date.setUTCDate(day);
13258             test = function(){
13259                 return new_month != new_date.getUTCMonth();
13260             };
13261         }
13262         // Common date-resetting loop -- if date is beyond end of month, make it
13263         // end of month
13264         while (test()){
13265             new_date.setUTCDate(--day);
13266             new_date.setUTCMonth(new_month);
13267         }
13268         return new_date;
13269     },
13270
13271     moveYear: function(date, dir){
13272         return this.moveMonth(date, dir*12);
13273     },
13274
13275     dateWithinRange: function(date){
13276         return date >= this.startDate && date <= this.endDate;
13277     },
13278
13279     
13280     remove: function() {
13281         this.picker().remove();
13282     }
13283    
13284 });
13285
13286 Roo.apply(Roo.bootstrap.DateField,  {
13287     
13288     head : {
13289         tag: 'thead',
13290         cn: [
13291         {
13292             tag: 'tr',
13293             cn: [
13294             {
13295                 tag: 'th',
13296                 cls: 'prev',
13297                 html: '<i class="icon-arrow-left"/>'
13298             },
13299             {
13300                 tag: 'th',
13301                 cls: 'switch',
13302                 colspan: '5'
13303             },
13304             {
13305                 tag: 'th',
13306                 cls: 'next',
13307                 html: '<i class="icon-arrow-right"/>'
13308             }
13309
13310             ]
13311         }
13312         ]
13313     },
13314     
13315     content : {
13316         tag: 'tbody',
13317         cn: [
13318         {
13319             tag: 'tr',
13320             cn: [
13321             {
13322                 tag: 'td',
13323                 colspan: '7'
13324             }
13325             ]
13326         }
13327         ]
13328     },
13329     
13330     footer : {
13331         tag: 'tfoot',
13332         cn: [
13333         {
13334             tag: 'tr',
13335             cn: [
13336             {
13337                 tag: 'th',
13338                 colspan: '7',
13339                 cls: 'today'
13340             }
13341                     
13342             ]
13343         }
13344         ]
13345     },
13346     
13347     dates:{
13348         en: {
13349             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13350             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13351             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13352             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13353             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13354             today: "Today"
13355         }
13356     },
13357     
13358     modes: [
13359     {
13360         clsName: 'days',
13361         navFnc: 'Month',
13362         navStep: 1
13363     },
13364     {
13365         clsName: 'months',
13366         navFnc: 'FullYear',
13367         navStep: 1
13368     },
13369     {
13370         clsName: 'years',
13371         navFnc: 'FullYear',
13372         navStep: 10
13373     }]
13374 });
13375
13376 Roo.apply(Roo.bootstrap.DateField,  {
13377   
13378     template : {
13379         tag: 'div',
13380         cls: 'datepicker dropdown-menu',
13381         cn: [
13382         {
13383             tag: 'div',
13384             cls: 'datepicker-days',
13385             cn: [
13386             {
13387                 tag: 'table',
13388                 cls: 'table-condensed',
13389                 cn:[
13390                 Roo.bootstrap.DateField.head,
13391                 {
13392                     tag: 'tbody'
13393                 },
13394                 Roo.bootstrap.DateField.footer
13395                 ]
13396             }
13397             ]
13398         },
13399         {
13400             tag: 'div',
13401             cls: 'datepicker-months',
13402             cn: [
13403             {
13404                 tag: 'table',
13405                 cls: 'table-condensed',
13406                 cn:[
13407                 Roo.bootstrap.DateField.head,
13408                 Roo.bootstrap.DateField.content,
13409                 Roo.bootstrap.DateField.footer
13410                 ]
13411             }
13412             ]
13413         },
13414         {
13415             tag: 'div',
13416             cls: 'datepicker-years',
13417             cn: [
13418             {
13419                 tag: 'table',
13420                 cls: 'table-condensed',
13421                 cn:[
13422                 Roo.bootstrap.DateField.head,
13423                 Roo.bootstrap.DateField.content,
13424                 Roo.bootstrap.DateField.footer
13425                 ]
13426             }
13427             ]
13428         }
13429         ]
13430     }
13431 });
13432
13433  
13434
13435  /*
13436  * - LGPL
13437  *
13438  * TimeField
13439  * 
13440  */
13441
13442 /**
13443  * @class Roo.bootstrap.TimeField
13444  * @extends Roo.bootstrap.Input
13445  * Bootstrap DateField class
13446  * 
13447  * 
13448  * @constructor
13449  * Create a new TimeField
13450  * @param {Object} config The config object
13451  */
13452
13453 Roo.bootstrap.TimeField = function(config){
13454     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13455     this.addEvents({
13456             /**
13457              * @event show
13458              * Fires when this field show.
13459              * @param {Roo.bootstrap.DateField} this
13460              * @param {Mixed} date The date value
13461              */
13462             show : true,
13463             /**
13464              * @event show
13465              * Fires when this field hide.
13466              * @param {Roo.bootstrap.DateField} this
13467              * @param {Mixed} date The date value
13468              */
13469             hide : true,
13470             /**
13471              * @event select
13472              * Fires when select a date.
13473              * @param {Roo.bootstrap.DateField} this
13474              * @param {Mixed} date The date value
13475              */
13476             select : true
13477         });
13478 };
13479
13480 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13481     
13482     /**
13483      * @cfg {String} format
13484      * The default time format string which can be overriden for localization support.  The format must be
13485      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13486      */
13487     format : "H:i",
13488        
13489     onRender: function(ct, position)
13490     {
13491         
13492         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13493                 
13494         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13495         
13496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13497         
13498         this.pop = this.picker().select('>.datepicker-time',true).first();
13499         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13500         
13501         this.picker().on('mousedown', this.onMousedown, this);
13502         this.picker().on('click', this.onClick, this);
13503         
13504         this.picker().addClass('datepicker-dropdown');
13505     
13506         this.fillTime();
13507         this.update();
13508             
13509         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13510         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13511         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13512         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13513         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13514         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13515
13516     },
13517     
13518     fireKey: function(e){
13519         if (!this.picker().isVisible()){
13520             if (e.keyCode == 27) // allow escape to hide and re-show picker
13521                 this.show();
13522             return;
13523         }
13524
13525         e.preventDefault();
13526         
13527         switch(e.keyCode){
13528             case 27: // escape
13529                 this.hide();
13530                 break;
13531             case 37: // left
13532             case 39: // right
13533                 this.onTogglePeriod();
13534                 break;
13535             case 38: // up
13536                 this.onIncrementMinutes();
13537                 break;
13538             case 40: // down
13539                 this.onDecrementMinutes();
13540                 break;
13541             case 13: // enter
13542             case 9: // tab
13543                 this.setTime();
13544                 break;
13545         }
13546     },
13547     
13548     onClick: function(e) {
13549         e.stopPropagation();
13550         e.preventDefault();
13551     },
13552     
13553     picker : function()
13554     {
13555         return this.el.select('.datepicker', true).first();
13556     },
13557     
13558     fillTime: function()
13559     {    
13560         var time = this.pop.select('tbody', true).first();
13561         
13562         time.dom.innerHTML = '';
13563         
13564         time.createChild({
13565             tag: 'tr',
13566             cn: [
13567                 {
13568                     tag: 'td',
13569                     cn: [
13570                         {
13571                             tag: 'a',
13572                             href: '#',
13573                             cls: 'btn',
13574                             cn: [
13575                                 {
13576                                     tag: 'span',
13577                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13578                                 }
13579                             ]
13580                         } 
13581                     ]
13582                 },
13583                 {
13584                     tag: 'td',
13585                     cls: 'separator'
13586                 },
13587                 {
13588                     tag: 'td',
13589                     cn: [
13590                         {
13591                             tag: 'a',
13592                             href: '#',
13593                             cls: 'btn',
13594                             cn: [
13595                                 {
13596                                     tag: 'span',
13597                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13598                                 }
13599                             ]
13600                         }
13601                     ]
13602                 },
13603                 {
13604                     tag: 'td',
13605                     cls: 'separator'
13606                 }
13607             ]
13608         });
13609         
13610         time.createChild({
13611             tag: 'tr',
13612             cn: [
13613                 {
13614                     tag: 'td',
13615                     cn: [
13616                         {
13617                             tag: 'span',
13618                             cls: 'timepicker-hour',
13619                             html: '00'
13620                         }  
13621                     ]
13622                 },
13623                 {
13624                     tag: 'td',
13625                     cls: 'separator',
13626                     html: ':'
13627                 },
13628                 {
13629                     tag: 'td',
13630                     cn: [
13631                         {
13632                             tag: 'span',
13633                             cls: 'timepicker-minute',
13634                             html: '00'
13635                         }  
13636                     ]
13637                 },
13638                 {
13639                     tag: 'td',
13640                     cls: 'separator'
13641                 },
13642                 {
13643                     tag: 'td',
13644                     cn: [
13645                         {
13646                             tag: 'button',
13647                             type: 'button',
13648                             cls: 'btn btn-primary period',
13649                             html: 'AM'
13650                             
13651                         }
13652                     ]
13653                 }
13654             ]
13655         });
13656         
13657         time.createChild({
13658             tag: 'tr',
13659             cn: [
13660                 {
13661                     tag: 'td',
13662                     cn: [
13663                         {
13664                             tag: 'a',
13665                             href: '#',
13666                             cls: 'btn',
13667                             cn: [
13668                                 {
13669                                     tag: 'span',
13670                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13671                                 }
13672                             ]
13673                         }
13674                     ]
13675                 },
13676                 {
13677                     tag: 'td',
13678                     cls: 'separator'
13679                 },
13680                 {
13681                     tag: 'td',
13682                     cn: [
13683                         {
13684                             tag: 'a',
13685                             href: '#',
13686                             cls: 'btn',
13687                             cn: [
13688                                 {
13689                                     tag: 'span',
13690                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13691                                 }
13692                             ]
13693                         }
13694                     ]
13695                 },
13696                 {
13697                     tag: 'td',
13698                     cls: 'separator'
13699                 }
13700             ]
13701         });
13702         
13703     },
13704     
13705     update: function()
13706     {
13707         
13708         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13709         
13710         this.fill();
13711     },
13712     
13713     fill: function() 
13714     {
13715         var hours = this.time.getHours();
13716         var minutes = this.time.getMinutes();
13717         var period = 'AM';
13718         
13719         if(hours > 11){
13720             period = 'PM';
13721         }
13722         
13723         if(hours == 0){
13724             hours = 12;
13725         }
13726         
13727         
13728         if(hours > 12){
13729             hours = hours - 12;
13730         }
13731         
13732         if(hours < 10){
13733             hours = '0' + hours;
13734         }
13735         
13736         if(minutes < 10){
13737             minutes = '0' + minutes;
13738         }
13739         
13740         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13741         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13742         this.pop.select('button', true).first().dom.innerHTML = period;
13743         
13744     },
13745     
13746     place: function()
13747     {   
13748         this.picker().removeClass(['bottom', 'top']);
13749         
13750         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13751             /*
13752              * place to the top of element!
13753              *
13754              */
13755             
13756             this.picker().addClass('top');
13757             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13758             
13759             return;
13760         }
13761         
13762         this.picker().addClass('bottom');
13763         
13764         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13765     },
13766   
13767     onFocus : function()
13768     {
13769         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13770         this.show();
13771     },
13772     
13773     onBlur : function()
13774     {
13775         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13776         this.hide();
13777     },
13778     
13779     show : function()
13780     {
13781         this.picker().show();
13782         this.pop.show();
13783         this.update();
13784         this.place();
13785         
13786         this.fireEvent('show', this, this.date);
13787     },
13788     
13789     hide : function()
13790     {
13791         this.picker().hide();
13792         this.pop.hide();
13793         
13794         this.fireEvent('hide', this, this.date);
13795     },
13796     
13797     setTime : function()
13798     {
13799         this.hide();
13800         this.setValue(this.time.format(this.format));
13801         
13802         this.fireEvent('select', this, this.date);
13803         
13804         
13805     },
13806     
13807     onMousedown: function(e){
13808         e.stopPropagation();
13809         e.preventDefault();
13810     },
13811     
13812     onIncrementHours: function()
13813     {
13814         Roo.log('onIncrementHours');
13815         this.time = this.time.add(Date.HOUR, 1);
13816         this.update();
13817         
13818     },
13819     
13820     onDecrementHours: function()
13821     {
13822         Roo.log('onDecrementHours');
13823         this.time = this.time.add(Date.HOUR, -1);
13824         this.update();
13825     },
13826     
13827     onIncrementMinutes: function()
13828     {
13829         Roo.log('onIncrementMinutes');
13830         this.time = this.time.add(Date.MINUTE, 1);
13831         this.update();
13832     },
13833     
13834     onDecrementMinutes: function()
13835     {
13836         Roo.log('onDecrementMinutes');
13837         this.time = this.time.add(Date.MINUTE, -1);
13838         this.update();
13839     },
13840     
13841     onTogglePeriod: function()
13842     {
13843         Roo.log('onTogglePeriod');
13844         this.time = this.time.add(Date.HOUR, 12);
13845         this.update();
13846     }
13847     
13848    
13849 });
13850
13851 Roo.apply(Roo.bootstrap.TimeField,  {
13852     
13853     content : {
13854         tag: 'tbody',
13855         cn: [
13856             {
13857                 tag: 'tr',
13858                 cn: [
13859                 {
13860                     tag: 'td',
13861                     colspan: '7'
13862                 }
13863                 ]
13864             }
13865         ]
13866     },
13867     
13868     footer : {
13869         tag: 'tfoot',
13870         cn: [
13871             {
13872                 tag: 'tr',
13873                 cn: [
13874                 {
13875                     tag: 'th',
13876                     colspan: '7',
13877                     cls: '',
13878                     cn: [
13879                         {
13880                             tag: 'button',
13881                             cls: 'btn btn-info ok',
13882                             html: 'OK'
13883                         }
13884                     ]
13885                 }
13886
13887                 ]
13888             }
13889         ]
13890     }
13891 });
13892
13893 Roo.apply(Roo.bootstrap.TimeField,  {
13894   
13895     template : {
13896         tag: 'div',
13897         cls: 'datepicker dropdown-menu',
13898         cn: [
13899             {
13900                 tag: 'div',
13901                 cls: 'datepicker-time',
13902                 cn: [
13903                 {
13904                     tag: 'table',
13905                     cls: 'table-condensed',
13906                     cn:[
13907                     Roo.bootstrap.TimeField.content,
13908                     Roo.bootstrap.TimeField.footer
13909                     ]
13910                 }
13911                 ]
13912             }
13913         ]
13914     }
13915 });
13916
13917  
13918
13919  /*
13920  * - LGPL
13921  *
13922  * CheckBox
13923  * 
13924  */
13925
13926 /**
13927  * @class Roo.bootstrap.CheckBox
13928  * @extends Roo.bootstrap.Input
13929  * Bootstrap CheckBox class
13930  * 
13931  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13932  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13933  * @cfg {String} boxLabel The text that appears beside the checkbox
13934  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13935  * @cfg {Boolean} checked initnal the element
13936  * 
13937  * 
13938  * @constructor
13939  * Create a new CheckBox
13940  * @param {Object} config The config object
13941  */
13942
13943 Roo.bootstrap.CheckBox = function(config){
13944     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13945    
13946         this.addEvents({
13947             /**
13948             * @event check
13949             * Fires when the element is checked or unchecked.
13950             * @param {Roo.bootstrap.CheckBox} this This input
13951             * @param {Boolean} checked The new checked value
13952             */
13953            check : true
13954         });
13955 };
13956
13957 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13958     
13959     inputType: 'checkbox',
13960     inputValue: 1,
13961     valueOff: 0,
13962     boxLabel: false,
13963     checked: false,
13964     weight : false,
13965     
13966     getAutoCreate : function()
13967     {
13968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13969         
13970         var id = Roo.id();
13971         
13972         var cfg = {};
13973         
13974         cfg.cls = 'form-group checkbox' //input-group
13975         
13976         
13977         
13978         
13979         var input =  {
13980             tag: 'input',
13981             id : id,
13982             type : this.inputType,
13983             value : (!this.checked) ? this.valueOff : this.inputValue,
13984             cls : 'roo-checkbox', //'form-box',
13985             placeholder : this.placeholder || ''
13986             
13987         };
13988         
13989         if (this.weight) { // Validity check?
13990             cfg.cls += " checkbox-" + this.weight;
13991         }
13992         
13993         if (this.disabled) {
13994             input.disabled=true;
13995         }
13996         
13997         if(this.checked){
13998             input.checked = this.checked;
13999         }
14000         
14001         if (this.name) {
14002             input.name = this.name;
14003         }
14004         
14005         if (this.size) {
14006             input.cls += ' input-' + this.size;
14007         }
14008         
14009         var settings=this;
14010         ['xs','sm','md','lg'].map(function(size){
14011             if (settings[size]) {
14012                 cfg.cls += ' col-' + size + '-' + settings[size];
14013             }
14014         });
14015         
14016        
14017         
14018         var inputblock = input;
14019         
14020         
14021         
14022         
14023         if (this.before || this.after) {
14024             
14025             inputblock = {
14026                 cls : 'input-group',
14027                 cn :  [] 
14028             };
14029             if (this.before) {
14030                 inputblock.cn.push({
14031                     tag :'span',
14032                     cls : 'input-group-addon',
14033                     html : this.before
14034                 });
14035             }
14036             inputblock.cn.push(input);
14037             if (this.after) {
14038                 inputblock.cn.push({
14039                     tag :'span',
14040                     cls : 'input-group-addon',
14041                     html : this.after
14042                 });
14043             }
14044             
14045         };
14046         
14047         if (align ==='left' && this.fieldLabel.length) {
14048                 Roo.log("left and has label");
14049                 cfg.cn = [
14050                     
14051                     {
14052                         tag: 'label',
14053                         'for' :  id,
14054                         cls : 'control-label col-md-' + this.labelWidth,
14055                         html : this.fieldLabel
14056                         
14057                     },
14058                     {
14059                         cls : "col-md-" + (12 - this.labelWidth), 
14060                         cn: [
14061                             inputblock
14062                         ]
14063                     }
14064                     
14065                 ];
14066         } else if ( this.fieldLabel.length) {
14067                 Roo.log(" label");
14068                 cfg.cn = [
14069                    
14070                     {
14071                         tag: this.boxLabel ? 'span' : 'label',
14072                         'for': id,
14073                         cls: 'control-label box-input-label',
14074                         //cls : 'input-group-addon',
14075                         html : this.fieldLabel
14076                         
14077                     },
14078                     
14079                     inputblock
14080                     
14081                 ];
14082
14083         } else {
14084             
14085                 Roo.log(" no label && no align");
14086                 cfg.cn = [  inputblock ] ;
14087                 
14088                 
14089         };
14090          if(this.boxLabel){
14091             cfg.cn.push( {
14092                 tag: 'label',
14093                 'for': id,
14094                 cls: 'box-label',
14095                 html: this.boxLabel
14096                 
14097             });
14098         }
14099         
14100         
14101        
14102         return cfg;
14103         
14104     },
14105     
14106     /**
14107      * return the real input element.
14108      */
14109     inputEl: function ()
14110     {
14111         return this.el.select('input.roo-checkbox',true).first();
14112     },
14113     
14114     label: function()
14115     {
14116         return this.el.select('label.control-label',true).first();
14117     },
14118     
14119     initEvents : function()
14120     {
14121 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14122         
14123         this.inputEl().on('click', this.onClick,  this);
14124         
14125     },
14126     
14127     onClick : function()
14128     {   
14129         this.setChecked(!this.checked);
14130     },
14131     
14132     setChecked : function(state,suppressEvent)
14133     {
14134         this.checked = state;
14135         
14136         this.inputEl().dom.checked = state;
14137         
14138         if(suppressEvent !== true){
14139             this.fireEvent('check', this, state);
14140         }
14141         
14142         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14143         
14144     },
14145     
14146     setValue : function(v,suppressEvent)
14147     {
14148         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14149     }
14150     
14151 });
14152
14153  
14154 /*
14155  * - LGPL
14156  *
14157  * Radio
14158  * 
14159  */
14160
14161 /**
14162  * @class Roo.bootstrap.Radio
14163  * @extends Roo.bootstrap.CheckBox
14164  * Bootstrap Radio class
14165
14166  * @constructor
14167  * Create a new Radio
14168  * @param {Object} config The config object
14169  */
14170
14171 Roo.bootstrap.Radio = function(config){
14172     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14173    
14174 };
14175
14176 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14177     
14178     inputType: 'radio',
14179     inputValue: '',
14180     valueOff: '',
14181     
14182     getAutoCreate : function()
14183     {
14184         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14185         
14186         var id = Roo.id();
14187         
14188         var cfg = {};
14189         
14190         cfg.cls = 'form-group radio' //input-group
14191         
14192         var input =  {
14193             tag: 'input',
14194             id : id,
14195             type : this.inputType,
14196             value : (!this.checked) ? this.valueOff : this.inputValue,
14197             cls : 'roo-radio',
14198             placeholder : this.placeholder || ''
14199             
14200         };
14201           if (this.weight) { // Validity check?
14202             cfg.cls += " radio-" + this.weight;
14203         }
14204         if (this.disabled) {
14205             input.disabled=true;
14206         }
14207         
14208         if(this.checked){
14209             input.checked = this.checked;
14210         }
14211         
14212         if (this.name) {
14213             input.name = this.name;
14214         }
14215         
14216         if (this.size) {
14217             input.cls += ' input-' + this.size;
14218         }
14219         
14220         var settings=this;
14221         ['xs','sm','md','lg'].map(function(size){
14222             if (settings[size]) {
14223                 cfg.cls += ' col-' + size + '-' + settings[size];
14224             }
14225         });
14226         
14227         var inputblock = input;
14228         
14229         if (this.before || this.after) {
14230             
14231             inputblock = {
14232                 cls : 'input-group',
14233                 cn :  [] 
14234             };
14235             if (this.before) {
14236                 inputblock.cn.push({
14237                     tag :'span',
14238                     cls : 'input-group-addon',
14239                     html : this.before
14240                 });
14241             }
14242             inputblock.cn.push(input);
14243             if (this.after) {
14244                 inputblock.cn.push({
14245                     tag :'span',
14246                     cls : 'input-group-addon',
14247                     html : this.after
14248                 });
14249             }
14250             
14251         };
14252         
14253         if (align ==='left' && this.fieldLabel.length) {
14254                 Roo.log("left and has label");
14255                 cfg.cn = [
14256                     
14257                     {
14258                         tag: 'label',
14259                         'for' :  id,
14260                         cls : 'control-label col-md-' + this.labelWidth,
14261                         html : this.fieldLabel
14262                         
14263                     },
14264                     {
14265                         cls : "col-md-" + (12 - this.labelWidth), 
14266                         cn: [
14267                             inputblock
14268                         ]
14269                     }
14270                     
14271                 ];
14272         } else if ( this.fieldLabel.length) {
14273                 Roo.log(" label");
14274                  cfg.cn = [
14275                    
14276                     {
14277                         tag: 'label',
14278                         'for': id,
14279                         cls: 'control-label box-input-label',
14280                         //cls : 'input-group-addon',
14281                         html : this.fieldLabel
14282                         
14283                     },
14284                     
14285                     inputblock
14286                     
14287                 ];
14288
14289         } else {
14290             
14291                    Roo.log(" no label && no align");
14292                 cfg.cn = [
14293                     
14294                         inputblock
14295                     
14296                 ];
14297                 
14298                 
14299         };
14300         
14301         if(this.boxLabel){
14302             cfg.cn.push({
14303                 tag: 'label',
14304                 'for': id,
14305                 cls: 'box-label',
14306                 html: this.boxLabel
14307             })
14308         }
14309         
14310         return cfg;
14311         
14312     },
14313     inputEl: function ()
14314     {
14315         return this.el.select('input.roo-radio',true).first();
14316     },
14317     onClick : function()
14318     {   
14319         this.setChecked(true);
14320     },
14321     
14322     setChecked : function(state,suppressEvent)
14323     {
14324         if(state){
14325             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14326                 v.dom.checked = false;
14327             });
14328         }
14329         
14330         this.checked = state;
14331         this.inputEl().dom.checked = state;
14332         
14333         if(suppressEvent !== true){
14334             this.fireEvent('check', this, state);
14335         }
14336         
14337         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14338         
14339     },
14340     
14341     getGroupValue : function()
14342     {
14343         var value = ''
14344         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14345             if(v.dom.checked == true){
14346                 value = v.dom.value;
14347             }
14348         });
14349         
14350         return value;
14351     },
14352     
14353     /**
14354      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14355      * @return {Mixed} value The field value
14356      */
14357     getValue : function(){
14358         return this.getGroupValue();
14359     }
14360     
14361 });
14362
14363  
14364 //<script type="text/javascript">
14365
14366 /*
14367  * Based  Ext JS Library 1.1.1
14368  * Copyright(c) 2006-2007, Ext JS, LLC.
14369  * LGPL
14370  *
14371  */
14372  
14373 /**
14374  * @class Roo.HtmlEditorCore
14375  * @extends Roo.Component
14376  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14377  *
14378  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14379  */
14380
14381 Roo.HtmlEditorCore = function(config){
14382     
14383     
14384     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14385     this.addEvents({
14386         /**
14387          * @event initialize
14388          * Fires when the editor is fully initialized (including the iframe)
14389          * @param {Roo.HtmlEditorCore} this
14390          */
14391         initialize: true,
14392         /**
14393          * @event activate
14394          * Fires when the editor is first receives the focus. Any insertion must wait
14395          * until after this event.
14396          * @param {Roo.HtmlEditorCore} this
14397          */
14398         activate: true,
14399          /**
14400          * @event beforesync
14401          * Fires before the textarea is updated with content from the editor iframe. Return false
14402          * to cancel the sync.
14403          * @param {Roo.HtmlEditorCore} this
14404          * @param {String} html
14405          */
14406         beforesync: true,
14407          /**
14408          * @event beforepush
14409          * Fires before the iframe editor is updated with content from the textarea. Return false
14410          * to cancel the push.
14411          * @param {Roo.HtmlEditorCore} this
14412          * @param {String} html
14413          */
14414         beforepush: true,
14415          /**
14416          * @event sync
14417          * Fires when the textarea is updated with content from the editor iframe.
14418          * @param {Roo.HtmlEditorCore} this
14419          * @param {String} html
14420          */
14421         sync: true,
14422          /**
14423          * @event push
14424          * Fires when the iframe editor is updated with content from the textarea.
14425          * @param {Roo.HtmlEditorCore} this
14426          * @param {String} html
14427          */
14428         push: true,
14429         
14430         /**
14431          * @event editorevent
14432          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14433          * @param {Roo.HtmlEditorCore} this
14434          */
14435         editorevent: true
14436     });
14437      
14438 };
14439
14440
14441 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14442
14443
14444      /**
14445      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14446      */
14447     
14448     owner : false,
14449     
14450      /**
14451      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14452      *                        Roo.resizable.
14453      */
14454     resizable : false,
14455      /**
14456      * @cfg {Number} height (in pixels)
14457      */   
14458     height: 300,
14459    /**
14460      * @cfg {Number} width (in pixels)
14461      */   
14462     width: 500,
14463     
14464     /**
14465      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14466      * 
14467      */
14468     stylesheets: false,
14469     
14470     // id of frame..
14471     frameId: false,
14472     
14473     // private properties
14474     validationEvent : false,
14475     deferHeight: true,
14476     initialized : false,
14477     activated : false,
14478     sourceEditMode : false,
14479     onFocus : Roo.emptyFn,
14480     iframePad:3,
14481     hideMode:'offsets',
14482     
14483     clearUp: true,
14484     
14485      
14486     
14487
14488     /**
14489      * Protected method that will not generally be called directly. It
14490      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14491      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14492      */
14493     getDocMarkup : function(){
14494         // body styles..
14495         var st = '';
14496         Roo.log(this.stylesheets);
14497         
14498         // inherit styels from page...?? 
14499         if (this.stylesheets === false) {
14500             
14501             Roo.get(document.head).select('style').each(function(node) {
14502                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14503             });
14504             
14505             Roo.get(document.head).select('link').each(function(node) { 
14506                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14507             });
14508             
14509         } else if (!this.stylesheets.length) {
14510                 // simple..
14511                 st = '<style type="text/css">' +
14512                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14513                    '</style>';
14514         } else {
14515             Roo.each(this.stylesheets, function(s) {
14516                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14517             });
14518             
14519         }
14520         
14521         st +=  '<style type="text/css">' +
14522             'IMG { cursor: pointer } ' +
14523         '</style>';
14524
14525         
14526         return '<html><head>' + st  +
14527             //<style type="text/css">' +
14528             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14529             //'</style>' +
14530             ' </head><body class="roo-htmleditor-body"></body></html>';
14531     },
14532
14533     // private
14534     onRender : function(ct, position)
14535     {
14536         var _t = this;
14537         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14538         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14539         
14540         
14541         this.el.dom.style.border = '0 none';
14542         this.el.dom.setAttribute('tabIndex', -1);
14543         this.el.addClass('x-hidden hide');
14544         
14545         
14546         
14547         if(Roo.isIE){ // fix IE 1px bogus margin
14548             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14549         }
14550        
14551         
14552         this.frameId = Roo.id();
14553         
14554          
14555         
14556         var iframe = this.owner.wrap.createChild({
14557             tag: 'iframe',
14558             cls: 'form-control', // bootstrap..
14559             id: this.frameId,
14560             name: this.frameId,
14561             frameBorder : 'no',
14562             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14563         }, this.el
14564         );
14565         
14566         
14567         this.iframe = iframe.dom;
14568
14569          this.assignDocWin();
14570         
14571         this.doc.designMode = 'on';
14572        
14573         this.doc.open();
14574         this.doc.write(this.getDocMarkup());
14575         this.doc.close();
14576
14577         
14578         var task = { // must defer to wait for browser to be ready
14579             run : function(){
14580                 //console.log("run task?" + this.doc.readyState);
14581                 this.assignDocWin();
14582                 if(this.doc.body || this.doc.readyState == 'complete'){
14583                     try {
14584                         this.doc.designMode="on";
14585                     } catch (e) {
14586                         return;
14587                     }
14588                     Roo.TaskMgr.stop(task);
14589                     this.initEditor.defer(10, this);
14590                 }
14591             },
14592             interval : 10,
14593             duration: 10000,
14594             scope: this
14595         };
14596         Roo.TaskMgr.start(task);
14597
14598         
14599          
14600     },
14601
14602     // private
14603     onResize : function(w, h)
14604     {
14605          Roo.log('resize: ' +w + ',' + h );
14606         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14607         if(!this.iframe){
14608             return;
14609         }
14610         if(typeof w == 'number'){
14611             
14612             this.iframe.style.width = w + 'px';
14613         }
14614         if(typeof h == 'number'){
14615             
14616             this.iframe.style.height = h + 'px';
14617             if(this.doc){
14618                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14619             }
14620         }
14621         
14622     },
14623
14624     /**
14625      * Toggles the editor between standard and source edit mode.
14626      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14627      */
14628     toggleSourceEdit : function(sourceEditMode){
14629         
14630         this.sourceEditMode = sourceEditMode === true;
14631         
14632         if(this.sourceEditMode){
14633  
14634             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14635             
14636         }else{
14637             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14638             //this.iframe.className = '';
14639             this.deferFocus();
14640         }
14641         //this.setSize(this.owner.wrap.getSize());
14642         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14643     },
14644
14645     
14646   
14647
14648     /**
14649      * Protected method that will not generally be called directly. If you need/want
14650      * custom HTML cleanup, this is the method you should override.
14651      * @param {String} html The HTML to be cleaned
14652      * return {String} The cleaned HTML
14653      */
14654     cleanHtml : function(html){
14655         html = String(html);
14656         if(html.length > 5){
14657             if(Roo.isSafari){ // strip safari nonsense
14658                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14659             }
14660         }
14661         if(html == '&nbsp;'){
14662             html = '';
14663         }
14664         return html;
14665     },
14666
14667     /**
14668      * HTML Editor -> Textarea
14669      * Protected method that will not generally be called directly. Syncs the contents
14670      * of the editor iframe with the textarea.
14671      */
14672     syncValue : function(){
14673         if(this.initialized){
14674             var bd = (this.doc.body || this.doc.documentElement);
14675             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14676             var html = bd.innerHTML;
14677             if(Roo.isSafari){
14678                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14679                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14680                 if(m && m[1]){
14681                     html = '<div style="'+m[0]+'">' + html + '</div>';
14682                 }
14683             }
14684             html = this.cleanHtml(html);
14685             // fix up the special chars.. normaly like back quotes in word...
14686             // however we do not want to do this with chinese..
14687             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14688                 var cc = b.charCodeAt();
14689                 if (
14690                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14691                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14692                     (cc >= 0xf900 && cc < 0xfb00 )
14693                 ) {
14694                         return b;
14695                 }
14696                 return "&#"+cc+";" 
14697             });
14698             if(this.owner.fireEvent('beforesync', this, html) !== false){
14699                 this.el.dom.value = html;
14700                 this.owner.fireEvent('sync', this, html);
14701             }
14702         }
14703     },
14704
14705     /**
14706      * Protected method that will not generally be called directly. Pushes the value of the textarea
14707      * into the iframe editor.
14708      */
14709     pushValue : function(){
14710         if(this.initialized){
14711             var v = this.el.dom.value.trim();
14712             
14713 //            if(v.length < 1){
14714 //                v = '&#160;';
14715 //            }
14716             
14717             if(this.owner.fireEvent('beforepush', this, v) !== false){
14718                 var d = (this.doc.body || this.doc.documentElement);
14719                 d.innerHTML = v;
14720                 this.cleanUpPaste();
14721                 this.el.dom.value = d.innerHTML;
14722                 this.owner.fireEvent('push', this, v);
14723             }
14724         }
14725     },
14726
14727     // private
14728     deferFocus : function(){
14729         this.focus.defer(10, this);
14730     },
14731
14732     // doc'ed in Field
14733     focus : function(){
14734         if(this.win && !this.sourceEditMode){
14735             this.win.focus();
14736         }else{
14737             this.el.focus();
14738         }
14739     },
14740     
14741     assignDocWin: function()
14742     {
14743         var iframe = this.iframe;
14744         
14745          if(Roo.isIE){
14746             this.doc = iframe.contentWindow.document;
14747             this.win = iframe.contentWindow;
14748         } else {
14749             if (!Roo.get(this.frameId)) {
14750                 return;
14751             }
14752             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14753             this.win = Roo.get(this.frameId).dom.contentWindow;
14754         }
14755     },
14756     
14757     // private
14758     initEditor : function(){
14759         //console.log("INIT EDITOR");
14760         this.assignDocWin();
14761         
14762         
14763         
14764         this.doc.designMode="on";
14765         this.doc.open();
14766         this.doc.write(this.getDocMarkup());
14767         this.doc.close();
14768         
14769         var dbody = (this.doc.body || this.doc.documentElement);
14770         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14771         // this copies styles from the containing element into thsi one..
14772         // not sure why we need all of this..
14773         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14774         ss['background-attachment'] = 'fixed'; // w3c
14775         dbody.bgProperties = 'fixed'; // ie
14776         Roo.DomHelper.applyStyles(dbody, ss);
14777         Roo.EventManager.on(this.doc, {
14778             //'mousedown': this.onEditorEvent,
14779             'mouseup': this.onEditorEvent,
14780             'dblclick': this.onEditorEvent,
14781             'click': this.onEditorEvent,
14782             'keyup': this.onEditorEvent,
14783             buffer:100,
14784             scope: this
14785         });
14786         if(Roo.isGecko){
14787             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14788         }
14789         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14790             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14791         }
14792         this.initialized = true;
14793
14794         this.owner.fireEvent('initialize', this);
14795         this.pushValue();
14796     },
14797
14798     // private
14799     onDestroy : function(){
14800         
14801         
14802         
14803         if(this.rendered){
14804             
14805             //for (var i =0; i < this.toolbars.length;i++) {
14806             //    // fixme - ask toolbars for heights?
14807             //    this.toolbars[i].onDestroy();
14808            // }
14809             
14810             //this.wrap.dom.innerHTML = '';
14811             //this.wrap.remove();
14812         }
14813     },
14814
14815     // private
14816     onFirstFocus : function(){
14817         
14818         this.assignDocWin();
14819         
14820         
14821         this.activated = true;
14822          
14823     
14824         if(Roo.isGecko){ // prevent silly gecko errors
14825             this.win.focus();
14826             var s = this.win.getSelection();
14827             if(!s.focusNode || s.focusNode.nodeType != 3){
14828                 var r = s.getRangeAt(0);
14829                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14830                 r.collapse(true);
14831                 this.deferFocus();
14832             }
14833             try{
14834                 this.execCmd('useCSS', true);
14835                 this.execCmd('styleWithCSS', false);
14836             }catch(e){}
14837         }
14838         this.owner.fireEvent('activate', this);
14839     },
14840
14841     // private
14842     adjustFont: function(btn){
14843         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14844         //if(Roo.isSafari){ // safari
14845         //    adjust *= 2;
14846        // }
14847         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14848         if(Roo.isSafari){ // safari
14849             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14850             v =  (v < 10) ? 10 : v;
14851             v =  (v > 48) ? 48 : v;
14852             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14853             
14854         }
14855         
14856         
14857         v = Math.max(1, v+adjust);
14858         
14859         this.execCmd('FontSize', v  );
14860     },
14861
14862     onEditorEvent : function(e){
14863         this.owner.fireEvent('editorevent', this, e);
14864       //  this.updateToolbar();
14865         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14866     },
14867
14868     insertTag : function(tg)
14869     {
14870         // could be a bit smarter... -> wrap the current selected tRoo..
14871         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14872             
14873             range = this.createRange(this.getSelection());
14874             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14875             wrappingNode.appendChild(range.extractContents());
14876             range.insertNode(wrappingNode);
14877
14878             return;
14879             
14880             
14881             
14882         }
14883         this.execCmd("formatblock",   tg);
14884         
14885     },
14886     
14887     insertText : function(txt)
14888     {
14889         
14890         
14891         var range = this.createRange();
14892         range.deleteContents();
14893                //alert(Sender.getAttribute('label'));
14894                
14895         range.insertNode(this.doc.createTextNode(txt));
14896     } ,
14897     
14898      
14899
14900     /**
14901      * Executes a Midas editor command on the editor document and performs necessary focus and
14902      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14903      * @param {String} cmd The Midas command
14904      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14905      */
14906     relayCmd : function(cmd, value){
14907         this.win.focus();
14908         this.execCmd(cmd, value);
14909         this.owner.fireEvent('editorevent', this);
14910         //this.updateToolbar();
14911         this.owner.deferFocus();
14912     },
14913
14914     /**
14915      * Executes a Midas editor command directly on the editor document.
14916      * For visual commands, you should use {@link #relayCmd} instead.
14917      * <b>This should only be called after the editor is initialized.</b>
14918      * @param {String} cmd The Midas command
14919      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14920      */
14921     execCmd : function(cmd, value){
14922         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14923         this.syncValue();
14924     },
14925  
14926  
14927    
14928     /**
14929      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14930      * to insert tRoo.
14931      * @param {String} text | dom node.. 
14932      */
14933     insertAtCursor : function(text)
14934     {
14935         
14936         
14937         
14938         if(!this.activated){
14939             return;
14940         }
14941         /*
14942         if(Roo.isIE){
14943             this.win.focus();
14944             var r = this.doc.selection.createRange();
14945             if(r){
14946                 r.collapse(true);
14947                 r.pasteHTML(text);
14948                 this.syncValue();
14949                 this.deferFocus();
14950             
14951             }
14952             return;
14953         }
14954         */
14955         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14956             this.win.focus();
14957             
14958             
14959             // from jquery ui (MIT licenced)
14960             var range, node;
14961             var win = this.win;
14962             
14963             if (win.getSelection && win.getSelection().getRangeAt) {
14964                 range = win.getSelection().getRangeAt(0);
14965                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14966                 range.insertNode(node);
14967             } else if (win.document.selection && win.document.selection.createRange) {
14968                 // no firefox support
14969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14970                 win.document.selection.createRange().pasteHTML(txt);
14971             } else {
14972                 // no firefox support
14973                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14974                 this.execCmd('InsertHTML', txt);
14975             } 
14976             
14977             this.syncValue();
14978             
14979             this.deferFocus();
14980         }
14981     },
14982  // private
14983     mozKeyPress : function(e){
14984         if(e.ctrlKey){
14985             var c = e.getCharCode(), cmd;
14986           
14987             if(c > 0){
14988                 c = String.fromCharCode(c).toLowerCase();
14989                 switch(c){
14990                     case 'b':
14991                         cmd = 'bold';
14992                         break;
14993                     case 'i':
14994                         cmd = 'italic';
14995                         break;
14996                     
14997                     case 'u':
14998                         cmd = 'underline';
14999                         break;
15000                     
15001                     case 'v':
15002                         this.cleanUpPaste.defer(100, this);
15003                         return;
15004                         
15005                 }
15006                 if(cmd){
15007                     this.win.focus();
15008                     this.execCmd(cmd);
15009                     this.deferFocus();
15010                     e.preventDefault();
15011                 }
15012                 
15013             }
15014         }
15015     },
15016
15017     // private
15018     fixKeys : function(){ // load time branching for fastest keydown performance
15019         if(Roo.isIE){
15020             return function(e){
15021                 var k = e.getKey(), r;
15022                 if(k == e.TAB){
15023                     e.stopEvent();
15024                     r = this.doc.selection.createRange();
15025                     if(r){
15026                         r.collapse(true);
15027                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15028                         this.deferFocus();
15029                     }
15030                     return;
15031                 }
15032                 
15033                 if(k == e.ENTER){
15034                     r = this.doc.selection.createRange();
15035                     if(r){
15036                         var target = r.parentElement();
15037                         if(!target || target.tagName.toLowerCase() != 'li'){
15038                             e.stopEvent();
15039                             r.pasteHTML('<br />');
15040                             r.collapse(false);
15041                             r.select();
15042                         }
15043                     }
15044                 }
15045                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15046                     this.cleanUpPaste.defer(100, this);
15047                     return;
15048                 }
15049                 
15050                 
15051             };
15052         }else if(Roo.isOpera){
15053             return function(e){
15054                 var k = e.getKey();
15055                 if(k == e.TAB){
15056                     e.stopEvent();
15057                     this.win.focus();
15058                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15059                     this.deferFocus();
15060                 }
15061                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15062                     this.cleanUpPaste.defer(100, this);
15063                     return;
15064                 }
15065                 
15066             };
15067         }else if(Roo.isSafari){
15068             return function(e){
15069                 var k = e.getKey();
15070                 
15071                 if(k == e.TAB){
15072                     e.stopEvent();
15073                     this.execCmd('InsertText','\t');
15074                     this.deferFocus();
15075                     return;
15076                 }
15077                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15078                     this.cleanUpPaste.defer(100, this);
15079                     return;
15080                 }
15081                 
15082              };
15083         }
15084     }(),
15085     
15086     getAllAncestors: function()
15087     {
15088         var p = this.getSelectedNode();
15089         var a = [];
15090         if (!p) {
15091             a.push(p); // push blank onto stack..
15092             p = this.getParentElement();
15093         }
15094         
15095         
15096         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15097             a.push(p);
15098             p = p.parentNode;
15099         }
15100         a.push(this.doc.body);
15101         return a;
15102     },
15103     lastSel : false,
15104     lastSelNode : false,
15105     
15106     
15107     getSelection : function() 
15108     {
15109         this.assignDocWin();
15110         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15111     },
15112     
15113     getSelectedNode: function() 
15114     {
15115         // this may only work on Gecko!!!
15116         
15117         // should we cache this!!!!
15118         
15119         
15120         
15121          
15122         var range = this.createRange(this.getSelection()).cloneRange();
15123         
15124         if (Roo.isIE) {
15125             var parent = range.parentElement();
15126             while (true) {
15127                 var testRange = range.duplicate();
15128                 testRange.moveToElementText(parent);
15129                 if (testRange.inRange(range)) {
15130                     break;
15131                 }
15132                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15133                     break;
15134                 }
15135                 parent = parent.parentElement;
15136             }
15137             return parent;
15138         }
15139         
15140         // is ancestor a text element.
15141         var ac =  range.commonAncestorContainer;
15142         if (ac.nodeType == 3) {
15143             ac = ac.parentNode;
15144         }
15145         
15146         var ar = ac.childNodes;
15147          
15148         var nodes = [];
15149         var other_nodes = [];
15150         var has_other_nodes = false;
15151         for (var i=0;i<ar.length;i++) {
15152             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15153                 continue;
15154             }
15155             // fullly contained node.
15156             
15157             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15158                 nodes.push(ar[i]);
15159                 continue;
15160             }
15161             
15162             // probably selected..
15163             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15164                 other_nodes.push(ar[i]);
15165                 continue;
15166             }
15167             // outer..
15168             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15169                 continue;
15170             }
15171             
15172             
15173             has_other_nodes = true;
15174         }
15175         if (!nodes.length && other_nodes.length) {
15176             nodes= other_nodes;
15177         }
15178         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15179             return false;
15180         }
15181         
15182         return nodes[0];
15183     },
15184     createRange: function(sel)
15185     {
15186         // this has strange effects when using with 
15187         // top toolbar - not sure if it's a great idea.
15188         //this.editor.contentWindow.focus();
15189         if (typeof sel != "undefined") {
15190             try {
15191                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15192             } catch(e) {
15193                 return this.doc.createRange();
15194             }
15195         } else {
15196             return this.doc.createRange();
15197         }
15198     },
15199     getParentElement: function()
15200     {
15201         
15202         this.assignDocWin();
15203         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15204         
15205         var range = this.createRange(sel);
15206          
15207         try {
15208             var p = range.commonAncestorContainer;
15209             while (p.nodeType == 3) { // text node
15210                 p = p.parentNode;
15211             }
15212             return p;
15213         } catch (e) {
15214             return null;
15215         }
15216     
15217     },
15218     /***
15219      *
15220      * Range intersection.. the hard stuff...
15221      *  '-1' = before
15222      *  '0' = hits..
15223      *  '1' = after.
15224      *         [ -- selected range --- ]
15225      *   [fail]                        [fail]
15226      *
15227      *    basically..
15228      *      if end is before start or  hits it. fail.
15229      *      if start is after end or hits it fail.
15230      *
15231      *   if either hits (but other is outside. - then it's not 
15232      *   
15233      *    
15234      **/
15235     
15236     
15237     // @see http://www.thismuchiknow.co.uk/?p=64.
15238     rangeIntersectsNode : function(range, node)
15239     {
15240         var nodeRange = node.ownerDocument.createRange();
15241         try {
15242             nodeRange.selectNode(node);
15243         } catch (e) {
15244             nodeRange.selectNodeContents(node);
15245         }
15246     
15247         var rangeStartRange = range.cloneRange();
15248         rangeStartRange.collapse(true);
15249     
15250         var rangeEndRange = range.cloneRange();
15251         rangeEndRange.collapse(false);
15252     
15253         var nodeStartRange = nodeRange.cloneRange();
15254         nodeStartRange.collapse(true);
15255     
15256         var nodeEndRange = nodeRange.cloneRange();
15257         nodeEndRange.collapse(false);
15258     
15259         return rangeStartRange.compareBoundaryPoints(
15260                  Range.START_TO_START, nodeEndRange) == -1 &&
15261                rangeEndRange.compareBoundaryPoints(
15262                  Range.START_TO_START, nodeStartRange) == 1;
15263         
15264          
15265     },
15266     rangeCompareNode : function(range, node)
15267     {
15268         var nodeRange = node.ownerDocument.createRange();
15269         try {
15270             nodeRange.selectNode(node);
15271         } catch (e) {
15272             nodeRange.selectNodeContents(node);
15273         }
15274         
15275         
15276         range.collapse(true);
15277     
15278         nodeRange.collapse(true);
15279      
15280         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15281         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15282          
15283         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15284         
15285         var nodeIsBefore   =  ss == 1;
15286         var nodeIsAfter    = ee == -1;
15287         
15288         if (nodeIsBefore && nodeIsAfter)
15289             return 0; // outer
15290         if (!nodeIsBefore && nodeIsAfter)
15291             return 1; //right trailed.
15292         
15293         if (nodeIsBefore && !nodeIsAfter)
15294             return 2;  // left trailed.
15295         // fully contined.
15296         return 3;
15297     },
15298
15299     // private? - in a new class?
15300     cleanUpPaste :  function()
15301     {
15302         // cleans up the whole document..
15303         Roo.log('cleanuppaste');
15304         
15305         this.cleanUpChildren(this.doc.body);
15306         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15307         if (clean != this.doc.body.innerHTML) {
15308             this.doc.body.innerHTML = clean;
15309         }
15310         
15311     },
15312     
15313     cleanWordChars : function(input) {// change the chars to hex code
15314         var he = Roo.HtmlEditorCore;
15315         
15316         var output = input;
15317         Roo.each(he.swapCodes, function(sw) { 
15318             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15319             
15320             output = output.replace(swapper, sw[1]);
15321         });
15322         
15323         return output;
15324     },
15325     
15326     
15327     cleanUpChildren : function (n)
15328     {
15329         if (!n.childNodes.length) {
15330             return;
15331         }
15332         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15333            this.cleanUpChild(n.childNodes[i]);
15334         }
15335     },
15336     
15337     
15338         
15339     
15340     cleanUpChild : function (node)
15341     {
15342         var ed = this;
15343         //console.log(node);
15344         if (node.nodeName == "#text") {
15345             // clean up silly Windows -- stuff?
15346             return; 
15347         }
15348         if (node.nodeName == "#comment") {
15349             node.parentNode.removeChild(node);
15350             // clean up silly Windows -- stuff?
15351             return; 
15352         }
15353         
15354         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15355             // remove node.
15356             node.parentNode.removeChild(node);
15357             return;
15358             
15359         }
15360         
15361         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15362         
15363         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15364         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15365         
15366         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15367         //    remove_keep_children = true;
15368         //}
15369         
15370         if (remove_keep_children) {
15371             this.cleanUpChildren(node);
15372             // inserts everything just before this node...
15373             while (node.childNodes.length) {
15374                 var cn = node.childNodes[0];
15375                 node.removeChild(cn);
15376                 node.parentNode.insertBefore(cn, node);
15377             }
15378             node.parentNode.removeChild(node);
15379             return;
15380         }
15381         
15382         if (!node.attributes || !node.attributes.length) {
15383             this.cleanUpChildren(node);
15384             return;
15385         }
15386         
15387         function cleanAttr(n,v)
15388         {
15389             
15390             if (v.match(/^\./) || v.match(/^\//)) {
15391                 return;
15392             }
15393             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15394                 return;
15395             }
15396             if (v.match(/^#/)) {
15397                 return;
15398             }
15399 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15400             node.removeAttribute(n);
15401             
15402         }
15403         
15404         function cleanStyle(n,v)
15405         {
15406             if (v.match(/expression/)) { //XSS?? should we even bother..
15407                 node.removeAttribute(n);
15408                 return;
15409             }
15410             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15411             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15412             
15413             
15414             var parts = v.split(/;/);
15415             var clean = [];
15416             
15417             Roo.each(parts, function(p) {
15418                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15419                 if (!p.length) {
15420                     return true;
15421                 }
15422                 var l = p.split(':').shift().replace(/\s+/g,'');
15423                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15424                 
15425                 if ( cblack.indexOf(l) > -1) {
15426 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15427                     //node.removeAttribute(n);
15428                     return true;
15429                 }
15430                 //Roo.log()
15431                 // only allow 'c whitelisted system attributes'
15432                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15433 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15434                     //node.removeAttribute(n);
15435                     return true;
15436                 }
15437                 
15438                 
15439                  
15440                 
15441                 clean.push(p);
15442                 return true;
15443             });
15444             if (clean.length) { 
15445                 node.setAttribute(n, clean.join(';'));
15446             } else {
15447                 node.removeAttribute(n);
15448             }
15449             
15450         }
15451         
15452         
15453         for (var i = node.attributes.length-1; i > -1 ; i--) {
15454             var a = node.attributes[i];
15455             //console.log(a);
15456             
15457             if (a.name.toLowerCase().substr(0,2)=='on')  {
15458                 node.removeAttribute(a.name);
15459                 continue;
15460             }
15461             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15462                 node.removeAttribute(a.name);
15463                 continue;
15464             }
15465             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15466                 cleanAttr(a.name,a.value); // fixme..
15467                 continue;
15468             }
15469             if (a.name == 'style') {
15470                 cleanStyle(a.name,a.value);
15471                 continue;
15472             }
15473             /// clean up MS crap..
15474             // tecnically this should be a list of valid class'es..
15475             
15476             
15477             if (a.name == 'class') {
15478                 if (a.value.match(/^Mso/)) {
15479                     node.className = '';
15480                 }
15481                 
15482                 if (a.value.match(/body/)) {
15483                     node.className = '';
15484                 }
15485                 continue;
15486             }
15487             
15488             // style cleanup!?
15489             // class cleanup?
15490             
15491         }
15492         
15493         
15494         this.cleanUpChildren(node);
15495         
15496         
15497     },
15498     /**
15499      * Clean up MS wordisms...
15500      */
15501     cleanWord : function(node)
15502     {
15503         var _t = this;
15504         var cleanWordChildren = function()
15505         {
15506             if (!node.childNodes.length) {
15507                 return;
15508             }
15509             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15510                _t.cleanWord(node.childNodes[i]);
15511             }
15512         }
15513         
15514         
15515         if (!node) {
15516             this.cleanWord(this.doc.body);
15517             return;
15518         }
15519         if (node.nodeName == "#text") {
15520             // clean up silly Windows -- stuff?
15521             return; 
15522         }
15523         if (node.nodeName == "#comment") {
15524             node.parentNode.removeChild(node);
15525             // clean up silly Windows -- stuff?
15526             return; 
15527         }
15528         
15529         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15530             node.parentNode.removeChild(node);
15531             return;
15532         }
15533         
15534         // remove - but keep children..
15535         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15536             while (node.childNodes.length) {
15537                 var cn = node.childNodes[0];
15538                 node.removeChild(cn);
15539                 node.parentNode.insertBefore(cn, node);
15540             }
15541             node.parentNode.removeChild(node);
15542             cleanWordChildren();
15543             return;
15544         }
15545         // clean styles
15546         if (node.className.length) {
15547             
15548             var cn = node.className.split(/\W+/);
15549             var cna = [];
15550             Roo.each(cn, function(cls) {
15551                 if (cls.match(/Mso[a-zA-Z]+/)) {
15552                     return;
15553                 }
15554                 cna.push(cls);
15555             });
15556             node.className = cna.length ? cna.join(' ') : '';
15557             if (!cna.length) {
15558                 node.removeAttribute("class");
15559             }
15560         }
15561         
15562         if (node.hasAttribute("lang")) {
15563             node.removeAttribute("lang");
15564         }
15565         
15566         if (node.hasAttribute("style")) {
15567             
15568             var styles = node.getAttribute("style").split(";");
15569             var nstyle = [];
15570             Roo.each(styles, function(s) {
15571                 if (!s.match(/:/)) {
15572                     return;
15573                 }
15574                 var kv = s.split(":");
15575                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15576                     return;
15577                 }
15578                 // what ever is left... we allow.
15579                 nstyle.push(s);
15580             });
15581             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15582             if (!nstyle.length) {
15583                 node.removeAttribute('style');
15584             }
15585         }
15586         
15587         cleanWordChildren();
15588         
15589         
15590     },
15591     domToHTML : function(currentElement, depth, nopadtext) {
15592         
15593             depth = depth || 0;
15594             nopadtext = nopadtext || false;
15595         
15596             if (!currentElement) {
15597                 return this.domToHTML(this.doc.body);
15598             }
15599             
15600             //Roo.log(currentElement);
15601             var j;
15602             var allText = false;
15603             var nodeName = currentElement.nodeName;
15604             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15605             
15606             if  (nodeName == '#text') {
15607                 return currentElement.nodeValue;
15608             }
15609             
15610             
15611             var ret = '';
15612             if (nodeName != 'BODY') {
15613                  
15614                 var i = 0;
15615                 // Prints the node tagName, such as <A>, <IMG>, etc
15616                 if (tagName) {
15617                     var attr = [];
15618                     for(i = 0; i < currentElement.attributes.length;i++) {
15619                         // quoting?
15620                         var aname = currentElement.attributes.item(i).name;
15621                         if (!currentElement.attributes.item(i).value.length) {
15622                             continue;
15623                         }
15624                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15625                     }
15626                     
15627                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15628                 } 
15629                 else {
15630                     
15631                     // eack
15632                 }
15633             } else {
15634                 tagName = false;
15635             }
15636             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15637                 return ret;
15638             }
15639             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15640                 nopadtext = true;
15641             }
15642             
15643             
15644             // Traverse the tree
15645             i = 0;
15646             var currentElementChild = currentElement.childNodes.item(i);
15647             var allText = true;
15648             var innerHTML  = '';
15649             lastnode = '';
15650             while (currentElementChild) {
15651                 // Formatting code (indent the tree so it looks nice on the screen)
15652                 var nopad = nopadtext;
15653                 if (lastnode == 'SPAN') {
15654                     nopad  = true;
15655                 }
15656                 // text
15657                 if  (currentElementChild.nodeName == '#text') {
15658                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15659                     if (!nopad && toadd.length > 80) {
15660                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15661                     }
15662                     innerHTML  += toadd;
15663                     
15664                     i++;
15665                     currentElementChild = currentElement.childNodes.item(i);
15666                     lastNode = '';
15667                     continue;
15668                 }
15669                 allText = false;
15670                 
15671                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15672                     
15673                 // Recursively traverse the tree structure of the child node
15674                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15675                 lastnode = currentElementChild.nodeName;
15676                 i++;
15677                 currentElementChild=currentElement.childNodes.item(i);
15678             }
15679             
15680             ret += innerHTML;
15681             
15682             if (!allText) {
15683                     // The remaining code is mostly for formatting the tree
15684                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15685             }
15686             
15687             
15688             if (tagName) {
15689                 ret+= "</"+tagName+">";
15690             }
15691             return ret;
15692             
15693         }
15694     
15695     // hide stuff that is not compatible
15696     /**
15697      * @event blur
15698      * @hide
15699      */
15700     /**
15701      * @event change
15702      * @hide
15703      */
15704     /**
15705      * @event focus
15706      * @hide
15707      */
15708     /**
15709      * @event specialkey
15710      * @hide
15711      */
15712     /**
15713      * @cfg {String} fieldClass @hide
15714      */
15715     /**
15716      * @cfg {String} focusClass @hide
15717      */
15718     /**
15719      * @cfg {String} autoCreate @hide
15720      */
15721     /**
15722      * @cfg {String} inputType @hide
15723      */
15724     /**
15725      * @cfg {String} invalidClass @hide
15726      */
15727     /**
15728      * @cfg {String} invalidText @hide
15729      */
15730     /**
15731      * @cfg {String} msgFx @hide
15732      */
15733     /**
15734      * @cfg {String} validateOnBlur @hide
15735      */
15736 });
15737
15738 Roo.HtmlEditorCore.white = [
15739         'area', 'br', 'img', 'input', 'hr', 'wbr',
15740         
15741        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15742        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15743        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15744        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15745        'table',   'ul',         'xmp', 
15746        
15747        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15748       'thead',   'tr', 
15749      
15750       'dir', 'menu', 'ol', 'ul', 'dl',
15751        
15752       'embed',  'object'
15753 ];
15754
15755
15756 Roo.HtmlEditorCore.black = [
15757     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15758         'applet', // 
15759         'base',   'basefont', 'bgsound', 'blink',  'body', 
15760         'frame',  'frameset', 'head',    'html',   'ilayer', 
15761         'iframe', 'layer',  'link',     'meta',    'object',   
15762         'script', 'style' ,'title',  'xml' // clean later..
15763 ];
15764 Roo.HtmlEditorCore.clean = [
15765     'script', 'style', 'title', 'xml'
15766 ];
15767 Roo.HtmlEditorCore.remove = [
15768     'font'
15769 ];
15770 // attributes..
15771
15772 Roo.HtmlEditorCore.ablack = [
15773     'on'
15774 ];
15775     
15776 Roo.HtmlEditorCore.aclean = [ 
15777     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15778 ];
15779
15780 // protocols..
15781 Roo.HtmlEditorCore.pwhite= [
15782         'http',  'https',  'mailto'
15783 ];
15784
15785 // white listed style attributes.
15786 Roo.HtmlEditorCore.cwhite= [
15787       //  'text-align', /// default is to allow most things..
15788       
15789          
15790 //        'font-size'//??
15791 ];
15792
15793 // black listed style attributes.
15794 Roo.HtmlEditorCore.cblack= [
15795       //  'font-size' -- this can be set by the project 
15796 ];
15797
15798
15799 Roo.HtmlEditorCore.swapCodes   =[ 
15800     [    8211, "--" ], 
15801     [    8212, "--" ], 
15802     [    8216,  "'" ],  
15803     [    8217, "'" ],  
15804     [    8220, '"' ],  
15805     [    8221, '"' ],  
15806     [    8226, "*" ],  
15807     [    8230, "..." ]
15808 ]; 
15809
15810     /*
15811  * - LGPL
15812  *
15813  * HtmlEditor
15814  * 
15815  */
15816
15817 /**
15818  * @class Roo.bootstrap.HtmlEditor
15819  * @extends Roo.bootstrap.TextArea
15820  * Bootstrap HtmlEditor class
15821
15822  * @constructor
15823  * Create a new HtmlEditor
15824  * @param {Object} config The config object
15825  */
15826
15827 Roo.bootstrap.HtmlEditor = function(config){
15828     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15829     if (!this.toolbars) {
15830         this.toolbars = [];
15831     }
15832     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15833     this.addEvents({
15834             /**
15835              * @event initialize
15836              * Fires when the editor is fully initialized (including the iframe)
15837              * @param {HtmlEditor} this
15838              */
15839             initialize: true,
15840             /**
15841              * @event activate
15842              * Fires when the editor is first receives the focus. Any insertion must wait
15843              * until after this event.
15844              * @param {HtmlEditor} this
15845              */
15846             activate: true,
15847              /**
15848              * @event beforesync
15849              * Fires before the textarea is updated with content from the editor iframe. Return false
15850              * to cancel the sync.
15851              * @param {HtmlEditor} this
15852              * @param {String} html
15853              */
15854             beforesync: true,
15855              /**
15856              * @event beforepush
15857              * Fires before the iframe editor is updated with content from the textarea. Return false
15858              * to cancel the push.
15859              * @param {HtmlEditor} this
15860              * @param {String} html
15861              */
15862             beforepush: true,
15863              /**
15864              * @event sync
15865              * Fires when the textarea is updated with content from the editor iframe.
15866              * @param {HtmlEditor} this
15867              * @param {String} html
15868              */
15869             sync: true,
15870              /**
15871              * @event push
15872              * Fires when the iframe editor is updated with content from the textarea.
15873              * @param {HtmlEditor} this
15874              * @param {String} html
15875              */
15876             push: true,
15877              /**
15878              * @event editmodechange
15879              * Fires when the editor switches edit modes
15880              * @param {HtmlEditor} this
15881              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15882              */
15883             editmodechange: true,
15884             /**
15885              * @event editorevent
15886              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15887              * @param {HtmlEditor} this
15888              */
15889             editorevent: true,
15890             /**
15891              * @event firstfocus
15892              * Fires when on first focus - needed by toolbars..
15893              * @param {HtmlEditor} this
15894              */
15895             firstfocus: true,
15896             /**
15897              * @event autosave
15898              * Auto save the htmlEditor value as a file into Events
15899              * @param {HtmlEditor} this
15900              */
15901             autosave: true,
15902             /**
15903              * @event savedpreview
15904              * preview the saved version of htmlEditor
15905              * @param {HtmlEditor} this
15906              */
15907             savedpreview: true
15908         });
15909 };
15910
15911
15912 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15913     
15914     
15915       /**
15916      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15917      */
15918     toolbars : false,
15919    
15920      /**
15921      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15922      *                        Roo.resizable.
15923      */
15924     resizable : false,
15925      /**
15926      * @cfg {Number} height (in pixels)
15927      */   
15928     height: 300,
15929    /**
15930      * @cfg {Number} width (in pixels)
15931      */   
15932     width: false,
15933     
15934     /**
15935      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15936      * 
15937      */
15938     stylesheets: false,
15939     
15940     // id of frame..
15941     frameId: false,
15942     
15943     // private properties
15944     validationEvent : false,
15945     deferHeight: true,
15946     initialized : false,
15947     activated : false,
15948     
15949     onFocus : Roo.emptyFn,
15950     iframePad:3,
15951     hideMode:'offsets',
15952     
15953     
15954     tbContainer : false,
15955     
15956     toolbarContainer :function() {
15957         return this.wrap.select('.x-html-editor-tb',true).first();
15958     },
15959
15960     /**
15961      * Protected method that will not generally be called directly. It
15962      * is called when the editor creates its toolbar. Override this method if you need to
15963      * add custom toolbar buttons.
15964      * @param {HtmlEditor} editor
15965      */
15966     createToolbar : function(){
15967         
15968         Roo.log("create toolbars");
15969         
15970         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15971         this.toolbars[0].render(this.toolbarContainer());
15972         
15973         return;
15974         
15975 //        if (!editor.toolbars || !editor.toolbars.length) {
15976 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15977 //        }
15978 //        
15979 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15980 //            editor.toolbars[i] = Roo.factory(
15981 //                    typeof(editor.toolbars[i]) == 'string' ?
15982 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15983 //                Roo.bootstrap.HtmlEditor);
15984 //            editor.toolbars[i].init(editor);
15985 //        }
15986     },
15987
15988      
15989     // private
15990     onRender : function(ct, position)
15991     {
15992        // Roo.log("Call onRender: " + this.xtype);
15993         var _t = this;
15994         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15995       
15996         this.wrap = this.inputEl().wrap({
15997             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15998         });
15999         
16000         this.editorcore.onRender(ct, position);
16001          
16002         if (this.resizable) {
16003             this.resizeEl = new Roo.Resizable(this.wrap, {
16004                 pinned : true,
16005                 wrap: true,
16006                 dynamic : true,
16007                 minHeight : this.height,
16008                 height: this.height,
16009                 handles : this.resizable,
16010                 width: this.width,
16011                 listeners : {
16012                     resize : function(r, w, h) {
16013                         _t.onResize(w,h); // -something
16014                     }
16015                 }
16016             });
16017             
16018         }
16019         this.createToolbar(this);
16020        
16021         
16022         if(!this.width && this.resizable){
16023             this.setSize(this.wrap.getSize());
16024         }
16025         if (this.resizeEl) {
16026             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16027             // should trigger onReize..
16028         }
16029         
16030     },
16031
16032     // private
16033     onResize : function(w, h)
16034     {
16035         Roo.log('resize: ' +w + ',' + h );
16036         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16037         var ew = false;
16038         var eh = false;
16039         
16040         if(this.inputEl() ){
16041             if(typeof w == 'number'){
16042                 var aw = w - this.wrap.getFrameWidth('lr');
16043                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16044                 ew = aw;
16045             }
16046             if(typeof h == 'number'){
16047                  var tbh = -11;  // fixme it needs to tool bar size!
16048                 for (var i =0; i < this.toolbars.length;i++) {
16049                     // fixme - ask toolbars for heights?
16050                     tbh += this.toolbars[i].el.getHeight();
16051                     //if (this.toolbars[i].footer) {
16052                     //    tbh += this.toolbars[i].footer.el.getHeight();
16053                     //}
16054                 }
16055               
16056                 
16057                 
16058                 
16059                 
16060                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16061                 ah -= 5; // knock a few pixes off for look..
16062                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16063                 var eh = ah;
16064             }
16065         }
16066         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16067         this.editorcore.onResize(ew,eh);
16068         
16069     },
16070
16071     /**
16072      * Toggles the editor between standard and source edit mode.
16073      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16074      */
16075     toggleSourceEdit : function(sourceEditMode)
16076     {
16077         this.editorcore.toggleSourceEdit(sourceEditMode);
16078         
16079         if(this.editorcore.sourceEditMode){
16080             Roo.log('editor - showing textarea');
16081             
16082 //            Roo.log('in');
16083 //            Roo.log(this.syncValue());
16084             this.syncValue();
16085             this.inputEl().removeClass('hide');
16086             this.inputEl().dom.removeAttribute('tabIndex');
16087             this.inputEl().focus();
16088         }else{
16089             Roo.log('editor - hiding textarea');
16090 //            Roo.log('out')
16091 //            Roo.log(this.pushValue()); 
16092             this.pushValue();
16093             
16094             this.inputEl().addClass('hide');
16095             this.inputEl().dom.setAttribute('tabIndex', -1);
16096             //this.deferFocus();
16097         }
16098          
16099         if(this.resizable){
16100             this.setSize(this.wrap.getSize());
16101         }
16102         
16103         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16104     },
16105  
16106     // private (for BoxComponent)
16107     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16108
16109     // private (for BoxComponent)
16110     getResizeEl : function(){
16111         return this.wrap;
16112     },
16113
16114     // private (for BoxComponent)
16115     getPositionEl : function(){
16116         return this.wrap;
16117     },
16118
16119     // private
16120     initEvents : function(){
16121         this.originalValue = this.getValue();
16122     },
16123
16124 //    /**
16125 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16126 //     * @method
16127 //     */
16128 //    markInvalid : Roo.emptyFn,
16129 //    /**
16130 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16131 //     * @method
16132 //     */
16133 //    clearInvalid : Roo.emptyFn,
16134
16135     setValue : function(v){
16136         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16137         this.editorcore.pushValue();
16138     },
16139
16140      
16141     // private
16142     deferFocus : function(){
16143         this.focus.defer(10, this);
16144     },
16145
16146     // doc'ed in Field
16147     focus : function(){
16148         this.editorcore.focus();
16149         
16150     },
16151       
16152
16153     // private
16154     onDestroy : function(){
16155         
16156         
16157         
16158         if(this.rendered){
16159             
16160             for (var i =0; i < this.toolbars.length;i++) {
16161                 // fixme - ask toolbars for heights?
16162                 this.toolbars[i].onDestroy();
16163             }
16164             
16165             this.wrap.dom.innerHTML = '';
16166             this.wrap.remove();
16167         }
16168     },
16169
16170     // private
16171     onFirstFocus : function(){
16172         //Roo.log("onFirstFocus");
16173         this.editorcore.onFirstFocus();
16174          for (var i =0; i < this.toolbars.length;i++) {
16175             this.toolbars[i].onFirstFocus();
16176         }
16177         
16178     },
16179     
16180     // private
16181     syncValue : function()
16182     {   
16183         this.editorcore.syncValue();
16184     },
16185     
16186     pushValue : function()
16187     {   
16188         this.editorcore.pushValue();
16189     }
16190      
16191     
16192     // hide stuff that is not compatible
16193     /**
16194      * @event blur
16195      * @hide
16196      */
16197     /**
16198      * @event change
16199      * @hide
16200      */
16201     /**
16202      * @event focus
16203      * @hide
16204      */
16205     /**
16206      * @event specialkey
16207      * @hide
16208      */
16209     /**
16210      * @cfg {String} fieldClass @hide
16211      */
16212     /**
16213      * @cfg {String} focusClass @hide
16214      */
16215     /**
16216      * @cfg {String} autoCreate @hide
16217      */
16218     /**
16219      * @cfg {String} inputType @hide
16220      */
16221     /**
16222      * @cfg {String} invalidClass @hide
16223      */
16224     /**
16225      * @cfg {String} invalidText @hide
16226      */
16227     /**
16228      * @cfg {String} msgFx @hide
16229      */
16230     /**
16231      * @cfg {String} validateOnBlur @hide
16232      */
16233 });
16234  
16235     
16236    
16237    
16238    
16239       
16240
16241 /**
16242  * @class Roo.bootstrap.HtmlEditorToolbar1
16243  * Basic Toolbar
16244  * 
16245  * Usage:
16246  *
16247  new Roo.bootstrap.HtmlEditor({
16248     ....
16249     toolbars : [
16250         new Roo.bootstrap.HtmlEditorToolbar1({
16251             disable : { fonts: 1 , format: 1, ..., ... , ...],
16252             btns : [ .... ]
16253         })
16254     }
16255      
16256  * 
16257  * @cfg {Object} disable List of elements to disable..
16258  * @cfg {Array} btns List of additional buttons.
16259  * 
16260  * 
16261  * NEEDS Extra CSS? 
16262  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16263  */
16264  
16265 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16266 {
16267     
16268     Roo.apply(this, config);
16269     
16270     // default disabled, based on 'good practice'..
16271     this.disable = this.disable || {};
16272     Roo.applyIf(this.disable, {
16273         fontSize : true,
16274         colors : true,
16275         specialElements : true
16276     });
16277     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16278     
16279     this.editor = config.editor;
16280     this.editorcore = config.editor.editorcore;
16281     
16282     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16283     
16284     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16285     // dont call parent... till later.
16286 }
16287 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16288     
16289     
16290     bar : true,
16291     
16292     editor : false,
16293     editorcore : false,
16294     
16295     
16296     formats : [
16297         "p" ,  
16298         "h1","h2","h3","h4","h5","h6", 
16299         "pre", "code", 
16300         "abbr", "acronym", "address", "cite", "samp", "var",
16301         'div','span'
16302     ],
16303     
16304     onRender : function(ct, position)
16305     {
16306        // Roo.log("Call onRender: " + this.xtype);
16307         
16308        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16309        Roo.log(this.el);
16310        this.el.dom.style.marginBottom = '0';
16311        var _this = this;
16312        var editorcore = this.editorcore;
16313        var editor= this.editor;
16314        
16315        var children = [];
16316        var btn = function(id,cmd , toggle, handler){
16317        
16318             var  event = toggle ? 'toggle' : 'click';
16319        
16320             var a = {
16321                 size : 'sm',
16322                 xtype: 'Button',
16323                 xns: Roo.bootstrap,
16324                 glyphicon : id,
16325                 cmd : id || cmd,
16326                 enableToggle:toggle !== false,
16327                 //html : 'submit'
16328                 pressed : toggle ? false : null,
16329                 listeners : {}
16330             }
16331             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16332                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16333             }
16334             children.push(a);
16335             return a;
16336        }
16337         
16338         var style = {
16339                 xtype: 'Button',
16340                 size : 'sm',
16341                 xns: Roo.bootstrap,
16342                 glyphicon : 'font',
16343                 //html : 'submit'
16344                 menu : {
16345                     xtype: 'Menu',
16346                     xns: Roo.bootstrap,
16347                     items:  []
16348                 }
16349         };
16350         Roo.each(this.formats, function(f) {
16351             style.menu.items.push({
16352                 xtype :'MenuItem',
16353                 xns: Roo.bootstrap,
16354                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16355                 tagname : f,
16356                 listeners : {
16357                     click : function()
16358                     {
16359                         editorcore.insertTag(this.tagname);
16360                         editor.focus();
16361                     }
16362                 }
16363                 
16364             });
16365         });
16366          children.push(style);   
16367             
16368             
16369         btn('bold',false,true);
16370         btn('italic',false,true);
16371         btn('align-left', 'justifyleft',true);
16372         btn('align-center', 'justifycenter',true);
16373         btn('align-right' , 'justifyright',true);
16374         btn('link', false, false, function(btn) {
16375             //Roo.log("create link?");
16376             var url = prompt(this.createLinkText, this.defaultLinkValue);
16377             if(url && url != 'http:/'+'/'){
16378                 this.editorcore.relayCmd('createlink', url);
16379             }
16380         }),
16381         btn('list','insertunorderedlist',true);
16382         btn('pencil', false,true, function(btn){
16383                 Roo.log(this);
16384                 
16385                 this.toggleSourceEdit(btn.pressed);
16386         });
16387         /*
16388         var cog = {
16389                 xtype: 'Button',
16390                 size : 'sm',
16391                 xns: Roo.bootstrap,
16392                 glyphicon : 'cog',
16393                 //html : 'submit'
16394                 menu : {
16395                     xtype: 'Menu',
16396                     xns: Roo.bootstrap,
16397                     items:  []
16398                 }
16399         };
16400         
16401         cog.menu.items.push({
16402             xtype :'MenuItem',
16403             xns: Roo.bootstrap,
16404             html : Clean styles,
16405             tagname : f,
16406             listeners : {
16407                 click : function()
16408                 {
16409                     editorcore.insertTag(this.tagname);
16410                     editor.focus();
16411                 }
16412             }
16413             
16414         });
16415        */
16416         
16417          
16418        this.xtype = 'NavSimplebar';
16419         
16420         for(var i=0;i< children.length;i++) {
16421             
16422             this.buttons.add(this.addxtypeChild(children[i]));
16423             
16424         }
16425         
16426         editor.on('editorevent', this.updateToolbar, this);
16427     },
16428     onBtnClick : function(id)
16429     {
16430        this.editorcore.relayCmd(id);
16431        this.editorcore.focus();
16432     },
16433     
16434     /**
16435      * Protected method that will not generally be called directly. It triggers
16436      * a toolbar update by reading the markup state of the current selection in the editor.
16437      */
16438     updateToolbar: function(){
16439
16440         if(!this.editorcore.activated){
16441             this.editor.onFirstFocus(); // is this neeed?
16442             return;
16443         }
16444
16445         var btns = this.buttons; 
16446         var doc = this.editorcore.doc;
16447         btns.get('bold').setActive(doc.queryCommandState('bold'));
16448         btns.get('italic').setActive(doc.queryCommandState('italic'));
16449         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16450         
16451         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16452         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16453         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16454         
16455         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16456         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16457          /*
16458         
16459         var ans = this.editorcore.getAllAncestors();
16460         if (this.formatCombo) {
16461             
16462             
16463             var store = this.formatCombo.store;
16464             this.formatCombo.setValue("");
16465             for (var i =0; i < ans.length;i++) {
16466                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16467                     // select it..
16468                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16469                     break;
16470                 }
16471             }
16472         }
16473         
16474         
16475         
16476         // hides menus... - so this cant be on a menu...
16477         Roo.bootstrap.MenuMgr.hideAll();
16478         */
16479         Roo.bootstrap.MenuMgr.hideAll();
16480         //this.editorsyncValue();
16481     },
16482     onFirstFocus: function() {
16483         this.buttons.each(function(item){
16484            item.enable();
16485         });
16486     },
16487     toggleSourceEdit : function(sourceEditMode){
16488         
16489           
16490         if(sourceEditMode){
16491             Roo.log("disabling buttons");
16492            this.buttons.each( function(item){
16493                 if(item.cmd != 'pencil'){
16494                     item.disable();
16495                 }
16496             });
16497           
16498         }else{
16499             Roo.log("enabling buttons");
16500             if(this.editorcore.initialized){
16501                 this.buttons.each( function(item){
16502                     item.enable();
16503                 });
16504             }
16505             
16506         }
16507         Roo.log("calling toggole on editor");
16508         // tell the editor that it's been pressed..
16509         this.editor.toggleSourceEdit(sourceEditMode);
16510        
16511     }
16512 });
16513
16514
16515
16516
16517
16518 /**
16519  * @class Roo.bootstrap.Table.AbstractSelectionModel
16520  * @extends Roo.util.Observable
16521  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16522  * implemented by descendant classes.  This class should not be directly instantiated.
16523  * @constructor
16524  */
16525 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16526     this.locked = false;
16527     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16528 };
16529
16530
16531 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
16532     /** @ignore Called by the grid automatically. Do not call directly. */
16533     init : function(grid){
16534         this.grid = grid;
16535         this.initEvents();
16536     },
16537
16538     /**
16539      * Locks the selections.
16540      */
16541     lock : function(){
16542         this.locked = true;
16543     },
16544
16545     /**
16546      * Unlocks the selections.
16547      */
16548     unlock : function(){
16549         this.locked = false;
16550     },
16551
16552     /**
16553      * Returns true if the selections are locked.
16554      * @return {Boolean}
16555      */
16556     isLocked : function(){
16557         return this.locked;
16558     }
16559 });
16560 /**
16561  * @class Roo.bootstrap.Table.ColumnModel
16562  * @extends Roo.util.Observable
16563  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16564  * the columns in the table.
16565  
16566  * @constructor
16567  * @param {Object} config An Array of column config objects. See this class's
16568  * config objects for details.
16569 */
16570 Roo.bootstrap.Table.ColumnModel = function(config){
16571         /**
16572      * The config passed into the constructor
16573      */
16574     this.config = config;
16575     this.lookup = {};
16576
16577     // if no id, create one
16578     // if the column does not have a dataIndex mapping,
16579     // map it to the order it is in the config
16580     for(var i = 0, len = config.length; i < len; i++){
16581         var c = config[i];
16582         if(typeof c.dataIndex == "undefined"){
16583             c.dataIndex = i;
16584         }
16585         if(typeof c.renderer == "string"){
16586             c.renderer = Roo.util.Format[c.renderer];
16587         }
16588         if(typeof c.id == "undefined"){
16589             c.id = Roo.id();
16590         }
16591 //        if(c.editor && c.editor.xtype){
16592 //            c.editor  = Roo.factory(c.editor, Roo.grid);
16593 //        }
16594 //        if(c.editor && c.editor.isFormField){
16595 //            c.editor = new Roo.grid.GridEditor(c.editor);
16596 //        }
16597
16598         this.lookup[c.id] = c;
16599     }
16600
16601     /**
16602      * The width of columns which have no width specified (defaults to 100)
16603      * @type Number
16604      */
16605     this.defaultWidth = 100;
16606
16607     /**
16608      * Default sortable of columns which have no sortable specified (defaults to false)
16609      * @type Boolean
16610      */
16611     this.defaultSortable = false;
16612
16613     this.addEvents({
16614         /**
16615              * @event widthchange
16616              * Fires when the width of a column changes.
16617              * @param {ColumnModel} this
16618              * @param {Number} columnIndex The column index
16619              * @param {Number} newWidth The new width
16620              */
16621             "widthchange": true,
16622         /**
16623              * @event headerchange
16624              * Fires when the text of a header changes.
16625              * @param {ColumnModel} this
16626              * @param {Number} columnIndex The column index
16627              * @param {Number} newText The new header text
16628              */
16629             "headerchange": true,
16630         /**
16631              * @event hiddenchange
16632              * Fires when a column is hidden or "unhidden".
16633              * @param {ColumnModel} this
16634              * @param {Number} columnIndex The column index
16635              * @param {Boolean} hidden true if hidden, false otherwise
16636              */
16637             "hiddenchange": true,
16638             /**
16639          * @event columnmoved
16640          * Fires when a column is moved.
16641          * @param {ColumnModel} this
16642          * @param {Number} oldIndex
16643          * @param {Number} newIndex
16644          */
16645         "columnmoved" : true,
16646         /**
16647          * @event columlockchange
16648          * Fires when a column's locked state is changed
16649          * @param {ColumnModel} this
16650          * @param {Number} colIndex
16651          * @param {Boolean} locked true if locked
16652          */
16653         "columnlockchange" : true
16654     });
16655     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16656 };
16657 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16658     /**
16659      * @cfg {String} header The header text to display in the Grid view.
16660      */
16661     /**
16662      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16663      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16664      * specified, the column's index is used as an index into the Record's data Array.
16665      */
16666     /**
16667      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16668      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16669      */
16670     /**
16671      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16672      * Defaults to the value of the {@link #defaultSortable} property.
16673      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16674      */
16675     /**
16676      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16677      */
16678     /**
16679      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16680      */
16681     /**
16682      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16683      */
16684     /**
16685      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16686      */
16687     /**
16688      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16689      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16690      * default renderer uses the raw data value.
16691      */
16692     /**
16693      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16694      */
16695
16696     /**
16697      * Returns the id of the column at the specified index.
16698      * @param {Number} index The column index
16699      * @return {String} the id
16700      */
16701     getColumnId : function(index){
16702         return this.config[index].id;
16703     },
16704
16705     /**
16706      * Returns the column for a specified id.
16707      * @param {String} id The column id
16708      * @return {Object} the column
16709      */
16710     getColumnById : function(id){
16711         return this.lookup[id];
16712     },
16713
16714     
16715     /**
16716      * Returns the column for a specified dataIndex.
16717      * @param {String} dataIndex The column dataIndex
16718      * @return {Object|Boolean} the column or false if not found
16719      */
16720     getColumnByDataIndex: function(dataIndex){
16721         var index = this.findColumnIndex(dataIndex);
16722         return index > -1 ? this.config[index] : false;
16723     },
16724     
16725     /**
16726      * Returns the index for a specified column id.
16727      * @param {String} id The column id
16728      * @return {Number} the index, or -1 if not found
16729      */
16730     getIndexById : function(id){
16731         for(var i = 0, len = this.config.length; i < len; i++){
16732             if(this.config[i].id == id){
16733                 return i;
16734             }
16735         }
16736         return -1;
16737     },
16738     
16739     /**
16740      * Returns the index for a specified column dataIndex.
16741      * @param {String} dataIndex The column dataIndex
16742      * @return {Number} the index, or -1 if not found
16743      */
16744     
16745     findColumnIndex : function(dataIndex){
16746         for(var i = 0, len = this.config.length; i < len; i++){
16747             if(this.config[i].dataIndex == dataIndex){
16748                 return i;
16749             }
16750         }
16751         return -1;
16752     },
16753     
16754     
16755     moveColumn : function(oldIndex, newIndex){
16756         var c = this.config[oldIndex];
16757         this.config.splice(oldIndex, 1);
16758         this.config.splice(newIndex, 0, c);
16759         this.dataMap = null;
16760         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16761     },
16762
16763     isLocked : function(colIndex){
16764         return this.config[colIndex].locked === true;
16765     },
16766
16767     setLocked : function(colIndex, value, suppressEvent){
16768         if(this.isLocked(colIndex) == value){
16769             return;
16770         }
16771         this.config[colIndex].locked = value;
16772         if(!suppressEvent){
16773             this.fireEvent("columnlockchange", this, colIndex, value);
16774         }
16775     },
16776
16777     getTotalLockedWidth : function(){
16778         var totalWidth = 0;
16779         for(var i = 0; i < this.config.length; i++){
16780             if(this.isLocked(i) && !this.isHidden(i)){
16781                 this.totalWidth += this.getColumnWidth(i);
16782             }
16783         }
16784         return totalWidth;
16785     },
16786
16787     getLockedCount : function(){
16788         for(var i = 0, len = this.config.length; i < len; i++){
16789             if(!this.isLocked(i)){
16790                 return i;
16791             }
16792         }
16793     },
16794
16795     /**
16796      * Returns the number of columns.
16797      * @return {Number}
16798      */
16799     getColumnCount : function(visibleOnly){
16800         if(visibleOnly === true){
16801             var c = 0;
16802             for(var i = 0, len = this.config.length; i < len; i++){
16803                 if(!this.isHidden(i)){
16804                     c++;
16805                 }
16806             }
16807             return c;
16808         }
16809         return this.config.length;
16810     },
16811
16812     /**
16813      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16814      * @param {Function} fn
16815      * @param {Object} scope (optional)
16816      * @return {Array} result
16817      */
16818     getColumnsBy : function(fn, scope){
16819         var r = [];
16820         for(var i = 0, len = this.config.length; i < len; i++){
16821             var c = this.config[i];
16822             if(fn.call(scope||this, c, i) === true){
16823                 r[r.length] = c;
16824             }
16825         }
16826         return r;
16827     },
16828
16829     /**
16830      * Returns true if the specified column is sortable.
16831      * @param {Number} col The column index
16832      * @return {Boolean}
16833      */
16834     isSortable : function(col){
16835         if(typeof this.config[col].sortable == "undefined"){
16836             return this.defaultSortable;
16837         }
16838         return this.config[col].sortable;
16839     },
16840
16841     /**
16842      * Returns the rendering (formatting) function defined for the column.
16843      * @param {Number} col The column index.
16844      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16845      */
16846     getRenderer : function(col){
16847         if(!this.config[col].renderer){
16848             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16849         }
16850         return this.config[col].renderer;
16851     },
16852
16853     /**
16854      * Sets the rendering (formatting) function for a column.
16855      * @param {Number} col The column index
16856      * @param {Function} fn The function to use to process the cell's raw data
16857      * to return HTML markup for the grid view. The render function is called with
16858      * the following parameters:<ul>
16859      * <li>Data value.</li>
16860      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16861      * <li>css A CSS style string to apply to the table cell.</li>
16862      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16863      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16864      * <li>Row index</li>
16865      * <li>Column index</li>
16866      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16867      */
16868     setRenderer : function(col, fn){
16869         this.config[col].renderer = fn;
16870     },
16871
16872     /**
16873      * Returns the width for the specified column.
16874      * @param {Number} col The column index
16875      * @return {Number}
16876      */
16877     getColumnWidth : function(col){
16878         return this.config[col].width * 1 || this.defaultWidth;
16879     },
16880
16881     /**
16882      * Sets the width for a column.
16883      * @param {Number} col The column index
16884      * @param {Number} width The new width
16885      */
16886     setColumnWidth : function(col, width, suppressEvent){
16887         this.config[col].width = width;
16888         this.totalWidth = null;
16889         if(!suppressEvent){
16890              this.fireEvent("widthchange", this, col, width);
16891         }
16892     },
16893
16894     /**
16895      * Returns the total width of all columns.
16896      * @param {Boolean} includeHidden True to include hidden column widths
16897      * @return {Number}
16898      */
16899     getTotalWidth : function(includeHidden){
16900         if(!this.totalWidth){
16901             this.totalWidth = 0;
16902             for(var i = 0, len = this.config.length; i < len; i++){
16903                 if(includeHidden || !this.isHidden(i)){
16904                     this.totalWidth += this.getColumnWidth(i);
16905                 }
16906             }
16907         }
16908         return this.totalWidth;
16909     },
16910
16911     /**
16912      * Returns the header for the specified column.
16913      * @param {Number} col The column index
16914      * @return {String}
16915      */
16916     getColumnHeader : function(col){
16917         return this.config[col].header;
16918     },
16919
16920     /**
16921      * Sets the header for a column.
16922      * @param {Number} col The column index
16923      * @param {String} header The new header
16924      */
16925     setColumnHeader : function(col, header){
16926         this.config[col].header = header;
16927         this.fireEvent("headerchange", this, col, header);
16928     },
16929
16930     /**
16931      * Returns the tooltip for the specified column.
16932      * @param {Number} col The column index
16933      * @return {String}
16934      */
16935     getColumnTooltip : function(col){
16936             return this.config[col].tooltip;
16937     },
16938     /**
16939      * Sets the tooltip for a column.
16940      * @param {Number} col The column index
16941      * @param {String} tooltip The new tooltip
16942      */
16943     setColumnTooltip : function(col, tooltip){
16944             this.config[col].tooltip = tooltip;
16945     },
16946
16947     /**
16948      * Returns the dataIndex for the specified column.
16949      * @param {Number} col The column index
16950      * @return {Number}
16951      */
16952     getDataIndex : function(col){
16953         return this.config[col].dataIndex;
16954     },
16955
16956     /**
16957      * Sets the dataIndex for a column.
16958      * @param {Number} col The column index
16959      * @param {Number} dataIndex The new dataIndex
16960      */
16961     setDataIndex : function(col, dataIndex){
16962         this.config[col].dataIndex = dataIndex;
16963     },
16964
16965     
16966     
16967     /**
16968      * Returns true if the cell is editable.
16969      * @param {Number} colIndex The column index
16970      * @param {Number} rowIndex The row index
16971      * @return {Boolean}
16972      */
16973     isCellEditable : function(colIndex, rowIndex){
16974         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16975     },
16976
16977     /**
16978      * Returns the editor defined for the cell/column.
16979      * return false or null to disable editing.
16980      * @param {Number} colIndex The column index
16981      * @param {Number} rowIndex The row index
16982      * @return {Object}
16983      */
16984     getCellEditor : function(colIndex, rowIndex){
16985         return this.config[colIndex].editor;
16986     },
16987
16988     /**
16989      * Sets if a column is editable.
16990      * @param {Number} col The column index
16991      * @param {Boolean} editable True if the column is editable
16992      */
16993     setEditable : function(col, editable){
16994         this.config[col].editable = editable;
16995     },
16996
16997
16998     /**
16999      * Returns true if the column is hidden.
17000      * @param {Number} colIndex The column index
17001      * @return {Boolean}
17002      */
17003     isHidden : function(colIndex){
17004         return this.config[colIndex].hidden;
17005     },
17006
17007
17008     /**
17009      * Returns true if the column width cannot be changed
17010      */
17011     isFixed : function(colIndex){
17012         return this.config[colIndex].fixed;
17013     },
17014
17015     /**
17016      * Returns true if the column can be resized
17017      * @return {Boolean}
17018      */
17019     isResizable : function(colIndex){
17020         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
17021     },
17022     /**
17023      * Sets if a column is hidden.
17024      * @param {Number} colIndex The column index
17025      * @param {Boolean} hidden True if the column is hidden
17026      */
17027     setHidden : function(colIndex, hidden){
17028         this.config[colIndex].hidden = hidden;
17029         this.totalWidth = null;
17030         this.fireEvent("hiddenchange", this, colIndex, hidden);
17031     },
17032
17033     /**
17034      * Sets the editor for a column.
17035      * @param {Number} col The column index
17036      * @param {Object} editor The editor object
17037      */
17038     setEditor : function(col, editor){
17039         this.config[col].editor = editor;
17040     }
17041 });
17042
17043 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
17044         if(typeof value == "string" && value.length < 1){
17045             return "&#160;";
17046         }
17047         return value;
17048 };
17049
17050 // Alias for backwards compatibility
17051 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17052
17053 /**
17054  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17055  * @class Roo.bootstrap.Table.RowSelectionModel
17056  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17057  * It supports multiple selections and keyboard selection/navigation. 
17058  * @constructor
17059  * @param {Object} config
17060  */
17061
17062 Roo.bootstrap.Table.RowSelectionModel = function(config){
17063     Roo.apply(this, config);
17064     this.selections = new Roo.util.MixedCollection(false, function(o){
17065         return o.id;
17066     });
17067
17068     this.last = false;
17069     this.lastActive = false;
17070
17071     this.addEvents({
17072         /**
17073              * @event selectionchange
17074              * Fires when the selection changes
17075              * @param {SelectionModel} this
17076              */
17077             "selectionchange" : true,
17078         /**
17079              * @event afterselectionchange
17080              * Fires after the selection changes (eg. by key press or clicking)
17081              * @param {SelectionModel} this
17082              */
17083             "afterselectionchange" : true,
17084         /**
17085              * @event beforerowselect
17086              * Fires when a row is selected being selected, return false to cancel.
17087              * @param {SelectionModel} this
17088              * @param {Number} rowIndex The selected index
17089              * @param {Boolean} keepExisting False if other selections will be cleared
17090              */
17091             "beforerowselect" : true,
17092         /**
17093              * @event rowselect
17094              * Fires when a row is selected.
17095              * @param {SelectionModel} this
17096              * @param {Number} rowIndex The selected index
17097              * @param {Roo.data.Record} r The record
17098              */
17099             "rowselect" : true,
17100         /**
17101              * @event rowdeselect
17102              * Fires when a row is deselected.
17103              * @param {SelectionModel} this
17104              * @param {Number} rowIndex The selected index
17105              */
17106         "rowdeselect" : true
17107     });
17108     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17109     this.locked = false;
17110 };
17111
17112 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17113     /**
17114      * @cfg {Boolean} singleSelect
17115      * True to allow selection of only one row at a time (defaults to false)
17116      */
17117     singleSelect : false,
17118
17119     // private
17120     initEvents : function(){
17121
17122         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17123             this.grid.on("mousedown", this.handleMouseDown, this);
17124         }else{ // allow click to work like normal
17125             this.grid.on("rowclick", this.handleDragableRowClick, this);
17126         }
17127
17128         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17129             "up" : function(e){
17130                 if(!e.shiftKey){
17131                     this.selectPrevious(e.shiftKey);
17132                 }else if(this.last !== false && this.lastActive !== false){
17133                     var last = this.last;
17134                     this.selectRange(this.last,  this.lastActive-1);
17135                     this.grid.getView().focusRow(this.lastActive);
17136                     if(last !== false){
17137                         this.last = last;
17138                     }
17139                 }else{
17140                     this.selectFirstRow();
17141                 }
17142                 this.fireEvent("afterselectionchange", this);
17143             },
17144             "down" : function(e){
17145                 if(!e.shiftKey){
17146                     this.selectNext(e.shiftKey);
17147                 }else if(this.last !== false && this.lastActive !== false){
17148                     var last = this.last;
17149                     this.selectRange(this.last,  this.lastActive+1);
17150                     this.grid.getView().focusRow(this.lastActive);
17151                     if(last !== false){
17152                         this.last = last;
17153                     }
17154                 }else{
17155                     this.selectFirstRow();
17156                 }
17157                 this.fireEvent("afterselectionchange", this);
17158             },
17159             scope: this
17160         });
17161
17162         var view = this.grid.view;
17163         view.on("refresh", this.onRefresh, this);
17164         view.on("rowupdated", this.onRowUpdated, this);
17165         view.on("rowremoved", this.onRemove, this);
17166     },
17167
17168     // private
17169     onRefresh : function(){
17170         var ds = this.grid.dataSource, i, v = this.grid.view;
17171         var s = this.selections;
17172         s.each(function(r){
17173             if((i = ds.indexOfId(r.id)) != -1){
17174                 v.onRowSelect(i);
17175             }else{
17176                 s.remove(r);
17177             }
17178         });
17179     },
17180
17181     // private
17182     onRemove : function(v, index, r){
17183         this.selections.remove(r);
17184     },
17185
17186     // private
17187     onRowUpdated : function(v, index, r){
17188         if(this.isSelected(r)){
17189             v.onRowSelect(index);
17190         }
17191     },
17192
17193     /**
17194      * Select records.
17195      * @param {Array} records The records to select
17196      * @param {Boolean} keepExisting (optional) True to keep existing selections
17197      */
17198     selectRecords : function(records, keepExisting){
17199         if(!keepExisting){
17200             this.clearSelections();
17201         }
17202         var ds = this.grid.dataSource;
17203         for(var i = 0, len = records.length; i < len; i++){
17204             this.selectRow(ds.indexOf(records[i]), true);
17205         }
17206     },
17207
17208     /**
17209      * Gets the number of selected rows.
17210      * @return {Number}
17211      */
17212     getCount : function(){
17213         return this.selections.length;
17214     },
17215
17216     /**
17217      * Selects the first row in the grid.
17218      */
17219     selectFirstRow : function(){
17220         this.selectRow(0);
17221     },
17222
17223     /**
17224      * Select the last row.
17225      * @param {Boolean} keepExisting (optional) True to keep existing selections
17226      */
17227     selectLastRow : function(keepExisting){
17228         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17229     },
17230
17231     /**
17232      * Selects the row immediately following the last selected row.
17233      * @param {Boolean} keepExisting (optional) True to keep existing selections
17234      */
17235     selectNext : function(keepExisting){
17236         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17237             this.selectRow(this.last+1, keepExisting);
17238             this.grid.getView().focusRow(this.last);
17239         }
17240     },
17241
17242     /**
17243      * Selects the row that precedes the last selected row.
17244      * @param {Boolean} keepExisting (optional) True to keep existing selections
17245      */
17246     selectPrevious : function(keepExisting){
17247         if(this.last){
17248             this.selectRow(this.last-1, keepExisting);
17249             this.grid.getView().focusRow(this.last);
17250         }
17251     },
17252
17253     /**
17254      * Returns the selected records
17255      * @return {Array} Array of selected records
17256      */
17257     getSelections : function(){
17258         return [].concat(this.selections.items);
17259     },
17260
17261     /**
17262      * Returns the first selected record.
17263      * @return {Record}
17264      */
17265     getSelected : function(){
17266         return this.selections.itemAt(0);
17267     },
17268
17269
17270     /**
17271      * Clears all selections.
17272      */
17273     clearSelections : function(fast){
17274         if(this.locked) return;
17275         if(fast !== true){
17276             var ds = this.grid.dataSource;
17277             var s = this.selections;
17278             s.each(function(r){
17279                 this.deselectRow(ds.indexOfId(r.id));
17280             }, this);
17281             s.clear();
17282         }else{
17283             this.selections.clear();
17284         }
17285         this.last = false;
17286     },
17287
17288
17289     /**
17290      * Selects all rows.
17291      */
17292     selectAll : function(){
17293         if(this.locked) return;
17294         this.selections.clear();
17295         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17296             this.selectRow(i, true);
17297         }
17298     },
17299
17300     /**
17301      * Returns True if there is a selection.
17302      * @return {Boolean}
17303      */
17304     hasSelection : function(){
17305         return this.selections.length > 0;
17306     },
17307
17308     /**
17309      * Returns True if the specified row is selected.
17310      * @param {Number/Record} record The record or index of the record to check
17311      * @return {Boolean}
17312      */
17313     isSelected : function(index){
17314         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17315         return (r && this.selections.key(r.id) ? true : false);
17316     },
17317
17318     /**
17319      * Returns True if the specified record id is selected.
17320      * @param {String} id The id of record to check
17321      * @return {Boolean}
17322      */
17323     isIdSelected : function(id){
17324         return (this.selections.key(id) ? true : false);
17325     },
17326
17327     // private
17328     handleMouseDown : function(e, t){
17329         var view = this.grid.getView(), rowIndex;
17330         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17331             return;
17332         };
17333         if(e.shiftKey && this.last !== false){
17334             var last = this.last;
17335             this.selectRange(last, rowIndex, e.ctrlKey);
17336             this.last = last; // reset the last
17337             view.focusRow(rowIndex);
17338         }else{
17339             var isSelected = this.isSelected(rowIndex);
17340             if(e.button !== 0 && isSelected){
17341                 view.focusRow(rowIndex);
17342             }else if(e.ctrlKey && isSelected){
17343                 this.deselectRow(rowIndex);
17344             }else if(!isSelected){
17345                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17346                 view.focusRow(rowIndex);
17347             }
17348         }
17349         this.fireEvent("afterselectionchange", this);
17350     },
17351     // private
17352     handleDragableRowClick :  function(grid, rowIndex, e) 
17353     {
17354         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17355             this.selectRow(rowIndex, false);
17356             grid.view.focusRow(rowIndex);
17357              this.fireEvent("afterselectionchange", this);
17358         }
17359     },
17360     
17361     /**
17362      * Selects multiple rows.
17363      * @param {Array} rows Array of the indexes of the row to select
17364      * @param {Boolean} keepExisting (optional) True to keep existing selections
17365      */
17366     selectRows : function(rows, keepExisting){
17367         if(!keepExisting){
17368             this.clearSelections();
17369         }
17370         for(var i = 0, len = rows.length; i < len; i++){
17371             this.selectRow(rows[i], true);
17372         }
17373     },
17374
17375     /**
17376      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17377      * @param {Number} startRow The index of the first row in the range
17378      * @param {Number} endRow The index of the last row in the range
17379      * @param {Boolean} keepExisting (optional) True to retain existing selections
17380      */
17381     selectRange : function(startRow, endRow, keepExisting){
17382         if(this.locked) return;
17383         if(!keepExisting){
17384             this.clearSelections();
17385         }
17386         if(startRow <= endRow){
17387             for(var i = startRow; i <= endRow; i++){
17388                 this.selectRow(i, true);
17389             }
17390         }else{
17391             for(var i = startRow; i >= endRow; i--){
17392                 this.selectRow(i, true);
17393             }
17394         }
17395     },
17396
17397     /**
17398      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17399      * @param {Number} startRow The index of the first row in the range
17400      * @param {Number} endRow The index of the last row in the range
17401      */
17402     deselectRange : function(startRow, endRow, preventViewNotify){
17403         if(this.locked) return;
17404         for(var i = startRow; i <= endRow; i++){
17405             this.deselectRow(i, preventViewNotify);
17406         }
17407     },
17408
17409     /**
17410      * Selects a row.
17411      * @param {Number} row The index of the row to select
17412      * @param {Boolean} keepExisting (optional) True to keep existing selections
17413      */
17414     selectRow : function(index, keepExisting, preventViewNotify){
17415         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17416         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17417             if(!keepExisting || this.singleSelect){
17418                 this.clearSelections();
17419             }
17420             var r = this.grid.dataSource.getAt(index);
17421             this.selections.add(r);
17422             this.last = this.lastActive = index;
17423             if(!preventViewNotify){
17424                 this.grid.getView().onRowSelect(index);
17425             }
17426             this.fireEvent("rowselect", this, index, r);
17427             this.fireEvent("selectionchange", this);
17428         }
17429     },
17430
17431     /**
17432      * Deselects a row.
17433      * @param {Number} row The index of the row to deselect
17434      */
17435     deselectRow : function(index, preventViewNotify){
17436         if(this.locked) return;
17437         if(this.last == index){
17438             this.last = false;
17439         }
17440         if(this.lastActive == index){
17441             this.lastActive = false;
17442         }
17443         var r = this.grid.dataSource.getAt(index);
17444         this.selections.remove(r);
17445         if(!preventViewNotify){
17446             this.grid.getView().onRowDeselect(index);
17447         }
17448         this.fireEvent("rowdeselect", this, index);
17449         this.fireEvent("selectionchange", this);
17450     },
17451
17452     // private
17453     restoreLast : function(){
17454         if(this._last){
17455             this.last = this._last;
17456         }
17457     },
17458
17459     // private
17460     acceptsNav : function(row, col, cm){
17461         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17462     },
17463
17464     // private
17465     onEditorKey : function(field, e){
17466         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17467         if(k == e.TAB){
17468             e.stopEvent();
17469             ed.completeEdit();
17470             if(e.shiftKey){
17471                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17472             }else{
17473                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17474             }
17475         }else if(k == e.ENTER && !e.ctrlKey){
17476             e.stopEvent();
17477             ed.completeEdit();
17478             if(e.shiftKey){
17479                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17480             }else{
17481                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17482             }
17483         }else if(k == e.ESC){
17484             ed.cancelEdit();
17485         }
17486         if(newCell){
17487             g.startEditing(newCell[0], newCell[1]);
17488         }
17489     }
17490 });/*
17491  * - LGPL
17492  *
17493  * element
17494  * 
17495  */
17496
17497 /**
17498  * @class Roo.bootstrap.MessageBar
17499  * @extends Roo.bootstrap.Component
17500  * Bootstrap MessageBar class
17501  * @cfg {String} html contents of the MessageBar
17502  * @cfg {String} weight (info | success | warning | danger) default info
17503  * @cfg {String} beforeClass insert the bar before the given class
17504  * @cfg {Boolean} closable (true | false) default false
17505  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17506  * 
17507  * @constructor
17508  * Create a new Element
17509  * @param {Object} config The config object
17510  */
17511
17512 Roo.bootstrap.MessageBar = function(config){
17513     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17514 };
17515
17516 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17517     
17518     html: '',
17519     weight: 'info',
17520     closable: false,
17521     fixed: false,
17522     beforeClass: 'bootstrap-sticky-wrap',
17523     
17524     getAutoCreate : function(){
17525         
17526         var cfg = {
17527             tag: 'div',
17528             cls: 'alert alert-dismissable alert-' + this.weight,
17529             cn: [
17530                 {
17531                     tag: 'span',
17532                     cls: 'message',
17533                     html: this.html || ''
17534                 }
17535             ]
17536         }
17537         
17538         if(this.fixed){
17539             cfg.cls += ' alert-messages-fixed';
17540         }
17541         
17542         if(this.closable){
17543             cfg.cn.push({
17544                 tag: 'button',
17545                 cls: 'close',
17546                 html: 'x'
17547             });
17548         }
17549         
17550         return cfg;
17551     },
17552     
17553     onRender : function(ct, position)
17554     {
17555         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17556         
17557         if(!this.el){
17558             var cfg = Roo.apply({},  this.getAutoCreate());
17559             cfg.id = Roo.id();
17560             
17561             if (this.cls) {
17562                 cfg.cls += ' ' + this.cls;
17563             }
17564             if (this.style) {
17565                 cfg.style = this.style;
17566             }
17567             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17568             
17569             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17570         }
17571         
17572         this.el.select('>button.close').on('click', this.hide, this);
17573         
17574     },
17575     
17576     show : function()
17577     {
17578         if (!this.rendered) {
17579             this.render();
17580         }
17581         
17582         this.el.show();
17583         
17584         this.fireEvent('show', this);
17585         
17586     },
17587     
17588     hide : function()
17589     {
17590         if (!this.rendered) {
17591             this.render();
17592         }
17593         
17594         this.el.hide();
17595         
17596         this.fireEvent('hide', this);
17597     },
17598     
17599     update : function()
17600     {
17601 //        var e = this.el.dom.firstChild;
17602 //        
17603 //        if(this.closable){
17604 //            e = e.nextSibling;
17605 //        }
17606 //        
17607 //        e.data = this.html || '';
17608
17609         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17610     }
17611    
17612 });
17613
17614  
17615
17616