Roo/bootstrap/Body.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         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position)
306     {
307         Roo.log("Roo.bootstrap.Body - onRender");
308         if (this.cls && this.cls.length) {
309             Roo.get(document.body).addClass(this.cls);
310         }
311         // style??? xttr???
312     }
313     
314     
315  
316    
317 });
318
319  /*
320  * - LGPL
321  *
322  * button group
323  * 
324  */
325
326
327 /**
328  * @class Roo.bootstrap.ButtonGroup
329  * @extends Roo.bootstrap.Component
330  * Bootstrap ButtonGroup class
331  * @cfg {String} size lg | sm | xs (default empty normal)
332  * @cfg {String} align vertical | justified  (default none)
333  * @cfg {String} direction up | down (default down)
334  * @cfg {Boolean} toolbar false | true
335  * @cfg {Boolean} btn true | false
336  * 
337  * 
338  * @constructor
339  * Create a new Input
340  * @param {Object} config The config object
341  */
342
343 Roo.bootstrap.ButtonGroup = function(config){
344     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 };
346
347 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
348     
349     size: '',
350     align: '',
351     direction: '',
352     toolbar: false,
353     btn: true,
354
355     getAutoCreate : function(){
356         var cfg = {
357             cls: 'btn-group',
358             html : null
359         }
360         
361         cfg.html = this.html || cfg.html;
362         
363         if (this.toolbar) {
364             cfg = {
365                 cls: 'btn-toolbar',
366                 html: null
367             }
368             
369             return cfg;
370         }
371         
372         if (['vertical','justified'].indexOf(this.align)!==-1) {
373             cfg.cls = 'btn-group-' + this.align;
374             
375             if (this.align == 'justified') {
376                 console.log(this.items);
377             }
378         }
379         
380         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
381             cfg.cls += ' btn-group-' + this.size;
382         }
383         
384         if (this.direction == 'up') {
385             cfg.cls += ' dropup' ;
386         }
387         
388         return cfg;
389     }
390    
391 });
392
393  /*
394  * - LGPL
395  *
396  * button
397  * 
398  */
399
400 /**
401  * @class Roo.bootstrap.Button
402  * @extends Roo.bootstrap.Component
403  * Bootstrap Button class
404  * @cfg {String} html The button content
405  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
406  * @cfg {String} size empty | lg | sm | xs
407  * @cfg {String} tag empty | a | input | submit
408  * @cfg {String} href empty or href
409  * @cfg {Boolean} disabled false | true
410  * @cfg {Boolean} isClose false | true
411  * @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
412  * @cfg {String} badge text for badge
413  * @cfg {String} theme default (or empty) | glow
414  * @cfg {Boolean} inverse false | true
415  * @cfg {Boolean} toggle false | true
416  * @cfg {String} ontext text for on toggle state
417  * @cfg {String} offtext text for off toggle state
418  * @cfg {Boolean} defaulton true | false
419  * @cfg {Boolean} preventDefault (true | false) default true
420  * @cfg {Boolean} removeClass true | false remove the standard class..
421  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422  * 
423  * @constructor
424  * Create a new button
425  * @param {Object} config The config object
426  */
427
428
429 Roo.bootstrap.Button = function(config){
430     Roo.bootstrap.Button.superclass.constructor.call(this, config);
431     this.addEvents({
432         // raw events
433         /**
434          * @event click
435          * When a butotn is pressed
436          * @param {Roo.EventObject} e
437          */
438         "click" : true,
439          /**
440          * @event toggle
441          * After the button has been toggles
442          * @param {Roo.EventObject} e
443          * @param {boolean} pressed (also available as button.pressed)
444          */
445         "toggle" : true
446     });
447 };
448
449 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
450     html: false,
451     active: false,
452     weight: '',
453     size: '',
454     tag: 'button',
455     href: '',
456     disabled: false,
457     isClose: false,
458     glyphicon: '',
459     badge: '',
460     theme: 'default',
461     inverse: false,
462     
463     toggle: false,
464     ontext: 'ON',
465     offtext: 'OFF',
466     defaulton: true,
467     preventDefault: true,
468     removeClass: false,
469     name: false,
470     target: false,
471     
472     
473     pressed : null,
474      
475     
476     getAutoCreate : function(){
477         
478         var cfg = {
479             tag : 'button',
480             cls : 'roo-button',
481             html: ''
482         };
483         
484         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
485             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
486             this.tag = 'button';
487         } else {
488             cfg.tag = this.tag;
489         }
490         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
491         
492         if (this.toggle == true) {
493             cfg={
494                 tag: 'div',
495                 cls: 'slider-frame roo-button',
496                 cn: [
497                     {
498                         tag: 'span',
499                         'data-on-text':'ON',
500                         'data-off-text':'OFF',
501                         cls: 'slider-button',
502                         html: this.offtext
503                     }
504                 ]
505             };
506             
507             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
508                 cfg.cls += ' '+this.weight;
509             }
510             
511             return cfg;
512         }
513         
514         if (this.isClose) {
515             cfg.cls += ' close';
516             
517             cfg["aria-hidden"] = true;
518             
519             cfg.html = "&times;";
520             
521             return cfg;
522         }
523         
524          
525         if (this.theme==='default') {
526             cfg.cls = 'btn roo-button';
527             
528             //if (this.parentType != 'Navbar') {
529             this.weight = this.weight.length ?  this.weight : 'default';
530             //}
531             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
532                 
533                 cfg.cls += ' btn-' + this.weight;
534             }
535         } else if (this.theme==='glow') {
536             
537             cfg.tag = 'a';
538             cfg.cls = 'btn-glow roo-button';
539             
540             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
541                 
542                 cfg.cls += ' ' + this.weight;
543             }
544         }
545    
546         
547         if (this.inverse) {
548             this.cls += ' inverse';
549         }
550         
551         
552         if (this.active) {
553             cfg.cls += ' active';
554         }
555         
556         if (this.disabled) {
557             cfg.disabled = 'disabled';
558         }
559         
560         if (this.items) {
561             Roo.log('changing to ul' );
562             cfg.tag = 'ul';
563             this.glyphicon = 'caret';
564         }
565         
566         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
567          
568         //gsRoo.log(this.parentType);
569         if (this.parentType === 'Navbar' && !this.parent().bar) {
570             Roo.log('changing to li?');
571             
572             cfg.tag = 'li';
573             
574             cfg.cls = '';
575             cfg.cn =  [{
576                 tag : 'a',
577                 cls : 'roo-button',
578                 html : this.html,
579                 href : this.href || '#'
580             }];
581             if (this.menu) {
582                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
583                 cfg.cls += ' dropdown';
584             }   
585             
586             delete cfg.html;
587             
588         }
589         
590        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
591         
592         if (this.glyphicon) {
593             cfg.html = ' ' + cfg.html;
594             
595             cfg.cn = [
596                 {
597                     tag: 'span',
598                     cls: 'glyphicon glyphicon-' + this.glyphicon
599                 }
600             ];
601         }
602         
603         if (this.badge) {
604             cfg.html += ' ';
605             
606             cfg.tag = 'a';
607             
608 //            cfg.cls='btn roo-button';
609             
610             cfg.href=this.href;
611             
612             var value = cfg.html;
613             
614             if(this.glyphicon){
615                 value = {
616                             tag: 'span',
617                             cls: 'glyphicon glyphicon-' + this.glyphicon,
618                             html: this.html
619                         };
620                 
621             }
622             
623             cfg.cn = [
624                 value,
625                 {
626                     tag: 'span',
627                     cls: 'badge',
628                     html: this.badge
629                 }
630             ];
631             
632             cfg.html='';
633         }
634         
635         if (this.menu) {
636             cfg.cls += ' dropdown';
637             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638         }
639         
640         if (cfg.tag !== 'a' && this.href !== '') {
641             throw "Tag must be a to set href.";
642         } else if (this.href.length > 0) {
643             cfg.href = this.href;
644         }
645         
646         if(this.removeClass){
647             cfg.cls = '';
648         }
649         
650         if(this.target){
651             cfg.target = this.target;
652         }
653         
654         return cfg;
655     },
656     initEvents: function() {
657        // Roo.log('init events?');
658 //        Roo.log(this.el.dom);
659        if (this.el.hasClass('roo-button')) {
660             this.el.on('click', this.onClick, this);
661        } else {
662             this.el.select('.roo-button').on('click', this.onClick, this);
663        }
664        
665        if(this.removeClass){
666            this.el.on('click', this.onClick, this);
667        }
668        
669        this.el.enableDisplayMode();
670         
671     },
672     onClick : function(e)
673     {
674         if (this.disabled) {
675             return;
676         }
677         
678         Roo.log('button on click ');
679         if(this.preventDefault){
680             e.preventDefault();
681         }
682         if (this.pressed === true || this.pressed === false) {
683             this.pressed = !this.pressed;
684             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
685             this.fireEvent('toggle', this, e, this.pressed);
686         }
687         
688         
689         this.fireEvent('click', this, e);
690     },
691     
692     /**
693      * Enables this button
694      */
695     enable : function()
696     {
697         this.disabled = false;
698         this.el.removeClass('disabled');
699     },
700     
701     /**
702      * Disable this button
703      */
704     disable : function()
705     {
706         this.disabled = true;
707         this.el.addClass('disabled');
708     },
709      /**
710      * sets the active state on/off, 
711      * @param {Boolean} state (optional) Force a particular state
712      */
713     setActive : function(v) {
714         
715         this.el[v ? 'addClass' : 'removeClass']('active');
716     },
717      /**
718      * toggles the current active state 
719      */
720     toggleActive : function()
721     {
722        var active = this.el.hasClass('active');
723        this.setActive(!active);
724        
725         
726     },
727     setText : function(str)
728     {
729         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
730     },
731     hide: function() {
732        
733      
734         this.el.hide();   
735     },
736     show: function() {
737        
738         this.el.show();   
739     }
740     
741     
742 });
743
744  /*
745  * - LGPL
746  *
747  * column
748  * 
749  */
750
751 /**
752  * @class Roo.bootstrap.Column
753  * @extends Roo.bootstrap.Component
754  * Bootstrap Column class
755  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
756  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
757  * @cfg {Number} md colspan out of 12 for computer-sized screens
758  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
759  * @cfg {String} html content of column.
760  * 
761  * @constructor
762  * Create a new Column
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.Column = function(config){
767     Roo.bootstrap.Column.superclass.constructor.call(this, config);
768 };
769
770 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
771     
772     xs: null,
773     sm: null,
774     md: null,
775     lg: null,
776     html: '',
777     offset: 0,
778     
779     getAutoCreate : function(){
780         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
781         
782         cfg = {
783             tag: 'div',
784             cls: 'column'
785         };
786         
787         var settings=this;
788         ['xs','sm','md','lg'].map(function(size){
789             if (settings[size]) {
790                 cfg.cls += ' col-' + size + '-' + settings[size];
791             }
792         });
793         if (this.html.length) {
794             cfg.html = this.html;
795         }
796         
797         return cfg;
798     }
799    
800 });
801
802  
803
804  /*
805  * - LGPL
806  *
807  * page container.
808  * 
809  */
810
811
812 /**
813  * @class Roo.bootstrap.Container
814  * @extends Roo.bootstrap.Component
815  * Bootstrap Container class
816  * @cfg {Boolean} jumbotron is it a jumbotron element
817  * @cfg {String} html content of element
818  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
819  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
820  * @cfg {String} header content of header (for panel)
821  * @cfg {String} footer content of footer (for panel)
822  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
823  *     
824  * @constructor
825  * Create a new Container
826  * @param {Object} config The config object
827  */
828
829 Roo.bootstrap.Container = function(config){
830     Roo.bootstrap.Container.superclass.constructor.call(this, config);
831 };
832
833 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
834     
835     jumbotron : false,
836     well: '',
837     panel : '',
838     header: '',
839     footer : '',
840     sticky: '',
841   
842      
843     getChildContainer : function() {
844         
845         if(!this.el){
846             return false;
847         }
848         
849         if (this.panel.length) {
850             return this.el.select('.panel-body',true).first();
851         }
852         
853         return this.el;
854     },
855     
856     
857     getAutoCreate : function(){
858         
859         var cfg = {
860             html : '',
861             cls : ''
862         };
863         if (this.jumbotron) {
864             cfg.cls = 'jumbotron';
865         }
866         if (this.cls) {
867             cfg.cls = this.cls + '';
868         }
869         
870         if (this.sticky.length) {
871             
872             var bd = Roo.get(document.body);
873             if (!bd.hasClass('bootstrap-sticky')) {
874                 bd.addClass('bootstrap-sticky');
875                 Roo.select('html',true).setStyle('height', '100%');
876             }
877              
878             cfg.cls += 'bootstrap-sticky-' + this.sticky;
879         }
880         
881         
882         if (this.well.length) {
883             switch (this.well) {
884                 case 'lg':
885                 case 'sm':
886                     cfg.cls +=' well well-' +this.well;
887                     break;
888                 default:
889                     cfg.cls +=' well';
890                     break;
891             }
892         }
893         
894         var body = cfg;
895         
896         if (this.panel.length) {
897             cfg.cls += ' panel panel-' + this.panel;
898             cfg.cn = [];
899             if (this.header.length) {
900                 cfg.cn.push({
901                     
902                     cls : 'panel-heading',
903                     cn : [{
904                         tag: 'h3',
905                         cls : 'panel-title',
906                         html : this.header
907                     }]
908                     
909                 });
910             }
911             body = false;
912             cfg.cn.push({
913                 cls : 'panel-body',
914                 html : this.html
915             });
916             
917             
918             if (this.footer.length) {
919                 cfg.cn.push({
920                     cls : 'panel-footer',
921                     html : this.footer
922                     
923                 });
924             }
925             
926         }
927         if (body) {
928             body.html = this.html || cfg.html;
929         }
930         if (!cfg.cls.length) {
931             cfg.cls =  'container';
932         }
933         
934         return cfg;
935     }
936    
937 });
938
939  /*
940  * - LGPL
941  *
942  * image
943  * 
944  */
945
946
947 /**
948  * @class Roo.bootstrap.Img
949  * @extends Roo.bootstrap.Component
950  * Bootstrap Img class
951  * @cfg {Boolean} imgResponsive false | true
952  * @cfg {String} border rounded | circle | thumbnail
953  * @cfg {String} src image source
954  * @cfg {String} alt image alternative text
955  * @cfg {String} href a tag href
956  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
957  * 
958  * @constructor
959  * Create a new Input
960  * @param {Object} config The config object
961  */
962
963 Roo.bootstrap.Img = function(config){
964     Roo.bootstrap.Img.superclass.constructor.call(this, config);
965     
966     this.addEvents({
967         // img events
968         /**
969          * @event click
970          * The img click event for the img.
971          * @param {Roo.EventObject} e
972          */
973         "click" : true
974     });
975 };
976
977 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
978     
979     imgResponsive: true,
980     border: '',
981     src: '',
982     href: false,
983     target: false,
984
985     getAutoCreate : function(){
986         
987         var cfg = {
988             tag: 'img',
989             cls: (this.imgResponsive) ? 'img-responsive' : '',
990             html : null
991         }
992         
993         cfg.html = this.html || cfg.html;
994         
995         cfg.src = this.src || cfg.src;
996         
997         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
998             cfg.cls += ' img-' + this.border;
999         }
1000         
1001         if(this.alt){
1002             cfg.alt = this.alt;
1003         }
1004         
1005         if(this.href){
1006             var a = {
1007                 tag: 'a',
1008                 href: this.href,
1009                 cn: [
1010                     cfg
1011                 ]
1012             }
1013             
1014             if(this.target){
1015                 a.target = this.target;
1016             }
1017             
1018         }
1019         
1020         
1021         return (this.href) ? a : cfg;
1022     },
1023     
1024     initEvents: function() {
1025         
1026         if(!this.href){
1027             this.el.on('click', this.onClick, this);
1028         }
1029     },
1030     
1031     onClick : function(e)
1032     {
1033         Roo.log('img onclick');
1034         this.fireEvent('click', this, e);
1035     }
1036    
1037 });
1038
1039  /*
1040  * - LGPL
1041  *
1042  * header
1043  * 
1044  */
1045
1046 /**
1047  * @class Roo.bootstrap.Header
1048  * @extends Roo.bootstrap.Component
1049  * Bootstrap Header class
1050  * @cfg {String} html content of header
1051  * @cfg {Number} level (1|2|3|4|5|6) default 1
1052  * 
1053  * @constructor
1054  * Create a new Header
1055  * @param {Object} config The config object
1056  */
1057
1058
1059 Roo.bootstrap.Header  = function(config){
1060     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1061 };
1062
1063 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1064     
1065     //href : false,
1066     html : false,
1067     level : 1,
1068     
1069     
1070     
1071     getAutoCreate : function(){
1072         
1073         var cfg = {
1074             tag: 'h' + (1 *this.level),
1075             html: this.html || 'fill in html'
1076         } ;
1077         
1078         return cfg;
1079     }
1080    
1081 });
1082
1083  
1084
1085  /*
1086  * Based on:
1087  * Ext JS Library 1.1.1
1088  * Copyright(c) 2006-2007, Ext JS, LLC.
1089  *
1090  * Originally Released Under LGPL - original licence link has changed is not relivant.
1091  *
1092  * Fork - LGPL
1093  * <script type="text/javascript">
1094  */
1095  
1096 /**
1097  * @class Roo.bootstrap.MenuMgr
1098  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1099  * @singleton
1100  */
1101 Roo.bootstrap.MenuMgr = function(){
1102    var menus, active, groups = {}, attached = false, lastShow = new Date();
1103
1104    // private - called when first menu is created
1105    function init(){
1106        menus = {};
1107        active = new Roo.util.MixedCollection();
1108        Roo.get(document).addKeyListener(27, function(){
1109            if(active.length > 0){
1110                hideAll();
1111            }
1112        });
1113    }
1114
1115    // private
1116    function hideAll(){
1117        if(active && active.length > 0){
1118            var c = active.clone();
1119            c.each(function(m){
1120                m.hide();
1121            });
1122        }
1123    }
1124
1125    // private
1126    function onHide(m){
1127        active.remove(m);
1128        if(active.length < 1){
1129            Roo.get(document).un("mouseup", onMouseDown);
1130             
1131            attached = false;
1132        }
1133    }
1134
1135    // private
1136    function onShow(m){
1137        var last = active.last();
1138        lastShow = new Date();
1139        active.add(m);
1140        if(!attached){
1141           Roo.get(document).on("mouseup", onMouseDown);
1142            
1143            attached = true;
1144        }
1145        if(m.parentMenu){
1146           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1147           m.parentMenu.activeChild = m;
1148        }else if(last && last.isVisible()){
1149           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1150        }
1151    }
1152
1153    // private
1154    function onBeforeHide(m){
1155        if(m.activeChild){
1156            m.activeChild.hide();
1157        }
1158        if(m.autoHideTimer){
1159            clearTimeout(m.autoHideTimer);
1160            delete m.autoHideTimer;
1161        }
1162    }
1163
1164    // private
1165    function onBeforeShow(m){
1166        var pm = m.parentMenu;
1167        if(!pm && !m.allowOtherMenus){
1168            hideAll();
1169        }else if(pm && pm.activeChild && active != m){
1170            pm.activeChild.hide();
1171        }
1172    }
1173
1174    // private
1175    function onMouseDown(e){
1176         Roo.log("on MouseDown");
1177         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1178            hideAll();
1179         }
1180         
1181         
1182    }
1183
1184    // private
1185    function onBeforeCheck(mi, state){
1186        if(state){
1187            var g = groups[mi.group];
1188            for(var i = 0, l = g.length; i < l; i++){
1189                if(g[i] != mi){
1190                    g[i].setChecked(false);
1191                }
1192            }
1193        }
1194    }
1195
1196    return {
1197
1198        /**
1199         * Hides all menus that are currently visible
1200         */
1201        hideAll : function(){
1202             hideAll();  
1203        },
1204
1205        // private
1206        register : function(menu){
1207            if(!menus){
1208                init();
1209            }
1210            menus[menu.id] = menu;
1211            menu.on("beforehide", onBeforeHide);
1212            menu.on("hide", onHide);
1213            menu.on("beforeshow", onBeforeShow);
1214            menu.on("show", onShow);
1215            var g = menu.group;
1216            if(g && menu.events["checkchange"]){
1217                if(!groups[g]){
1218                    groups[g] = [];
1219                }
1220                groups[g].push(menu);
1221                menu.on("checkchange", onCheck);
1222            }
1223        },
1224
1225         /**
1226          * Returns a {@link Roo.menu.Menu} object
1227          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1228          * be used to generate and return a new Menu instance.
1229          */
1230        get : function(menu){
1231            if(typeof menu == "string"){ // menu id
1232                return menus[menu];
1233            }else if(menu.events){  // menu instance
1234                return menu;
1235            }
1236            /*else if(typeof menu.length == 'number'){ // array of menu items?
1237                return new Roo.bootstrap.Menu({items:menu});
1238            }else{ // otherwise, must be a config
1239                return new Roo.bootstrap.Menu(menu);
1240            }
1241            */
1242            return false;
1243        },
1244
1245        // private
1246        unregister : function(menu){
1247            delete menus[menu.id];
1248            menu.un("beforehide", onBeforeHide);
1249            menu.un("hide", onHide);
1250            menu.un("beforeshow", onBeforeShow);
1251            menu.un("show", onShow);
1252            var g = menu.group;
1253            if(g && menu.events["checkchange"]){
1254                groups[g].remove(menu);
1255                menu.un("checkchange", onCheck);
1256            }
1257        },
1258
1259        // private
1260        registerCheckable : function(menuItem){
1261            var g = menuItem.group;
1262            if(g){
1263                if(!groups[g]){
1264                    groups[g] = [];
1265                }
1266                groups[g].push(menuItem);
1267                menuItem.on("beforecheckchange", onBeforeCheck);
1268            }
1269        },
1270
1271        // private
1272        unregisterCheckable : function(menuItem){
1273            var g = menuItem.group;
1274            if(g){
1275                groups[g].remove(menuItem);
1276                menuItem.un("beforecheckchange", onBeforeCheck);
1277            }
1278        }
1279    };
1280 }();/*
1281  * - LGPL
1282  *
1283  * menu
1284  * 
1285  */
1286
1287 /**
1288  * @class Roo.bootstrap.Menu
1289  * @extends Roo.bootstrap.Component
1290  * Bootstrap Menu class - container for MenuItems
1291  * @cfg {String} type type of menu
1292  * 
1293  * @constructor
1294  * Create a new Menu
1295  * @param {Object} config The config object
1296  */
1297
1298
1299 Roo.bootstrap.Menu = function(config){
1300     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1301     if (this.registerMenu) {
1302         Roo.bootstrap.MenuMgr.register(this);
1303     }
1304     this.addEvents({
1305         /**
1306          * @event beforeshow
1307          * Fires before this menu is displayed
1308          * @param {Roo.menu.Menu} this
1309          */
1310         beforeshow : true,
1311         /**
1312          * @event beforehide
1313          * Fires before this menu is hidden
1314          * @param {Roo.menu.Menu} this
1315          */
1316         beforehide : true,
1317         /**
1318          * @event show
1319          * Fires after this menu is displayed
1320          * @param {Roo.menu.Menu} this
1321          */
1322         show : true,
1323         /**
1324          * @event hide
1325          * Fires after this menu is hidden
1326          * @param {Roo.menu.Menu} this
1327          */
1328         hide : true,
1329         /**
1330          * @event click
1331          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1332          * @param {Roo.menu.Menu} this
1333          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1334          * @param {Roo.EventObject} e
1335          */
1336         click : true,
1337         /**
1338          * @event mouseover
1339          * Fires when the mouse is hovering over this menu
1340          * @param {Roo.menu.Menu} this
1341          * @param {Roo.EventObject} e
1342          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1343          */
1344         mouseover : true,
1345         /**
1346          * @event mouseout
1347          * Fires when the mouse exits this menu
1348          * @param {Roo.menu.Menu} this
1349          * @param {Roo.EventObject} e
1350          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1351          */
1352         mouseout : true,
1353         /**
1354          * @event itemclick
1355          * Fires when a menu item contained in this menu is clicked
1356          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1357          * @param {Roo.EventObject} e
1358          */
1359         itemclick: true
1360     });
1361     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1362 };
1363
1364 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1365     
1366    /// html : false,
1367     //align : '',
1368     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1369     type: false,
1370     /**
1371      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1372      */
1373     registerMenu : true,
1374     
1375     menuItems :false, // stores the menu items..
1376     
1377     hidden:true,
1378     
1379     parentMenu : false,
1380     
1381     getChildContainer : function() {
1382         return this.el;  
1383     },
1384     
1385     getAutoCreate : function(){
1386          
1387         //if (['right'].indexOf(this.align)!==-1) {
1388         //    cfg.cn[1].cls += ' pull-right'
1389         //}
1390         var cfg = {
1391             tag : 'ul',
1392             cls : 'dropdown-menu' ,
1393             style : 'z-index:1000'
1394             
1395         }
1396         
1397         if (this.type === 'submenu') {
1398             cfg.cls = 'submenu active'
1399         }
1400         
1401         return cfg;
1402     },
1403     initEvents : function() {
1404         
1405        // Roo.log("ADD event");
1406        // Roo.log(this.triggerEl.dom);
1407         this.triggerEl.on('click', this.onTriggerPress, this);
1408         this.triggerEl.addClass('dropdown-toggle');
1409         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1410
1411         this.el.on("mouseover", this.onMouseOver, this);
1412         this.el.on("mouseout", this.onMouseOut, this);
1413         
1414         
1415     },
1416     findTargetItem : function(e){
1417         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1418         if(!t){
1419             return false;
1420         }
1421         //Roo.log(t);         Roo.log(t.id);
1422         if(t && t.id){
1423             //Roo.log(this.menuitems);
1424             return this.menuitems.get(t.id);
1425             
1426             //return this.items.get(t.menuItemId);
1427         }
1428         
1429         return false;
1430     },
1431     onClick : function(e){
1432         Roo.log("menu.onClick");
1433         var t = this.findTargetItem(e);
1434         if(!t){
1435             return;
1436         }
1437         Roo.log(e);
1438         /*
1439         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1440             if(t == this.activeItem && t.shouldDeactivate(e)){
1441                 this.activeItem.deactivate();
1442                 delete this.activeItem;
1443                 return;
1444             }
1445             if(t.canActivate){
1446                 this.setActiveItem(t, true);
1447             }
1448             return;
1449             
1450             
1451         }
1452         */
1453         Roo.log('pass click event');
1454         
1455         t.onClick(e);
1456         
1457         this.fireEvent("click", this, t, e);
1458         
1459         this.hide();
1460     },
1461      onMouseOver : function(e){
1462         var t  = this.findTargetItem(e);
1463         //Roo.log(t);
1464         //if(t){
1465         //    if(t.canActivate && !t.disabled){
1466         //        this.setActiveItem(t, true);
1467         //    }
1468         //}
1469         
1470         this.fireEvent("mouseover", this, e, t);
1471     },
1472     isVisible : function(){
1473         return !this.hidden;
1474     },
1475      onMouseOut : function(e){
1476         var t  = this.findTargetItem(e);
1477         
1478         //if(t ){
1479         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1480         //        this.activeItem.deactivate();
1481         //        delete this.activeItem;
1482         //    }
1483         //}
1484         this.fireEvent("mouseout", this, e, t);
1485     },
1486     
1487     
1488     /**
1489      * Displays this menu relative to another element
1490      * @param {String/HTMLElement/Roo.Element} element The element to align to
1491      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1492      * the element (defaults to this.defaultAlign)
1493      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1494      */
1495     show : function(el, pos, parentMenu){
1496         this.parentMenu = parentMenu;
1497         if(!this.el){
1498             this.render();
1499         }
1500         this.fireEvent("beforeshow", this);
1501         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1502     },
1503      /**
1504      * Displays this menu at a specific xy position
1505      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1506      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1507      */
1508     showAt : function(xy, parentMenu, /* private: */_e){
1509         this.parentMenu = parentMenu;
1510         if(!this.el){
1511             this.render();
1512         }
1513         if(_e !== false){
1514             this.fireEvent("beforeshow", this);
1515             
1516             //xy = this.el.adjustForConstraints(xy);
1517         }
1518         //this.el.setXY(xy);
1519         //this.el.show();
1520         this.hideMenuItems();
1521         this.hidden = false;
1522         this.triggerEl.addClass('open');
1523         this.focus();
1524         this.fireEvent("show", this);
1525     },
1526     
1527     focus : function(){
1528         return;
1529         if(!this.hidden){
1530             this.doFocus.defer(50, this);
1531         }
1532     },
1533
1534     doFocus : function(){
1535         if(!this.hidden){
1536             this.focusEl.focus();
1537         }
1538     },
1539
1540     /**
1541      * Hides this menu and optionally all parent menus
1542      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1543      */
1544     hide : function(deep){
1545         
1546         this.hideMenuItems();
1547         if(this.el && this.isVisible()){
1548             this.fireEvent("beforehide", this);
1549             if(this.activeItem){
1550                 this.activeItem.deactivate();
1551                 this.activeItem = null;
1552             }
1553             this.triggerEl.removeClass('open');;
1554             this.hidden = true;
1555             this.fireEvent("hide", this);
1556         }
1557         if(deep === true && this.parentMenu){
1558             this.parentMenu.hide(true);
1559         }
1560     },
1561     
1562     onTriggerPress  : function(e)
1563     {
1564         
1565         Roo.log('trigger press');
1566         //Roo.log(e.getTarget());
1567        // Roo.log(this.triggerEl.dom);
1568         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1569             return;
1570         }
1571         if (this.isVisible()) {
1572             Roo.log('hide');
1573             this.hide();
1574         } else {
1575             this.show(this.triggerEl, false, false);
1576         }
1577         
1578         
1579     },
1580     
1581          
1582        
1583     
1584     hideMenuItems : function()
1585     {
1586         //$(backdrop).remove()
1587         Roo.select('.open',true).each(function(aa) {
1588             
1589             aa.removeClass('open');
1590           //var parent = getParent($(this))
1591           //var relatedTarget = { relatedTarget: this }
1592           
1593            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1594           //if (e.isDefaultPrevented()) return
1595            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1596         })
1597     },
1598     addxtypeChild : function (tree, cntr) {
1599         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1600           
1601         this.menuitems.add(comp);
1602         return comp;
1603
1604     },
1605     getEl : function()
1606     {
1607         Roo.log(this.el);
1608         return this.el;
1609     }
1610 });
1611
1612  
1613  /*
1614  * - LGPL
1615  *
1616  * menu item
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.MenuItem
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap MenuItem class
1625  * @cfg {String} html the menu label
1626  * @cfg {String} href the link
1627  * @cfg {Boolean} preventDefault (true | false) default true
1628  * 
1629  * 
1630  * @constructor
1631  * Create a new MenuItem
1632  * @param {Object} config The config object
1633  */
1634
1635
1636 Roo.bootstrap.MenuItem = function(config){
1637     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1638     this.addEvents({
1639         // raw events
1640         /**
1641          * @event click
1642          * The raw click event for the entire grid.
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1650     
1651     href : false,
1652     html : false,
1653     preventDefault: true,
1654     
1655     getAutoCreate : function(){
1656         var cfg= {
1657             tag: 'li',
1658         cls: 'dropdown-menu-item',
1659             cn: [
1660             {
1661                 tag : 'a',
1662                 href : '#',
1663                 html : 'Link'
1664             }
1665             ]
1666     };
1667         
1668         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1669         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1670         return cfg;
1671     },
1672     
1673     initEvents: function() {
1674         
1675         //this.el.select('a').on('click', this.onClick, this);
1676         
1677     },
1678     onClick : function(e)
1679     {
1680         Roo.log('item on click ');
1681         //if(this.preventDefault){
1682         //    e.preventDefault();
1683         //}
1684         //this.parent().hideMenuItems();
1685         
1686         this.fireEvent('click', this, e);
1687     },
1688     getEl : function()
1689     {
1690         return this.el;
1691     }
1692 });
1693
1694  
1695
1696  /*
1697  * - LGPL
1698  *
1699  * menu separator
1700  * 
1701  */
1702
1703
1704 /**
1705  * @class Roo.bootstrap.MenuSeparator
1706  * @extends Roo.bootstrap.Component
1707  * Bootstrap MenuSeparator class
1708  * 
1709  * @constructor
1710  * Create a new MenuItem
1711  * @param {Object} config The config object
1712  */
1713
1714
1715 Roo.bootstrap.MenuSeparator = function(config){
1716     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1717 };
1718
1719 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1720     
1721     getAutoCreate : function(){
1722         var cfg = {
1723             cls: 'divider',
1724             tag : 'li'
1725         };
1726         
1727         return cfg;
1728     }
1729    
1730 });
1731
1732  
1733
1734  
1735 /*
1736 <div class="modal fade">
1737   <div class="modal-dialog">
1738     <div class="modal-content">
1739       <div class="modal-header">
1740         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1741         <h4 class="modal-title">Modal title</h4>
1742       </div>
1743       <div class="modal-body">
1744         <p>One fine body&hellip;</p>
1745       </div>
1746       <div class="modal-footer">
1747         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1748         <button type="button" class="btn btn-primary">Save changes</button>
1749       </div>
1750     </div><!-- /.modal-content -->
1751   </div><!-- /.modal-dialog -->
1752 </div><!-- /.modal -->
1753 */
1754 /*
1755  * - LGPL
1756  *
1757  * page contgainer.
1758  * 
1759  */
1760
1761 /**
1762  * @class Roo.bootstrap.Modal
1763  * @extends Roo.bootstrap.Component
1764  * Bootstrap Modal class
1765  * @cfg {String} title Title of dialog
1766  * @cfg {Boolean} specificTitle (true|false) default false
1767  * @cfg {Array} buttons Array of buttons or standard button set..
1768  * @cfg {String} buttonPosition (left|right|center) default right
1769  * 
1770  * @constructor
1771  * Create a new Modal Dialog
1772  * @param {Object} config The config object
1773  */
1774
1775 Roo.bootstrap.Modal = function(config){
1776     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1777     this.addEvents({
1778         // raw events
1779         /**
1780          * @event btnclick
1781          * The raw btnclick event for the button
1782          * @param {Roo.EventObject} e
1783          */
1784         "btnclick" : true
1785     });
1786     this.buttons = this.buttons || [];
1787 };
1788
1789 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1790     
1791     title : 'test dialog',
1792    
1793     buttons : false,
1794     
1795     // set on load...
1796     body:  false,
1797     
1798     specificTitle: false,
1799     
1800     buttonPosition: 'right',
1801     
1802     onRender : function(ct, position)
1803     {
1804         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1805      
1806         if(!this.el){
1807             var cfg = Roo.apply({},  this.getAutoCreate());
1808             cfg.id = Roo.id();
1809             //if(!cfg.name){
1810             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1811             //}
1812             //if (!cfg.name.length) {
1813             //    delete cfg.name;
1814            // }
1815             if (this.cls) {
1816                 cfg.cls += ' ' + this.cls;
1817             }
1818             if (this.style) {
1819                 cfg.style = this.style;
1820             }
1821             this.el = Roo.get(document.body).createChild(cfg, position);
1822         }
1823         //var type = this.el.dom.type;
1824         
1825         if(this.tabIndex !== undefined){
1826             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1827         }
1828         
1829         
1830         
1831         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1832         this.maskEl.enableDisplayMode("block");
1833         this.maskEl.hide();
1834         //this.el.addClass("x-dlg-modal");
1835     
1836         if (this.buttons.length) {
1837             Roo.each(this.buttons, function(bb) {
1838                 b = Roo.apply({}, bb);
1839                 b.xns = b.xns || Roo.bootstrap;
1840                 b.xtype = b.xtype || 'Button';
1841                 if (typeof(b.listeners) == 'undefined') {
1842                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1843                 }
1844                 
1845                 var btn = Roo.factory(b);
1846                 
1847                 btn.onRender(this.el.select('.modal-footer div').first());
1848                 
1849             },this);
1850         }
1851         // render the children.
1852         var nitems = [];
1853         
1854         if(typeof(this.items) != 'undefined'){
1855             var items = this.items;
1856             delete this.items;
1857
1858             for(var i =0;i < items.length;i++) {
1859                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1860             }
1861         }
1862         
1863         this.items = nitems;
1864         
1865         this.body = this.el.select('.modal-body',true).first();
1866         this.close = this.el.select('.modal-header .close', true).first();
1867         this.footer = this.el.select('.modal-footer',true).first();
1868         this.initEvents();
1869         //this.el.addClass([this.fieldClass, this.cls]);
1870         
1871     },
1872     getAutoCreate : function(){
1873         
1874         
1875         var bdy = {
1876                 cls : 'modal-body',
1877                 html : this.html || ''
1878         };
1879         
1880         var title = {
1881             tag: 'h4',
1882             cls : 'modal-title',
1883             html : this.title
1884         };
1885         
1886         if(this.specificTitle){
1887             title = this.title;
1888         };
1889         
1890         return modal = {
1891             cls: "modal fade",
1892             style : 'display: none',
1893             cn : [
1894                 {
1895                     cls: "modal-dialog",
1896                     cn : [
1897                         {
1898                             cls : "modal-content",
1899                             cn : [
1900                                 {
1901                                     cls : 'modal-header',
1902                                     cn : [
1903                                         {
1904                                             tag: 'button',
1905                                             cls : 'close',
1906                                             html : '&times'
1907                                         },
1908                                         title
1909                                     ]
1910                                 },
1911                                 bdy,
1912                                 {
1913                                     cls : 'modal-footer',
1914                                     cn : [
1915                                         {
1916                                             tag: 'div',
1917                                             cls: 'btn-' + this.buttonPosition
1918                                         }
1919                                     ]
1920                                     
1921                                 }
1922                                 
1923                                 
1924                             ]
1925                             
1926                         }
1927                     ]
1928                         
1929                 }
1930             ]
1931             
1932             
1933         };
1934           
1935     },
1936     getChildContainer : function() {
1937          
1938          return this.el.select('.modal-body',true).first();
1939         
1940     },
1941     getButtonContainer : function() {
1942          return this.el.select('.modal-footer div',true).first();
1943         
1944     },
1945     initEvents : function()
1946     {
1947         this.el.select('.modal-header .close').on('click', this.hide, this);
1948 //        
1949 //        this.addxtype(this);
1950     },
1951     show : function() {
1952         
1953         if (!this.rendered) {
1954             this.render();
1955         }
1956        
1957         this.el.addClass('on');
1958         this.el.removeClass('fade');
1959         this.el.setStyle('display', 'block');
1960         Roo.get(document.body).addClass("x-body-masked");
1961         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1962         this.maskEl.show();
1963         this.el.setStyle('zIndex', '10001');
1964         this.fireEvent('show', this);
1965         
1966         
1967     },
1968     hide : function()
1969     {
1970         Roo.log('Modal hide?!');
1971         this.maskEl.hide();
1972         Roo.get(document.body).removeClass("x-body-masked");
1973         this.el.removeClass('on');
1974         this.el.addClass('fade');
1975         this.el.setStyle('display', 'none');
1976         this.fireEvent('hide', this);
1977     },
1978     
1979     addButton : function(str, cb)
1980     {
1981          
1982         
1983         var b = Roo.apply({}, { html : str } );
1984         b.xns = b.xns || Roo.bootstrap;
1985         b.xtype = b.xtype || 'Button';
1986         if (typeof(b.listeners) == 'undefined') {
1987             b.listeners = { click : cb.createDelegate(this)  };
1988         }
1989         
1990         var btn = Roo.factory(b);
1991            
1992         btn.onRender(this.el.select('.modal-footer div').first());
1993         
1994         return btn;   
1995        
1996     },
1997     
1998     setDefaultButton : function(btn)
1999     {
2000         //this.el.select('.modal-footer').()
2001     },
2002     resizeTo: function(w,h)
2003     {
2004         // skip..
2005     },
2006     setContentSize  : function(w, h)
2007     {
2008         
2009     },
2010     onButtonClick: function(btn,e)
2011     {
2012         //Roo.log([a,b,c]);
2013         this.fireEvent('btnclick', btn.name, e);
2014     },
2015     setTitle: function(str) {
2016         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2017         
2018     }
2019 });
2020
2021
2022 Roo.apply(Roo.bootstrap.Modal,  {
2023     /**
2024          * Button config that displays a single OK button
2025          * @type Object
2026          */
2027         OK :  [{
2028             name : 'ok',
2029             weight : 'primary',
2030             html : 'OK'
2031         }], 
2032         /**
2033          * Button config that displays Yes and No buttons
2034          * @type Object
2035          */
2036         YESNO : [
2037             {
2038                 name  : 'no',
2039                 html : 'No'
2040             },
2041             {
2042                 name  :'yes',
2043                 weight : 'primary',
2044                 html : 'Yes'
2045             }
2046         ],
2047         
2048         /**
2049          * Button config that displays OK and Cancel buttons
2050          * @type Object
2051          */
2052         OKCANCEL : [
2053             {
2054                name : 'cancel',
2055                 html : 'Cancel'
2056             },
2057             {
2058                 name : 'ok',
2059                 weight : 'primary',
2060                 html : 'OK'
2061             }
2062         ],
2063         /**
2064          * Button config that displays Yes, No and Cancel buttons
2065          * @type Object
2066          */
2067         YESNOCANCEL : [
2068             {
2069                 name : 'yes',
2070                 weight : 'primary',
2071                 html : 'Yes'
2072             },
2073             {
2074                 name : 'no',
2075                 html : 'No'
2076             },
2077             {
2078                 name : 'cancel',
2079                 html : 'Cancel'
2080             }
2081         ]
2082 });
2083  /*
2084  * - LGPL
2085  *
2086  * messagebox - can be used as a replace
2087  * 
2088  */
2089 /**
2090  * @class Roo.MessageBox
2091  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2092  * Example usage:
2093  *<pre><code>
2094 // Basic alert:
2095 Roo.Msg.alert('Status', 'Changes saved successfully.');
2096
2097 // Prompt for user data:
2098 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2099     if (btn == 'ok'){
2100         // process text value...
2101     }
2102 });
2103
2104 // Show a dialog using config options:
2105 Roo.Msg.show({
2106    title:'Save Changes?',
2107    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2108    buttons: Roo.Msg.YESNOCANCEL,
2109    fn: processResult,
2110    animEl: 'elId'
2111 });
2112 </code></pre>
2113  * @singleton
2114  */
2115 Roo.bootstrap.MessageBox = function(){
2116     var dlg, opt, mask, waitTimer;
2117     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2118     var buttons, activeTextEl, bwidth;
2119
2120     
2121     // private
2122     var handleButton = function(button){
2123         dlg.hide();
2124         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2125     };
2126
2127     // private
2128     var handleHide = function(){
2129         if(opt && opt.cls){
2130             dlg.el.removeClass(opt.cls);
2131         }
2132         //if(waitTimer){
2133         //    Roo.TaskMgr.stop(waitTimer);
2134         //    waitTimer = null;
2135         //}
2136     };
2137
2138     // private
2139     var updateButtons = function(b){
2140         var width = 0;
2141         if(!b){
2142             buttons["ok"].hide();
2143             buttons["cancel"].hide();
2144             buttons["yes"].hide();
2145             buttons["no"].hide();
2146             //dlg.footer.dom.style.display = 'none';
2147             return width;
2148         }
2149         dlg.footer.dom.style.display = '';
2150         for(var k in buttons){
2151             if(typeof buttons[k] != "function"){
2152                 if(b[k]){
2153                     buttons[k].show();
2154                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2155                     width += buttons[k].el.getWidth()+15;
2156                 }else{
2157                     buttons[k].hide();
2158                 }
2159             }
2160         }
2161         return width;
2162     };
2163
2164     // private
2165     var handleEsc = function(d, k, e){
2166         if(opt && opt.closable !== false){
2167             dlg.hide();
2168         }
2169         if(e){
2170             e.stopEvent();
2171         }
2172     };
2173
2174     return {
2175         /**
2176          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2177          * @return {Roo.BasicDialog} The BasicDialog element
2178          */
2179         getDialog : function(){
2180            if(!dlg){
2181                 dlg = new Roo.bootstrap.Modal( {
2182                     //draggable: true,
2183                     //resizable:false,
2184                     //constraintoviewport:false,
2185                     //fixedcenter:true,
2186                     //collapsible : false,
2187                     //shim:true,
2188                     //modal: true,
2189                   //  width:400,
2190                   //  height:100,
2191                     //buttonAlign:"center",
2192                     closeClick : function(){
2193                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2194                             handleButton("no");
2195                         }else{
2196                             handleButton("cancel");
2197                         }
2198                     }
2199                 });
2200                 dlg.render();
2201                 dlg.on("hide", handleHide);
2202                 mask = dlg.mask;
2203                 //dlg.addKeyListener(27, handleEsc);
2204                 buttons = {};
2205                 this.buttons = buttons;
2206                 var bt = this.buttonText;
2207                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2208                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2209                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2210                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2211                 Roo.log(buttons)
2212                 bodyEl = dlg.body.createChild({
2213
2214                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2215                         '<textarea class="roo-mb-textarea"></textarea>' +
2216                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2217                 });
2218                 msgEl = bodyEl.dom.firstChild;
2219                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2220                 textboxEl.enableDisplayMode();
2221                 textboxEl.addKeyListener([10,13], function(){
2222                     if(dlg.isVisible() && opt && opt.buttons){
2223                         if(opt.buttons.ok){
2224                             handleButton("ok");
2225                         }else if(opt.buttons.yes){
2226                             handleButton("yes");
2227                         }
2228                     }
2229                 });
2230                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2231                 textareaEl.enableDisplayMode();
2232                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2233                 progressEl.enableDisplayMode();
2234                 var pf = progressEl.dom.firstChild;
2235                 if (pf) {
2236                     pp = Roo.get(pf.firstChild);
2237                     pp.setHeight(pf.offsetHeight);
2238                 }
2239                 
2240             }
2241             return dlg;
2242         },
2243
2244         /**
2245          * Updates the message box body text
2246          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2247          * the XHTML-compliant non-breaking space character '&amp;#160;')
2248          * @return {Roo.MessageBox} This message box
2249          */
2250         updateText : function(text){
2251             if(!dlg.isVisible() && !opt.width){
2252                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2253             }
2254             msgEl.innerHTML = text || '&#160;';
2255       
2256             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2257             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2258             var w = Math.max(
2259                     Math.min(opt.width || cw , this.maxWidth), 
2260                     Math.max(opt.minWidth || this.minWidth, bwidth)
2261             );
2262             if(opt.prompt){
2263                 activeTextEl.setWidth(w);
2264             }
2265             if(dlg.isVisible()){
2266                 dlg.fixedcenter = false;
2267             }
2268             // to big, make it scroll. = But as usual stupid IE does not support
2269             // !important..
2270             
2271             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2272                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2273                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2274             } else {
2275                 bodyEl.dom.style.height = '';
2276                 bodyEl.dom.style.overflowY = '';
2277             }
2278             if (cw > w) {
2279                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2280             } else {
2281                 bodyEl.dom.style.overflowX = '';
2282             }
2283             
2284             dlg.setContentSize(w, bodyEl.getHeight());
2285             if(dlg.isVisible()){
2286                 dlg.fixedcenter = true;
2287             }
2288             return this;
2289         },
2290
2291         /**
2292          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2293          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2294          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2295          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2296          * @return {Roo.MessageBox} This message box
2297          */
2298         updateProgress : function(value, text){
2299             if(text){
2300                 this.updateText(text);
2301             }
2302             if (pp) { // weird bug on my firefox - for some reason this is not defined
2303                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2304             }
2305             return this;
2306         },        
2307
2308         /**
2309          * Returns true if the message box is currently displayed
2310          * @return {Boolean} True if the message box is visible, else false
2311          */
2312         isVisible : function(){
2313             return dlg && dlg.isVisible();  
2314         },
2315
2316         /**
2317          * Hides the message box if it is displayed
2318          */
2319         hide : function(){
2320             if(this.isVisible()){
2321                 dlg.hide();
2322             }  
2323         },
2324
2325         /**
2326          * Displays a new message box, or reinitializes an existing message box, based on the config options
2327          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2328          * The following config object properties are supported:
2329          * <pre>
2330 Property    Type             Description
2331 ----------  ---------------  ------------------------------------------------------------------------------------
2332 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2333                                    closes (defaults to undefined)
2334 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2335                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2336 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2337                                    progress and wait dialogs will ignore this property and always hide the
2338                                    close button as they can only be closed programmatically.
2339 cls               String           A custom CSS class to apply to the message box element
2340 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2341                                    displayed (defaults to 75)
2342 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2343                                    function will be btn (the name of the button that was clicked, if applicable,
2344                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2345                                    Progress and wait dialogs will ignore this option since they do not respond to
2346                                    user actions and can only be closed programmatically, so any required function
2347                                    should be called by the same code after it closes the dialog.
2348 icon              String           A CSS class that provides a background image to be used as an icon for
2349                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2350 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2351 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2352 modal             Boolean          False to allow user interaction with the page while the message box is
2353                                    displayed (defaults to true)
2354 msg               String           A string that will replace the existing message box body text (defaults
2355                                    to the XHTML-compliant non-breaking space character '&#160;')
2356 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2357 progress          Boolean          True to display a progress bar (defaults to false)
2358 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2359 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2360 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2361 title             String           The title text
2362 value             String           The string value to set into the active textbox element if displayed
2363 wait              Boolean          True to display a progress bar (defaults to false)
2364 width             Number           The width of the dialog in pixels
2365 </pre>
2366          *
2367          * Example usage:
2368          * <pre><code>
2369 Roo.Msg.show({
2370    title: 'Address',
2371    msg: 'Please enter your address:',
2372    width: 300,
2373    buttons: Roo.MessageBox.OKCANCEL,
2374    multiline: true,
2375    fn: saveAddress,
2376    animEl: 'addAddressBtn'
2377 });
2378 </code></pre>
2379          * @param {Object} config Configuration options
2380          * @return {Roo.MessageBox} This message box
2381          */
2382         show : function(options)
2383         {
2384             
2385             // this causes nightmares if you show one dialog after another
2386             // especially on callbacks..
2387              
2388             if(this.isVisible()){
2389                 
2390                 this.hide();
2391                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2392                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2393                 Roo.log("New Dialog Message:" +  options.msg )
2394                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2395                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2396                 
2397             }
2398             var d = this.getDialog();
2399             opt = options;
2400             d.setTitle(opt.title || "&#160;");
2401             d.close.setDisplayed(opt.closable !== false);
2402             activeTextEl = textboxEl;
2403             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2404             if(opt.prompt){
2405                 if(opt.multiline){
2406                     textboxEl.hide();
2407                     textareaEl.show();
2408                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2409                         opt.multiline : this.defaultTextHeight);
2410                     activeTextEl = textareaEl;
2411                 }else{
2412                     textboxEl.show();
2413                     textareaEl.hide();
2414                 }
2415             }else{
2416                 textboxEl.hide();
2417                 textareaEl.hide();
2418             }
2419             progressEl.setDisplayed(opt.progress === true);
2420             this.updateProgress(0);
2421             activeTextEl.dom.value = opt.value || "";
2422             if(opt.prompt){
2423                 dlg.setDefaultButton(activeTextEl);
2424             }else{
2425                 var bs = opt.buttons;
2426                 var db = null;
2427                 if(bs && bs.ok){
2428                     db = buttons["ok"];
2429                 }else if(bs && bs.yes){
2430                     db = buttons["yes"];
2431                 }
2432                 dlg.setDefaultButton(db);
2433             }
2434             bwidth = updateButtons(opt.buttons);
2435             this.updateText(opt.msg);
2436             if(opt.cls){
2437                 d.el.addClass(opt.cls);
2438             }
2439             d.proxyDrag = opt.proxyDrag === true;
2440             d.modal = opt.modal !== false;
2441             d.mask = opt.modal !== false ? mask : false;
2442             if(!d.isVisible()){
2443                 // force it to the end of the z-index stack so it gets a cursor in FF
2444                 document.body.appendChild(dlg.el.dom);
2445                 d.animateTarget = null;
2446                 d.show(options.animEl);
2447             }
2448             return this;
2449         },
2450
2451         /**
2452          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2453          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2454          * and closing the message box when the process is complete.
2455          * @param {String} title The title bar text
2456          * @param {String} msg The message box body text
2457          * @return {Roo.MessageBox} This message box
2458          */
2459         progress : function(title, msg){
2460             this.show({
2461                 title : title,
2462                 msg : msg,
2463                 buttons: false,
2464                 progress:true,
2465                 closable:false,
2466                 minWidth: this.minProgressWidth,
2467                 modal : true
2468             });
2469             return this;
2470         },
2471
2472         /**
2473          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2474          * If a callback function is passed it will be called after the user clicks the button, and the
2475          * id of the button that was clicked will be passed as the only parameter to the callback
2476          * (could also be the top-right close button).
2477          * @param {String} title The title bar text
2478          * @param {String} msg The message box body text
2479          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2480          * @param {Object} scope (optional) The scope of the callback function
2481          * @return {Roo.MessageBox} This message box
2482          */
2483         alert : function(title, msg, fn, scope){
2484             this.show({
2485                 title : title,
2486                 msg : msg,
2487                 buttons: this.OK,
2488                 fn: fn,
2489                 scope : scope,
2490                 modal : true
2491             });
2492             return this;
2493         },
2494
2495         /**
2496          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2497          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2498          * You are responsible for closing the message box when the process is complete.
2499          * @param {String} msg The message box body text
2500          * @param {String} title (optional) The title bar text
2501          * @return {Roo.MessageBox} This message box
2502          */
2503         wait : function(msg, title){
2504             this.show({
2505                 title : title,
2506                 msg : msg,
2507                 buttons: false,
2508                 closable:false,
2509                 progress:true,
2510                 modal:true,
2511                 width:300,
2512                 wait:true
2513             });
2514             waitTimer = Roo.TaskMgr.start({
2515                 run: function(i){
2516                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2517                 },
2518                 interval: 1000
2519             });
2520             return this;
2521         },
2522
2523         /**
2524          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2525          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2526          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2527          * @param {String} title The title bar text
2528          * @param {String} msg The message box body text
2529          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2530          * @param {Object} scope (optional) The scope of the callback function
2531          * @return {Roo.MessageBox} This message box
2532          */
2533         confirm : function(title, msg, fn, scope){
2534             this.show({
2535                 title : title,
2536                 msg : msg,
2537                 buttons: this.YESNO,
2538                 fn: fn,
2539                 scope : scope,
2540                 modal : true
2541             });
2542             return this;
2543         },
2544
2545         /**
2546          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2547          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2548          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2549          * (could also be the top-right close button) and the text that was entered will be passed as the two
2550          * parameters to the callback.
2551          * @param {String} title The title bar text
2552          * @param {String} msg The message box body text
2553          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2554          * @param {Object} scope (optional) The scope of the callback function
2555          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2556          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2557          * @return {Roo.MessageBox} This message box
2558          */
2559         prompt : function(title, msg, fn, scope, multiline){
2560             this.show({
2561                 title : title,
2562                 msg : msg,
2563                 buttons: this.OKCANCEL,
2564                 fn: fn,
2565                 minWidth:250,
2566                 scope : scope,
2567                 prompt:true,
2568                 multiline: multiline,
2569                 modal : true
2570             });
2571             return this;
2572         },
2573
2574         /**
2575          * Button config that displays a single OK button
2576          * @type Object
2577          */
2578         OK : {ok:true},
2579         /**
2580          * Button config that displays Yes and No buttons
2581          * @type Object
2582          */
2583         YESNO : {yes:true, no:true},
2584         /**
2585          * Button config that displays OK and Cancel buttons
2586          * @type Object
2587          */
2588         OKCANCEL : {ok:true, cancel:true},
2589         /**
2590          * Button config that displays Yes, No and Cancel buttons
2591          * @type Object
2592          */
2593         YESNOCANCEL : {yes:true, no:true, cancel:true},
2594
2595         /**
2596          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2597          * @type Number
2598          */
2599         defaultTextHeight : 75,
2600         /**
2601          * The maximum width in pixels of the message box (defaults to 600)
2602          * @type Number
2603          */
2604         maxWidth : 600,
2605         /**
2606          * The minimum width in pixels of the message box (defaults to 100)
2607          * @type Number
2608          */
2609         minWidth : 100,
2610         /**
2611          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2612          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2613          * @type Number
2614          */
2615         minProgressWidth : 250,
2616         /**
2617          * An object containing the default button text strings that can be overriden for localized language support.
2618          * Supported properties are: ok, cancel, yes and no.
2619          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2620          * @type Object
2621          */
2622         buttonText : {
2623             ok : "OK",
2624             cancel : "Cancel",
2625             yes : "Yes",
2626             no : "No"
2627         }
2628     };
2629 }();
2630
2631 /**
2632  * Shorthand for {@link Roo.MessageBox}
2633  */
2634 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2635 Roo.Msg = Roo.Msg || Roo.MessageBox;
2636 /*
2637  * - LGPL
2638  *
2639  * navbar
2640  * 
2641  */
2642
2643 /**
2644  * @class Roo.bootstrap.Navbar
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap Navbar class
2647  * @cfg {Boolean} sidebar has side bar
2648  * @cfg {Boolean} bar is a bar?
2649  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2650  * @cfg {String} brand what is brand
2651  * @cfg {Boolean} inverse is inverted color
2652  * @cfg {String} type (nav | pills | tabs)
2653  * @cfg {Boolean} arrangement stacked | justified
2654  * @cfg {String} align (left | right) alignment
2655  * @cfg {String} brand_href href of the brand
2656  * @cfg {Boolean} main (true|false) main nav bar? default false
2657  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2658  *
2659  * 
2660  * @constructor
2661  * Create a new Navbar
2662  * @param {Object} config The config object
2663  */
2664
2665
2666 Roo.bootstrap.Navbar = function(config){
2667     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2668    
2669     
2670 };
2671
2672 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2673     
2674     sidebar: false,
2675     
2676     bar: false,
2677     brand: '',
2678     inverse: false,
2679     position: '',
2680     align : false,
2681     type: 'nav',
2682     arrangement: '',
2683     brand_href: false,
2684     main : false,
2685     loadMask : false,
2686     
2687     
2688     // private
2689     navItems : false,
2690     
2691     getAutoCreate : function(){
2692         var cfg = {
2693             cls : 'navbar'
2694         };
2695         
2696         if (this.sidebar === true) {
2697             cfg = {
2698                 tag: 'div',
2699                 cls: 'sidebar-nav'
2700             };
2701             return cfg;
2702         }
2703         
2704         if (this.bar === true) {
2705             cfg = {
2706                 tag: 'nav',
2707                 cls: 'navbar',
2708                 role: 'navigation',
2709                 cn: [
2710                     {
2711                         tag: 'div',
2712                         cls: 'navbar-header',
2713                         cn: [
2714                             {
2715                             tag: 'button',
2716                             type: 'button',
2717                             cls: 'navbar-toggle',
2718                             'data-toggle': 'collapse',
2719                             cn: [
2720                                 {
2721                                     tag: 'span',
2722                                     cls: 'sr-only',
2723                                     html: 'Toggle navigation'
2724                                 },
2725                                 {
2726                                     tag: 'span',
2727                                     cls: 'icon-bar'
2728                                 },
2729                                 {
2730                                     tag: 'span',
2731                                     cls: 'icon-bar'
2732                                 },
2733                                 {
2734                                     tag: 'span',
2735                                     cls: 'icon-bar'
2736                                 }
2737                             ]
2738                             }
2739                         ]
2740                     },
2741                     {
2742                     tag: 'div',
2743                     cls: 'collapse navbar-collapse'
2744                     }
2745                 ]
2746             };
2747             
2748             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2749             
2750             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2751                 cfg.cls += ' navbar-' + this.position;
2752                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2753             }
2754             
2755             if (this.brand !== '') {
2756                 cfg.cn[0].cn.push({
2757                     tag: 'a',
2758                     href: this.brand_href ? this.brand_href : '#',
2759                     cls: 'navbar-brand',
2760                     cn: [
2761                     this.brand
2762                     ]
2763                 });
2764             }
2765             
2766             if(this.main){
2767                 cfg.cls += ' main-nav';
2768             }
2769             
2770             
2771             return cfg;
2772         
2773         } else if (this.bar === false) {
2774             
2775         } else {
2776             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2777         }
2778         
2779         cfg.cn = [
2780             {
2781                 cls: 'nav',
2782                 tag : 'ul'
2783             }
2784         ];
2785         
2786         if (['tabs','pills'].indexOf(this.type)!==-1) {
2787             cfg.cn[0].cls += ' nav-' + this.type
2788         } else {
2789             if (this.type!=='nav') {
2790             Roo.log('nav type must be nav/tabs/pills')
2791             }
2792             cfg.cn[0].cls += ' navbar-nav'
2793         }
2794         
2795         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2796             cfg.cn[0].cls += ' nav-' + this.arrangement;
2797         }
2798         
2799         if (this.align === 'right') {
2800             cfg.cn[0].cls += ' navbar-right';
2801         }
2802         if (this.inverse) {
2803             cfg.cls += ' navbar-inverse';
2804             
2805         }
2806         
2807         
2808         return cfg;
2809     },
2810     
2811     initEvents :function ()
2812     {
2813         //Roo.log(this.el.select('.navbar-toggle',true));
2814         this.el.select('.navbar-toggle',true).on('click', function() {
2815            // Roo.log('click');
2816             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2817         }, this);
2818         
2819         var mark = {
2820             tag: "div",
2821             cls:"x-dlg-mask"
2822         }
2823         
2824         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2825         
2826         var size = this.el.getSize();
2827         this.maskEl.setSize(size.width, size.height);
2828         this.maskEl.enableDisplayMode("block");
2829         this.maskEl.hide();
2830         
2831         if(this.loadMask){
2832             this.maskEl.show();
2833         }
2834     },
2835     
2836     
2837     getChildContainer : function()
2838     {
2839         if (this.bar === true) {
2840             return this.el.select('.collapse',true).first();
2841         }
2842         
2843         return this.el;
2844     },
2845     
2846     mask : function()
2847     {
2848         this.maskEl.show();
2849     },
2850     
2851     unmask : function()
2852     {
2853         this.maskEl.hide();
2854     }
2855     
2856     
2857     
2858 });
2859
2860
2861
2862  
2863
2864  /*
2865  * - LGPL
2866  *
2867  * nav group
2868  * 
2869  */
2870
2871 /**
2872  * @class Roo.bootstrap.NavGroup
2873  * @extends Roo.bootstrap.Component
2874  * Bootstrap NavGroup class
2875  * @cfg {String} align left | right
2876  * @cfg {Boolean} inverse false | true
2877  * @cfg {String} type (nav|pills|tab) default nav
2878  * @cfg {String} navId - reference Id for navbar.
2879
2880  * 
2881  * @constructor
2882  * Create a new nav group
2883  * @param {Object} config The config object
2884  */
2885
2886 Roo.bootstrap.NavGroup = function(config){
2887     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2888     this.navItems = [];
2889     Roo.bootstrap.NavGroup.register(this);
2890      this.addEvents({
2891         /**
2892              * @event changed
2893              * Fires when the active item changes
2894              * @param {Roo.bootstrap.NavGroup} this
2895              * @param {Roo.bootstrap.Navbar.Item} item The item selected
2896              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
2897          */
2898         'changed': true
2899      });
2900     
2901 };
2902
2903 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2904     
2905     align: '',
2906     inverse: false,
2907     form: false,
2908     type: 'nav',
2909     navId : '',
2910     // private
2911     
2912     navItems : false,
2913     
2914     getAutoCreate : function()
2915     {
2916         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2917         
2918         cfg = {
2919             tag : 'ul',
2920             cls: 'nav' 
2921         }
2922         
2923         if (['tabs','pills'].indexOf(this.type)!==-1) {
2924             cfg.cls += ' nav-' + this.type
2925         } else {
2926             if (this.type!=='nav') {
2927                 Roo.log('nav type must be nav/tabs/pills')
2928             }
2929             cfg.cls += ' navbar-nav'
2930         }
2931         
2932         if (this.parent().sidebar === true) {
2933             cfg = {
2934                 tag: 'ul',
2935                 cls: 'dashboard-menu'
2936             }
2937             
2938             return cfg;
2939         }
2940         
2941         if (this.form === true) {
2942             cfg = {
2943                 tag: 'form',
2944                 cls: 'navbar-form'
2945             }
2946             
2947             if (this.align === 'right') {
2948                 cfg.cls += ' navbar-right';
2949             } else {
2950                 cfg.cls += ' navbar-left';
2951             }
2952         }
2953         
2954         if (this.align === 'right') {
2955             cfg.cls += ' navbar-right';
2956         }
2957         
2958         if (this.inverse) {
2959             cfg.cls += ' navbar-inverse';
2960             
2961         }
2962         
2963         
2964         return cfg;
2965     },
2966     
2967     setActiveItem : function(item)
2968     {
2969         var prev = false;
2970         Roo.each(this.navItems, function(v){
2971             if (v == item) {
2972                 return ;
2973             }
2974             if (v.isActive()) {
2975                 v.setActive(false, true);
2976                 prev = v;
2977                 
2978             }
2979             
2980         });
2981
2982         item.setActive(true, true);
2983         this.fireEvent('changed', this, item, prev);
2984         
2985         
2986     },
2987     
2988     
2989     register : function(item)
2990     {
2991         this.navItems.push( item);
2992         item.navId = this.navId;
2993     
2994     },
2995     getNavItem: function(tabId)
2996     {
2997         var ret = false;
2998         Roo.each(this.navItems, function(e) {
2999             if (e.tabId == tabId) {
3000                ret =  e;
3001                return false;
3002             }
3003             return true;
3004             
3005         });
3006         return ret;
3007     }
3008 });
3009
3010  
3011 Roo.apply(Roo.bootstrap.NavGroup, {
3012     
3013     groups: {},
3014     
3015     register : function(navgrp)
3016     {
3017         this.groups[navgrp.navId] = navgrp;
3018         
3019     },
3020     get: function(navId) {
3021         return this.groups[navId];
3022     }
3023     
3024     
3025     
3026 });
3027
3028  /*
3029  * - LGPL
3030  *
3031  * row
3032  * 
3033  */
3034
3035 /**
3036  * @class Roo.bootstrap.Navbar.Item
3037  * @extends Roo.bootstrap.Component
3038  * Bootstrap Navbar.Button class
3039  * @cfg {String} href  link to
3040  * @cfg {String} html content of button
3041  * @cfg {String} badge text inside badge
3042  * @cfg {String} glyphicon name of glyphicon
3043  * @cfg {String} icon name of font awesome icon
3044  * @cfg {Boolean} active Is item active
3045  * @cfg {Boolean} preventDefault (true | false) default false
3046  * @cfg {String} tabId the tab that this item activates.
3047   
3048  * @constructor
3049  * Create a new Navbar Button
3050  * @param {Object} config The config object
3051  */
3052 Roo.bootstrap.Navbar.Item = function(config){
3053     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3054     this.addEvents({
3055         // raw events
3056         /**
3057          * @event click
3058          * The raw click event for the entire grid.
3059          * @param {Roo.EventObject} e
3060          */
3061         "click" : true,
3062          /**
3063             * @event changed
3064             * Fires when the active item active state changes
3065             * @param {Roo.bootstrap.Navbar.Item} this
3066             * @param {boolean} state the new state
3067              
3068          */
3069         'changed': true
3070     });
3071    
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
3075     
3076     href: false,
3077     html: '',
3078     badge: '',
3079     icon: false,
3080     glyphicon: false,
3081     active: false,
3082     preventDefault : false,
3083     tabId : false,
3084     
3085     getAutoCreate : function(){
3086         
3087         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3088         
3089         if (this.parent().parent().sidebar === true) {
3090             cfg = {
3091                 tag: 'li',
3092                 cls: '',
3093                 cn: [
3094                     {
3095                         tag: 'p',
3096                         cls: ''
3097                     }
3098                 ]
3099             }
3100             
3101             if (this.html) {
3102                 cfg.cn[0].html = this.html;
3103             }
3104             
3105             if (this.active) {
3106                 this.cls += ' active';
3107             }
3108             
3109             if (this.menu) {
3110                 cfg.cn[0].cls += ' dropdown-toggle';
3111                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3112             }
3113             
3114             if (this.href) {
3115                 cfg.cn[0].tag = 'a',
3116                 cfg.cn[0].href = this.href;
3117             }
3118             
3119             if (this.glyphicon) {
3120                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3121             }
3122             
3123             if (this.icon) {
3124                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3125             }
3126             
3127             return cfg;
3128         }
3129         
3130         cfg = {
3131             tag: 'li',
3132             cls: 'nav-item'
3133         }
3134         
3135         if (this.active) {
3136             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3137         }
3138             
3139         cfg.cn = [
3140             {
3141                 tag: 'p',
3142                 html: 'Text'
3143             }
3144         ];
3145         
3146         if (this.glyphicon) {
3147             if(cfg.html){cfg.html = ' ' + this.html};
3148             cfg.cn=[
3149                 {
3150                     tag: 'span',
3151                     cls: 'glyphicon glyphicon-' + this.glyphicon
3152                 }
3153             ];
3154         }
3155         
3156         cfg.cn[0].html = this.html || cfg.cn[0].html ;
3157         
3158         if (this.menu) {
3159             cfg.cn[0].tag='a';
3160             cfg.cn[0].href='#';
3161             cfg.cn[0].html += " <span class='caret'></span>";
3162         //}else if (!this.href) {
3163         //    cfg.cn[0].tag='p';
3164         //    cfg.cn[0].cls='navbar-text';
3165         } else {
3166             cfg.cn[0].tag='a';
3167             cfg.cn[0].href=this.href||'#';
3168             cfg.cn[0].html=this.html;
3169         }
3170         
3171         if (this.badge !== '') {
3172             
3173             cfg.cn[0].cn=[
3174                 cfg.cn[0].html + ' ',
3175                 {
3176                     tag: 'span',
3177                     cls: 'badge',
3178                     html: this.badge
3179                 }
3180             ];
3181             cfg.cn[0].html=''
3182         }
3183          
3184         if (this.icon) {
3185             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3186         }
3187         
3188         return cfg;
3189     },
3190     initEvents: function() {
3191        // Roo.log('init events?');
3192        // Roo.log(this.el.dom);
3193         this.el.select('a',true).on('click', this.onClick, this);
3194         // at this point parent should be available..
3195         this.parent().register(this);
3196     },
3197     
3198     onClick : function(e)
3199     {
3200         if(this.preventDefault){
3201             e.preventDefault();
3202         }
3203         
3204         if(this.fireEvent('click', this, e) === false){
3205             return;
3206         };
3207         
3208         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3209              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3210                 this.parent().setActiveItem(this);
3211             }
3212             
3213             
3214             
3215         } 
3216     },
3217     
3218     isActive: function () {
3219         return this.active
3220     },
3221     setActive : function(state, fire)
3222     {
3223         this.active = state;
3224         if (!state ) {
3225             this.el.removeClass('active');
3226         } else if (!this.el.hasClass('active')) {
3227             this.el.addClass('active');
3228         }
3229         if (fire) {
3230             this.fireEvent('changed', this, state);
3231         }
3232         
3233         
3234     }
3235      // this should not be here...
3236  
3237 });
3238  
3239
3240  /*
3241  * - LGPL
3242  *
3243  * row
3244  * 
3245  */
3246
3247 /**
3248  * @class Roo.bootstrap.Row
3249  * @extends Roo.bootstrap.Component
3250  * Bootstrap Row class (contains columns...)
3251  * 
3252  * @constructor
3253  * Create a new Row
3254  * @param {Object} config The config object
3255  */
3256
3257 Roo.bootstrap.Row = function(config){
3258     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3259 };
3260
3261 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3262     
3263     getAutoCreate : function(){
3264        return {
3265             cls: 'row clearfix'
3266        };
3267     }
3268     
3269     
3270 });
3271
3272  
3273
3274  /*
3275  * - LGPL
3276  *
3277  * element
3278  * 
3279  */
3280
3281 /**
3282  * @class Roo.bootstrap.Element
3283  * @extends Roo.bootstrap.Component
3284  * Bootstrap Element class
3285  * @cfg {String} html contents of the element
3286  * @cfg {String} tag tag of the element
3287  * @cfg {String} cls class of the element
3288  * 
3289  * @constructor
3290  * Create a new Element
3291  * @param {Object} config The config object
3292  */
3293
3294 Roo.bootstrap.Element = function(config){
3295     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3296 };
3297
3298 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3299     
3300     tag: 'div',
3301     cls: '',
3302     html: '',
3303      
3304     
3305     getAutoCreate : function(){
3306         
3307         var cfg = {
3308             tag: this.tag,
3309             cls: this.cls,
3310             html: this.html
3311         }
3312         
3313         
3314         
3315         return cfg;
3316     }
3317    
3318 });
3319
3320  
3321
3322  /*
3323  * - LGPL
3324  *
3325  * pagination
3326  * 
3327  */
3328
3329 /**
3330  * @class Roo.bootstrap.Pagination
3331  * @extends Roo.bootstrap.Component
3332  * Bootstrap Pagination class
3333  * @cfg {String} size xs | sm | md | lg
3334  * @cfg {Boolean} inverse false | true
3335  * 
3336  * @constructor
3337  * Create a new Pagination
3338  * @param {Object} config The config object
3339  */
3340
3341 Roo.bootstrap.Pagination = function(config){
3342     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3346     
3347     cls: false,
3348     size: false,
3349     inverse: false,
3350     
3351     getAutoCreate : function(){
3352         var cfg = {
3353             tag: 'ul',
3354                 cls: 'pagination'
3355         };
3356         if (this.inverse) {
3357             cfg.cls += ' inverse';
3358         }
3359         if (this.html) {
3360             cfg.html=this.html;
3361         }
3362         if (this.cls) {
3363             cfg.cls += " " + this.cls;
3364         }
3365         return cfg;
3366     }
3367    
3368 });
3369
3370  
3371
3372  /*
3373  * - LGPL
3374  *
3375  * Pagination item
3376  * 
3377  */
3378
3379
3380 /**
3381  * @class Roo.bootstrap.PaginationItem
3382  * @extends Roo.bootstrap.Component
3383  * Bootstrap PaginationItem class
3384  * @cfg {String} html text
3385  * @cfg {String} href the link
3386  * @cfg {Boolean} preventDefault (true | false) default true
3387  * @cfg {Boolean} active (true | false) default false
3388  * 
3389  * 
3390  * @constructor
3391  * Create a new PaginationItem
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.PaginationItem = function(config){
3397     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3398     this.addEvents({
3399         // raw events
3400         /**
3401          * @event click
3402          * The raw click event for the entire grid.
3403          * @param {Roo.EventObject} e
3404          */
3405         "click" : true
3406     });
3407 };
3408
3409 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3410     
3411     href : false,
3412     html : false,
3413     preventDefault: true,
3414     active : false,
3415     cls : false,
3416     
3417     getAutoCreate : function(){
3418         var cfg= {
3419             tag: 'li',
3420             cn: [
3421                 {
3422                     tag : 'a',
3423                     href : this.href ? this.href : '#',
3424                     html : this.html ? this.html : ''
3425                 }
3426             ]
3427         };
3428         
3429         if(this.cls){
3430             cfg.cls = this.cls;
3431         }
3432         
3433         if(this.active){
3434             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3435         }
3436         
3437         return cfg;
3438     },
3439     
3440     initEvents: function() {
3441         
3442         this.el.on('click', this.onClick, this);
3443         
3444     },
3445     onClick : function(e)
3446     {
3447         Roo.log('PaginationItem on click ');
3448         if(this.preventDefault){
3449             e.preventDefault();
3450         }
3451         
3452         this.fireEvent('click', this, e);
3453     }
3454    
3455 });
3456
3457  
3458
3459  /*
3460  * - LGPL
3461  *
3462  * slider
3463  * 
3464  */
3465
3466
3467 /**
3468  * @class Roo.bootstrap.Slider
3469  * @extends Roo.bootstrap.Component
3470  * Bootstrap Slider class
3471  *    
3472  * @constructor
3473  * Create a new Slider
3474  * @param {Object} config The config object
3475  */
3476
3477 Roo.bootstrap.Slider = function(config){
3478     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3479 };
3480
3481 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3482     
3483     getAutoCreate : function(){
3484         
3485         var cfg = {
3486             tag: 'div',
3487             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3488             cn: [
3489                 {
3490                     tag: 'a',
3491                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3492                 }
3493             ]
3494         }
3495         
3496         return cfg;
3497     }
3498    
3499 });
3500
3501  /*
3502  * - LGPL
3503  *
3504  * table
3505  * 
3506  */
3507
3508 /**
3509  * @class Roo.bootstrap.Table
3510  * @extends Roo.bootstrap.Component
3511  * Bootstrap Table class
3512  * @cfg {String} cls table class
3513  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3514  * @cfg {String} bgcolor Specifies the background color for a table
3515  * @cfg {Number} border Specifies whether the table cells should have borders or not
3516  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3517  * @cfg {Number} cellspacing Specifies the space between cells
3518  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3519  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3520  * @cfg {String} sortable Specifies that the table should be sortable
3521  * @cfg {String} summary Specifies a summary of the content of a table
3522  * @cfg {Number} width Specifies the width of a table
3523  * 
3524  * @cfg {boolean} striped Should the rows be alternative striped
3525  * @cfg {boolean} bordered Add borders to the table
3526  * @cfg {boolean} hover Add hover highlighting
3527  * @cfg {boolean} condensed Format condensed
3528  * @cfg {boolean} responsive Format condensed
3529  *
3530  
3531  
3532  * 
3533  * @constructor
3534  * Create a new Table
3535  * @param {Object} config The config object
3536  */
3537
3538 Roo.bootstrap.Table = function(config){
3539     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3540     
3541     if (this.sm) {
3542         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3543         this.sm = this.selModel;
3544         this.sm.xmodule = this.xmodule || false;
3545     }
3546     if (this.cm && typeof(this.cm.config) == 'undefined') {
3547         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3548         this.cm = this.colModel;
3549         this.cm.xmodule = this.xmodule || false;
3550     }
3551     if (this.store) {
3552         this.store= Roo.factory(this.store, Roo.data);
3553         this.ds = this.store;
3554         this.ds.xmodule = this.xmodule || false;
3555          
3556     }
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3560     
3561     cls: false,
3562     align: false,
3563     bgcolor: false,
3564     border: false,
3565     cellpadding: false,
3566     cellspacing: false,
3567     frame: false,
3568     rules: false,
3569     sortable: false,
3570     summary: false,
3571     width: false,
3572     striped : false,
3573     bordered: false,
3574     hover:  false,
3575     condensed : false,
3576     responsive : false,
3577     sm : false,
3578     cm : false,
3579     store : false,
3580     
3581     getAutoCreate : function(){
3582         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3583         
3584         cfg = {
3585             tag: 'table',
3586             cls : 'table',
3587             cn : []
3588         }
3589             
3590         if (this.striped) {
3591             cfg.cls += ' table-striped';
3592         }
3593         if (this.hover) {
3594             cfg.cls += ' table-hover';
3595         }
3596         if (this.bordered) {
3597             cfg.cls += ' table-bordered';
3598         }
3599         if (this.condensed) {
3600             cfg.cls += ' table-condensed';
3601         }
3602         if (this.responsive) {
3603             cfg.cls += ' table-responsive';
3604         }
3605         
3606           
3607         
3608         
3609         if (this.cls) {
3610             cfg.cls+=  ' ' +this.cls;
3611         }
3612         
3613         // this lot should be simplifed...
3614         
3615         if (this.align) {
3616             cfg.align=this.align;
3617         }
3618         if (this.bgcolor) {
3619             cfg.bgcolor=this.bgcolor;
3620         }
3621         if (this.border) {
3622             cfg.border=this.border;
3623         }
3624         if (this.cellpadding) {
3625             cfg.cellpadding=this.cellpadding;
3626         }
3627         if (this.cellspacing) {
3628             cfg.cellspacing=this.cellspacing;
3629         }
3630         if (this.frame) {
3631             cfg.frame=this.frame;
3632         }
3633         if (this.rules) {
3634             cfg.rules=this.rules;
3635         }
3636         if (this.sortable) {
3637             cfg.sortable=this.sortable;
3638         }
3639         if (this.summary) {
3640             cfg.summary=this.summary;
3641         }
3642         if (this.width) {
3643             cfg.width=this.width;
3644         }
3645         
3646         if(this.store || this.cm){
3647             cfg.cn.push(this.renderHeader());
3648             cfg.cn.push(this.renderBody());
3649             cfg.cn.push(this.renderFooter());
3650             
3651             cfg.cls+=  ' TableGrid';
3652         }
3653         
3654         return cfg;
3655     },
3656 //    
3657 //    initTableGrid : function()
3658 //    {
3659 //        var cfg = {};
3660 //        
3661 //        var header = {
3662 //            tag: 'thead',
3663 //            cn : []
3664 //        };
3665 //        
3666 //        var cm = this.cm;
3667 //        
3668 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3669 //            header.cn.push({
3670 //                tag: 'th',
3671 //                html: cm.getColumnHeader(i)
3672 //            })
3673 //        }
3674 //        
3675 //        cfg.push(header);
3676 //        
3677 //        return cfg;
3678 //        
3679 //        
3680 //    },
3681     
3682     initEvents : function()
3683     {   
3684         if(!this.store || !this.cm){
3685             return;
3686         }
3687         
3688         Roo.log('initEvents with ds!!!!');
3689         
3690         var _this = this;
3691         
3692         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3693             e.on('click', _this.sort, _this);
3694         });
3695 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3696 //        this.maskEl.enableDisplayMode("block");
3697 //        this.maskEl.show();
3698         
3699         this.store.on('load', this.onLoad, this);
3700         this.store.on('beforeload', this.onBeforeLoad, this);
3701         
3702         this.store.load();
3703         
3704         
3705         
3706     },
3707     
3708     sort : function(e,el)
3709     {
3710         var col = Roo.get(el)
3711         
3712         if(!col.hasClass('sortable')){
3713             return;
3714         }
3715         
3716         var sort = col.attr('sort');
3717         var dir = 'ASC';
3718         
3719         if(col.hasClass('glyphicon-arrow-up')){
3720             dir = 'DESC';
3721         }
3722         
3723         this.store.sortInfo = {field : sort, direction : dir};
3724         
3725         this.store.load();
3726     },
3727     
3728     renderHeader : function()
3729     {
3730         var header = {
3731             tag: 'thead',
3732             cn : []
3733         };
3734         
3735         var cm = this.cm;
3736         
3737         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3738             
3739             var config = cm.config[i];
3740             
3741             var c = {
3742                 tag: 'th',
3743                 html: cm.getColumnHeader(i)
3744             };
3745             
3746             if(typeof(config.dataIndex) != 'undefined'){
3747                 c.sort = config.dataIndex;
3748             }
3749             
3750             if(typeof(config.sortable) != 'undefined' && config.sortable){
3751                 c.cls = 'sortable';
3752             }
3753             
3754             if(typeof(config.width) != 'undefined'){
3755                 c.style = 'width:' + config.width + 'px';
3756             }
3757             
3758             header.cn.push(c)
3759         }
3760         
3761         return header;
3762     },
3763     
3764     renderBody : function()
3765     {
3766         var body = {
3767             tag: 'tbody',
3768             cn : []
3769         };
3770         
3771         return body;
3772     },
3773     
3774     renderFooter : function()
3775     {
3776         var footer = {
3777             tag: 'tfoot',
3778             cn : []
3779         };
3780         
3781         return footer;
3782     },
3783     
3784     onLoad : function()
3785     {
3786         Roo.log('ds onload');
3787         
3788         var _this = this;
3789         var cm = this.cm;
3790         
3791         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3792             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3793             
3794             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3795                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3796             }
3797             
3798             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3799                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3800             }
3801         });
3802         
3803         var tbody = this.el.select('tbody', true).first();
3804         
3805         var renders = [];
3806         
3807         if(this.store.getCount() > 0){
3808             this.store.data.each(function(d){
3809                 var row = {
3810                     tag : 'tr',
3811                     cn : []
3812                 };
3813                 
3814                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3815                     var renderer = cm.getRenderer(i);
3816                     var config = cm.config[i];
3817                     var value = '';
3818                     var id = Roo.id();
3819                     
3820                     if(typeof(renderer) !== 'undefined'){
3821                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3822                     }
3823                     
3824                     if(typeof(value) === 'object'){
3825                         renders.push({
3826                             id : id,
3827                             cfg : value 
3828                         })
3829                     }
3830                     
3831                     var td = {
3832                         tag: 'td',
3833                         id: id,
3834                         html: (typeof(value) === 'object') ? '' : value
3835                     };
3836                     
3837                     if(typeof(config.width) != 'undefined'){
3838                         td.style = 'width:' +  config.width + 'px';
3839                     }
3840                     
3841                     row.cn.push(td);
3842                    
3843                 }
3844                 
3845                 tbody.createChild(row);
3846                 
3847             });
3848         }
3849         
3850         
3851         if(renders.length){
3852             var _this = this;
3853             Roo.each(renders, function(r){
3854                 _this.renderColumn(r);
3855             })
3856         }
3857 //        
3858 //        if(this.loadMask){
3859 //            this.maskEl.hide();
3860 //        }
3861     },
3862     
3863     onBeforeLoad : function()
3864     {
3865         Roo.log('ds onBeforeLoad');
3866         
3867         this.clear();
3868         
3869 //        if(this.loadMask){
3870 //            this.maskEl.show();
3871 //        }
3872     },
3873     
3874     clear : function()
3875     {
3876         this.el.select('tbody', true).first().dom.innerHTML = '';
3877     },
3878     
3879     getSelectionModel : function(){
3880         if(!this.selModel){
3881             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3882         }
3883         return this.selModel;
3884     },
3885     
3886     renderColumn : function(r)
3887     {
3888         var _this = this;
3889         r.cfg.render(Roo.get(r.id));
3890         
3891         if(r.cfg.cn){
3892             Roo.each(r.cfg.cn, function(c){
3893                 var child = {
3894                     id: r.id,
3895                     cfg: c
3896                 }
3897                 _this.renderColumn(child);
3898             })
3899         }
3900     }
3901    
3902 });
3903
3904  
3905
3906  /*
3907  * - LGPL
3908  *
3909  * table cell
3910  * 
3911  */
3912
3913 /**
3914  * @class Roo.bootstrap.TableCell
3915  * @extends Roo.bootstrap.Component
3916  * Bootstrap TableCell class
3917  * @cfg {String} html cell contain text
3918  * @cfg {String} cls cell class
3919  * @cfg {String} tag cell tag (td|th) default td
3920  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3921  * @cfg {String} align Aligns the content in a cell
3922  * @cfg {String} axis Categorizes cells
3923  * @cfg {String} bgcolor Specifies the background color of a cell
3924  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3925  * @cfg {Number} colspan Specifies the number of columns a cell should span
3926  * @cfg {String} headers Specifies one or more header cells a cell is related to
3927  * @cfg {Number} height Sets the height of a cell
3928  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3929  * @cfg {Number} rowspan Sets the number of rows a cell should span
3930  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3931  * @cfg {String} valign Vertical aligns the content in a cell
3932  * @cfg {Number} width Specifies the width of a cell
3933  * 
3934  * @constructor
3935  * Create a new TableCell
3936  * @param {Object} config The config object
3937  */
3938
3939 Roo.bootstrap.TableCell = function(config){
3940     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3941 };
3942
3943 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3944     
3945     html: false,
3946     cls: false,
3947     tag: false,
3948     abbr: false,
3949     align: false,
3950     axis: false,
3951     bgcolor: false,
3952     charoff: false,
3953     colspan: false,
3954     headers: false,
3955     height: false,
3956     nowrap: false,
3957     rowspan: false,
3958     scope: false,
3959     valign: false,
3960     width: false,
3961     
3962     
3963     getAutoCreate : function(){
3964         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3965         
3966         cfg = {
3967             tag: 'td'
3968         }
3969         
3970         if(this.tag){
3971             cfg.tag = this.tag;
3972         }
3973         
3974         if (this.html) {
3975             cfg.html=this.html
3976         }
3977         if (this.cls) {
3978             cfg.cls=this.cls
3979         }
3980         if (this.abbr) {
3981             cfg.abbr=this.abbr
3982         }
3983         if (this.align) {
3984             cfg.align=this.align
3985         }
3986         if (this.axis) {
3987             cfg.axis=this.axis
3988         }
3989         if (this.bgcolor) {
3990             cfg.bgcolor=this.bgcolor
3991         }
3992         if (this.charoff) {
3993             cfg.charoff=this.charoff
3994         }
3995         if (this.colspan) {
3996             cfg.colspan=this.colspan
3997         }
3998         if (this.headers) {
3999             cfg.headers=this.headers
4000         }
4001         if (this.height) {
4002             cfg.height=this.height
4003         }
4004         if (this.nowrap) {
4005             cfg.nowrap=this.nowrap
4006         }
4007         if (this.rowspan) {
4008             cfg.rowspan=this.rowspan
4009         }
4010         if (this.scope) {
4011             cfg.scope=this.scope
4012         }
4013         if (this.valign) {
4014             cfg.valign=this.valign
4015         }
4016         if (this.width) {
4017             cfg.width=this.width
4018         }
4019         
4020         
4021         return cfg;
4022     }
4023    
4024 });
4025
4026  
4027
4028  /*
4029  * - LGPL
4030  *
4031  * table row
4032  * 
4033  */
4034
4035 /**
4036  * @class Roo.bootstrap.TableRow
4037  * @extends Roo.bootstrap.Component
4038  * Bootstrap TableRow class
4039  * @cfg {String} cls row class
4040  * @cfg {String} align Aligns the content in a table row
4041  * @cfg {String} bgcolor Specifies a background color for a table row
4042  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4043  * @cfg {String} valign Vertical aligns the content in a table row
4044  * 
4045  * @constructor
4046  * Create a new TableRow
4047  * @param {Object} config The config object
4048  */
4049
4050 Roo.bootstrap.TableRow = function(config){
4051     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4052 };
4053
4054 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4055     
4056     cls: false,
4057     align: false,
4058     bgcolor: false,
4059     charoff: false,
4060     valign: false,
4061     
4062     getAutoCreate : function(){
4063         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4064         
4065         cfg = {
4066             tag: 'tr'
4067         }
4068             
4069         if(this.cls){
4070             cfg.cls = this.cls;
4071         }
4072         if(this.align){
4073             cfg.align = this.align;
4074         }
4075         if(this.bgcolor){
4076             cfg.bgcolor = this.bgcolor;
4077         }
4078         if(this.charoff){
4079             cfg.charoff = this.charoff;
4080         }
4081         if(this.valign){
4082             cfg.valign = this.valign;
4083         }
4084         
4085         return cfg;
4086     }
4087    
4088 });
4089
4090  
4091
4092  /*
4093  * - LGPL
4094  *
4095  * table body
4096  * 
4097  */
4098
4099 /**
4100  * @class Roo.bootstrap.TableBody
4101  * @extends Roo.bootstrap.Component
4102  * Bootstrap TableBody class
4103  * @cfg {String} cls element class
4104  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4105  * @cfg {String} align Aligns the content inside the element
4106  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4107  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4108  * 
4109  * @constructor
4110  * Create a new TableBody
4111  * @param {Object} config The config object
4112  */
4113
4114 Roo.bootstrap.TableBody = function(config){
4115     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4116 };
4117
4118 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4119     
4120     cls: false,
4121     tag: false,
4122     align: false,
4123     charoff: false,
4124     valign: false,
4125     
4126     getAutoCreate : function(){
4127         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4128         
4129         cfg = {
4130             tag: 'tbody'
4131         }
4132             
4133         if (this.cls) {
4134             cfg.cls=this.cls
4135         }
4136         if(this.tag){
4137             cfg.tag = this.tag;
4138         }
4139         
4140         if(this.align){
4141             cfg.align = this.align;
4142         }
4143         if(this.charoff){
4144             cfg.charoff = this.charoff;
4145         }
4146         if(this.valign){
4147             cfg.valign = this.valign;
4148         }
4149         
4150         return cfg;
4151     }
4152     
4153     
4154 //    initEvents : function()
4155 //    {
4156 //        
4157 //        if(!this.store){
4158 //            return;
4159 //        }
4160 //        
4161 //        this.store = Roo.factory(this.store, Roo.data);
4162 //        this.store.on('load', this.onLoad, this);
4163 //        
4164 //        this.store.load();
4165 //        
4166 //    },
4167 //    
4168 //    onLoad: function () 
4169 //    {   
4170 //        this.fireEvent('load', this);
4171 //    }
4172 //    
4173 //   
4174 });
4175
4176  
4177
4178  /*
4179  * Based on:
4180  * Ext JS Library 1.1.1
4181  * Copyright(c) 2006-2007, Ext JS, LLC.
4182  *
4183  * Originally Released Under LGPL - original licence link has changed is not relivant.
4184  *
4185  * Fork - LGPL
4186  * <script type="text/javascript">
4187  */
4188
4189 // as we use this in bootstrap.
4190 Roo.namespace('Roo.form');
4191  /**
4192  * @class Roo.form.Action
4193  * Internal Class used to handle form actions
4194  * @constructor
4195  * @param {Roo.form.BasicForm} el The form element or its id
4196  * @param {Object} config Configuration options
4197  */
4198
4199  
4200  
4201 // define the action interface
4202 Roo.form.Action = function(form, options){
4203     this.form = form;
4204     this.options = options || {};
4205 };
4206 /**
4207  * Client Validation Failed
4208  * @const 
4209  */
4210 Roo.form.Action.CLIENT_INVALID = 'client';
4211 /**
4212  * Server Validation Failed
4213  * @const 
4214  */
4215 Roo.form.Action.SERVER_INVALID = 'server';
4216  /**
4217  * Connect to Server Failed
4218  * @const 
4219  */
4220 Roo.form.Action.CONNECT_FAILURE = 'connect';
4221 /**
4222  * Reading Data from Server Failed
4223  * @const 
4224  */
4225 Roo.form.Action.LOAD_FAILURE = 'load';
4226
4227 Roo.form.Action.prototype = {
4228     type : 'default',
4229     failureType : undefined,
4230     response : undefined,
4231     result : undefined,
4232
4233     // interface method
4234     run : function(options){
4235
4236     },
4237
4238     // interface method
4239     success : function(response){
4240
4241     },
4242
4243     // interface method
4244     handleResponse : function(response){
4245
4246     },
4247
4248     // default connection failure
4249     failure : function(response){
4250         
4251         this.response = response;
4252         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4253         this.form.afterAction(this, false);
4254     },
4255
4256     processResponse : function(response){
4257         this.response = response;
4258         if(!response.responseText){
4259             return true;
4260         }
4261         this.result = this.handleResponse(response);
4262         return this.result;
4263     },
4264
4265     // utility functions used internally
4266     getUrl : function(appendParams){
4267         var url = this.options.url || this.form.url || this.form.el.dom.action;
4268         if(appendParams){
4269             var p = this.getParams();
4270             if(p){
4271                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4272             }
4273         }
4274         return url;
4275     },
4276
4277     getMethod : function(){
4278         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4279     },
4280
4281     getParams : function(){
4282         var bp = this.form.baseParams;
4283         var p = this.options.params;
4284         if(p){
4285             if(typeof p == "object"){
4286                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4287             }else if(typeof p == 'string' && bp){
4288                 p += '&' + Roo.urlEncode(bp);
4289             }
4290         }else if(bp){
4291             p = Roo.urlEncode(bp);
4292         }
4293         return p;
4294     },
4295
4296     createCallback : function(){
4297         return {
4298             success: this.success,
4299             failure: this.failure,
4300             scope: this,
4301             timeout: (this.form.timeout*1000),
4302             upload: this.form.fileUpload ? this.success : undefined
4303         };
4304     }
4305 };
4306
4307 Roo.form.Action.Submit = function(form, options){
4308     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4309 };
4310
4311 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4312     type : 'submit',
4313
4314     haveProgress : false,
4315     uploadComplete : false,
4316     
4317     // uploadProgress indicator.
4318     uploadProgress : function()
4319     {
4320         if (!this.form.progressUrl) {
4321             return;
4322         }
4323         
4324         if (!this.haveProgress) {
4325             Roo.MessageBox.progress("Uploading", "Uploading");
4326         }
4327         if (this.uploadComplete) {
4328            Roo.MessageBox.hide();
4329            return;
4330         }
4331         
4332         this.haveProgress = true;
4333    
4334         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4335         
4336         var c = new Roo.data.Connection();
4337         c.request({
4338             url : this.form.progressUrl,
4339             params: {
4340                 id : uid
4341             },
4342             method: 'GET',
4343             success : function(req){
4344                //console.log(data);
4345                 var rdata = false;
4346                 var edata;
4347                 try  {
4348                    rdata = Roo.decode(req.responseText)
4349                 } catch (e) {
4350                     Roo.log("Invalid data from server..");
4351                     Roo.log(edata);
4352                     return;
4353                 }
4354                 if (!rdata || !rdata.success) {
4355                     Roo.log(rdata);
4356                     Roo.MessageBox.alert(Roo.encode(rdata));
4357                     return;
4358                 }
4359                 var data = rdata.data;
4360                 
4361                 if (this.uploadComplete) {
4362                    Roo.MessageBox.hide();
4363                    return;
4364                 }
4365                    
4366                 if (data){
4367                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4368                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4369                     );
4370                 }
4371                 this.uploadProgress.defer(2000,this);
4372             },
4373        
4374             failure: function(data) {
4375                 Roo.log('progress url failed ');
4376                 Roo.log(data);
4377             },
4378             scope : this
4379         });
4380            
4381     },
4382     
4383     
4384     run : function()
4385     {
4386         // run get Values on the form, so it syncs any secondary forms.
4387         this.form.getValues();
4388         
4389         var o = this.options;
4390         var method = this.getMethod();
4391         var isPost = method == 'POST';
4392         if(o.clientValidation === false || this.form.isValid()){
4393             
4394             if (this.form.progressUrl) {
4395                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4396                     (new Date() * 1) + '' + Math.random());
4397                     
4398             } 
4399             
4400             
4401             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4402                 form:this.form.el.dom,
4403                 url:this.getUrl(!isPost),
4404                 method: method,
4405                 params:isPost ? this.getParams() : null,
4406                 isUpload: this.form.fileUpload
4407             }));
4408             
4409             this.uploadProgress();
4410
4411         }else if (o.clientValidation !== false){ // client validation failed
4412             this.failureType = Roo.form.Action.CLIENT_INVALID;
4413             this.form.afterAction(this, false);
4414         }
4415     },
4416
4417     success : function(response)
4418     {
4419         this.uploadComplete= true;
4420         if (this.haveProgress) {
4421             Roo.MessageBox.hide();
4422         }
4423         
4424         
4425         var result = this.processResponse(response);
4426         if(result === true || result.success){
4427             this.form.afterAction(this, true);
4428             return;
4429         }
4430         if(result.errors){
4431             this.form.markInvalid(result.errors);
4432             this.failureType = Roo.form.Action.SERVER_INVALID;
4433         }
4434         this.form.afterAction(this, false);
4435     },
4436     failure : function(response)
4437     {
4438         this.uploadComplete= true;
4439         if (this.haveProgress) {
4440             Roo.MessageBox.hide();
4441         }
4442         
4443         this.response = response;
4444         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4445         this.form.afterAction(this, false);
4446     },
4447     
4448     handleResponse : function(response){
4449         if(this.form.errorReader){
4450             var rs = this.form.errorReader.read(response);
4451             var errors = [];
4452             if(rs.records){
4453                 for(var i = 0, len = rs.records.length; i < len; i++) {
4454                     var r = rs.records[i];
4455                     errors[i] = r.data;
4456                 }
4457             }
4458             if(errors.length < 1){
4459                 errors = null;
4460             }
4461             return {
4462                 success : rs.success,
4463                 errors : errors
4464             };
4465         }
4466         var ret = false;
4467         try {
4468             ret = Roo.decode(response.responseText);
4469         } catch (e) {
4470             ret = {
4471                 success: false,
4472                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4473                 errors : []
4474             };
4475         }
4476         return ret;
4477         
4478     }
4479 });
4480
4481
4482 Roo.form.Action.Load = function(form, options){
4483     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4484     this.reader = this.form.reader;
4485 };
4486
4487 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4488     type : 'load',
4489
4490     run : function(){
4491         
4492         Roo.Ajax.request(Roo.apply(
4493                 this.createCallback(), {
4494                     method:this.getMethod(),
4495                     url:this.getUrl(false),
4496                     params:this.getParams()
4497         }));
4498     },
4499
4500     success : function(response){
4501         
4502         var result = this.processResponse(response);
4503         if(result === true || !result.success || !result.data){
4504             this.failureType = Roo.form.Action.LOAD_FAILURE;
4505             this.form.afterAction(this, false);
4506             return;
4507         }
4508         this.form.clearInvalid();
4509         this.form.setValues(result.data);
4510         this.form.afterAction(this, true);
4511     },
4512
4513     handleResponse : function(response){
4514         if(this.form.reader){
4515             var rs = this.form.reader.read(response);
4516             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4517             return {
4518                 success : rs.success,
4519                 data : data
4520             };
4521         }
4522         return Roo.decode(response.responseText);
4523     }
4524 });
4525
4526 Roo.form.Action.ACTION_TYPES = {
4527     'load' : Roo.form.Action.Load,
4528     'submit' : Roo.form.Action.Submit
4529 };/*
4530  * - LGPL
4531  *
4532  * form
4533  * 
4534  */
4535
4536 /**
4537  * @class Roo.bootstrap.Form
4538  * @extends Roo.bootstrap.Component
4539  * Bootstrap Form class
4540  * @cfg {String} method  GET | POST (default POST)
4541  * @cfg {String} labelAlign top | left (default top)
4542   * @cfg {String} align left  | right - for navbars
4543
4544  * 
4545  * @constructor
4546  * Create a new Form
4547  * @param {Object} config The config object
4548  */
4549
4550
4551 Roo.bootstrap.Form = function(config){
4552     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4553     this.addEvents({
4554         /**
4555          * @event clientvalidation
4556          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4557          * @param {Form} this
4558          * @param {Boolean} valid true if the form has passed client-side validation
4559          */
4560         clientvalidation: true,
4561         /**
4562          * @event beforeaction
4563          * Fires before any action is performed. Return false to cancel the action.
4564          * @param {Form} this
4565          * @param {Action} action The action to be performed
4566          */
4567         beforeaction: true,
4568         /**
4569          * @event actionfailed
4570          * Fires when an action fails.
4571          * @param {Form} this
4572          * @param {Action} action The action that failed
4573          */
4574         actionfailed : true,
4575         /**
4576          * @event actioncomplete
4577          * Fires when an action is completed.
4578          * @param {Form} this
4579          * @param {Action} action The action that completed
4580          */
4581         actioncomplete : true
4582     });
4583     
4584 };
4585
4586 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4587       
4588      /**
4589      * @cfg {String} method
4590      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4591      */
4592     method : 'POST',
4593     /**
4594      * @cfg {String} url
4595      * The URL to use for form actions if one isn't supplied in the action options.
4596      */
4597     /**
4598      * @cfg {Boolean} fileUpload
4599      * Set to true if this form is a file upload.
4600      */
4601      
4602     /**
4603      * @cfg {Object} baseParams
4604      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4605      */
4606       
4607     /**
4608      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4609      */
4610     timeout: 30,
4611     /**
4612      * @cfg {Sting} align (left|right) for navbar forms
4613      */
4614     align : 'left',
4615
4616     // private
4617     activeAction : null,
4618  
4619     /**
4620      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4621      * element by passing it or its id or mask the form itself by passing in true.
4622      * @type Mixed
4623      */
4624     waitMsgTarget : false,
4625     
4626      
4627     
4628     /**
4629      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4630      * element by passing it or its id or mask the form itself by passing in true.
4631      * @type Mixed
4632      */
4633     
4634     getAutoCreate : function(){
4635         
4636         var cfg = {
4637             tag: 'form',
4638             method : this.method || 'POST',
4639             id : this.id || Roo.id(),
4640             cls : ''
4641         }
4642         if (this.parent().xtype.match(/^Nav/)) {
4643             cfg.cls = 'navbar-form navbar-' + this.align;
4644             
4645         }
4646         
4647         if (this.labelAlign == 'left' ) {
4648             cfg.cls += ' form-horizontal';
4649         }
4650         
4651         
4652         return cfg;
4653     },
4654     initEvents : function()
4655     {
4656         this.el.on('submit', this.onSubmit, this);
4657         
4658         
4659     },
4660     // private
4661     onSubmit : function(e){
4662         e.stopEvent();
4663     },
4664     
4665      /**
4666      * Returns true if client-side validation on the form is successful.
4667      * @return Boolean
4668      */
4669     isValid : function(){
4670         var items = this.getItems();
4671         var valid = true;
4672         items.each(function(f){
4673            if(!f.validate()){
4674                valid = false;
4675                
4676            }
4677         });
4678         return valid;
4679     },
4680     /**
4681      * Returns true if any fields in this form have changed since their original load.
4682      * @return Boolean
4683      */
4684     isDirty : function(){
4685         var dirty = false;
4686         var items = this.getItems();
4687         items.each(function(f){
4688            if(f.isDirty()){
4689                dirty = true;
4690                return false;
4691            }
4692            return true;
4693         });
4694         return dirty;
4695     },
4696      /**
4697      * Performs a predefined action (submit or load) or custom actions you define on this form.
4698      * @param {String} actionName The name of the action type
4699      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
4700      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4701      * accept other config options):
4702      * <pre>
4703 Property          Type             Description
4704 ----------------  ---------------  ----------------------------------------------------------------------------------
4705 url               String           The url for the action (defaults to the form's url)
4706 method            String           The form method to use (defaults to the form's method, or POST if not defined)
4707 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
4708 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
4709                                    validate the form on the client (defaults to false)
4710      * </pre>
4711      * @return {BasicForm} this
4712      */
4713     doAction : function(action, options){
4714         if(typeof action == 'string'){
4715             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4716         }
4717         if(this.fireEvent('beforeaction', this, action) !== false){
4718             this.beforeAction(action);
4719             action.run.defer(100, action);
4720         }
4721         return this;
4722     },
4723     
4724     // private
4725     beforeAction : function(action){
4726         var o = action.options;
4727         
4728         // not really supported yet.. ??
4729         
4730         //if(this.waitMsgTarget === true){
4731             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4732         //}else if(this.waitMsgTarget){
4733         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4734         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4735         //}else {
4736         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4737        // }
4738          
4739     },
4740
4741     // private
4742     afterAction : function(action, success){
4743         this.activeAction = null;
4744         var o = action.options;
4745         
4746         //if(this.waitMsgTarget === true){
4747             this.el.unmask();
4748         //}else if(this.waitMsgTarget){
4749         //    this.waitMsgTarget.unmask();
4750         //}else{
4751         //    Roo.MessageBox.updateProgress(1);
4752         //    Roo.MessageBox.hide();
4753        // }
4754         // 
4755         if(success){
4756             if(o.reset){
4757                 this.reset();
4758             }
4759             Roo.callback(o.success, o.scope, [this, action]);
4760             this.fireEvent('actioncomplete', this, action);
4761             
4762         }else{
4763             
4764             // failure condition..
4765             // we have a scenario where updates need confirming.
4766             // eg. if a locking scenario exists..
4767             // we look for { errors : { needs_confirm : true }} in the response.
4768             if (
4769                 (typeof(action.result) != 'undefined')  &&
4770                 (typeof(action.result.errors) != 'undefined')  &&
4771                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4772            ){
4773                 var _t = this;
4774                 Roo.log("not supported yet");
4775                  /*
4776                 
4777                 Roo.MessageBox.confirm(
4778                     "Change requires confirmation",
4779                     action.result.errorMsg,
4780                     function(r) {
4781                         if (r != 'yes') {
4782                             return;
4783                         }
4784                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4785                     }
4786                     
4787                 );
4788                 */
4789                 
4790                 
4791                 return;
4792             }
4793             
4794             Roo.callback(o.failure, o.scope, [this, action]);
4795             // show an error message if no failed handler is set..
4796             if (!this.hasListener('actionfailed')) {
4797                 Roo.log("need to add dialog support");
4798                 /*
4799                 Roo.MessageBox.alert("Error",
4800                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4801                         action.result.errorMsg :
4802                         "Saving Failed, please check your entries or try again"
4803                 );
4804                 */
4805             }
4806             
4807             this.fireEvent('actionfailed', this, action);
4808         }
4809         
4810     },
4811     /**
4812      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4813      * @param {String} id The value to search for
4814      * @return Field
4815      */
4816     findField : function(id){
4817         var items = this.getItems();
4818         var field = items.get(id);
4819         if(!field){
4820              items.each(function(f){
4821                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4822                     field = f;
4823                     return false;
4824                 }
4825                 return true;
4826             });
4827         }
4828         return field || null;
4829     },
4830      /**
4831      * Mark fields in this form invalid in bulk.
4832      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4833      * @return {BasicForm} this
4834      */
4835     markInvalid : function(errors){
4836         if(errors instanceof Array){
4837             for(var i = 0, len = errors.length; i < len; i++){
4838                 var fieldError = errors[i];
4839                 var f = this.findField(fieldError.id);
4840                 if(f){
4841                     f.markInvalid(fieldError.msg);
4842                 }
4843             }
4844         }else{
4845             var field, id;
4846             for(id in errors){
4847                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4848                     field.markInvalid(errors[id]);
4849                 }
4850             }
4851         }
4852         //Roo.each(this.childForms || [], function (f) {
4853         //    f.markInvalid(errors);
4854         //});
4855         
4856         return this;
4857     },
4858
4859     /**
4860      * Set values for fields in this form in bulk.
4861      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4862      * @return {BasicForm} this
4863      */
4864     setValues : function(values){
4865         if(values instanceof Array){ // array of objects
4866             for(var i = 0, len = values.length; i < len; i++){
4867                 var v = values[i];
4868                 var f = this.findField(v.id);
4869                 if(f){
4870                     f.setValue(v.value);
4871                     if(this.trackResetOnLoad){
4872                         f.originalValue = f.getValue();
4873                     }
4874                 }
4875             }
4876         }else{ // object hash
4877             var field, id;
4878             for(id in values){
4879                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4880                     
4881                     if (field.setFromData && 
4882                         field.valueField && 
4883                         field.displayField &&
4884                         // combos' with local stores can 
4885                         // be queried via setValue()
4886                         // to set their value..
4887                         (field.store && !field.store.isLocal)
4888                         ) {
4889                         // it's a combo
4890                         var sd = { };
4891                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4892                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4893                         field.setFromData(sd);
4894                         
4895                     } else {
4896                         field.setValue(values[id]);
4897                     }
4898                     
4899                     
4900                     if(this.trackResetOnLoad){
4901                         field.originalValue = field.getValue();
4902                     }
4903                 }
4904             }
4905         }
4906          
4907         //Roo.each(this.childForms || [], function (f) {
4908         //    f.setValues(values);
4909         //});
4910                 
4911         return this;
4912     },
4913
4914     /**
4915      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4916      * they are returned as an array.
4917      * @param {Boolean} asString
4918      * @return {Object}
4919      */
4920     getValues : function(asString){
4921         //if (this.childForms) {
4922             // copy values from the child forms
4923         //    Roo.each(this.childForms, function (f) {
4924         //        this.setValues(f.getValues());
4925         //    }, this);
4926         //}
4927         
4928         
4929         
4930         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4931         if(asString === true){
4932             return fs;
4933         }
4934         return Roo.urlDecode(fs);
4935     },
4936     
4937     /**
4938      * Returns the fields in this form as an object with key/value pairs. 
4939      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4940      * @return {Object}
4941      */
4942     getFieldValues : function(with_hidden)
4943     {
4944         var items = this.getItems();
4945         var ret = {};
4946         items.each(function(f){
4947             if (!f.getName()) {
4948                 return;
4949             }
4950             var v = f.getValue();
4951             if (f.inputType =='radio') {
4952                 if (typeof(ret[f.getName()]) == 'undefined') {
4953                     ret[f.getName()] = ''; // empty..
4954                 }
4955                 
4956                 if (!f.el.dom.checked) {
4957                     return;
4958                     
4959                 }
4960                 v = f.el.dom.value;
4961                 
4962             }
4963             
4964             // not sure if this supported any more..
4965             if ((typeof(v) == 'object') && f.getRawValue) {
4966                 v = f.getRawValue() ; // dates..
4967             }
4968             // combo boxes where name != hiddenName...
4969             if (f.name != f.getName()) {
4970                 ret[f.name] = f.getRawValue();
4971             }
4972             ret[f.getName()] = v;
4973         });
4974         
4975         return ret;
4976     },
4977
4978     /**
4979      * Clears all invalid messages in this form.
4980      * @return {BasicForm} this
4981      */
4982     clearInvalid : function(){
4983         var items = this.getItems();
4984         
4985         items.each(function(f){
4986            f.clearInvalid();
4987         });
4988         
4989         
4990         
4991         return this;
4992     },
4993
4994     /**
4995      * Resets this form.
4996      * @return {BasicForm} this
4997      */
4998     reset : function(){
4999         var items = this.getItems();
5000         items.each(function(f){
5001             f.reset();
5002         });
5003         
5004         Roo.each(this.childForms || [], function (f) {
5005             f.reset();
5006         });
5007        
5008         
5009         return this;
5010     },
5011     getItems : function()
5012     {
5013         var r=new Roo.util.MixedCollection(false, function(o){
5014             return o.id || (o.id = Roo.id());
5015         });
5016         var iter = function(el) {
5017             if (el.inputEl) {
5018                 r.add(el);
5019             }
5020             if (!el.items) {
5021                 return;
5022             }
5023             Roo.each(el.items,function(e) {
5024                 iter(e);
5025             });
5026             
5027             
5028         };
5029         iter(this);
5030         return r;
5031         
5032         
5033         
5034         
5035     }
5036     
5037 });
5038
5039  
5040 /*
5041  * Based on:
5042  * Ext JS Library 1.1.1
5043  * Copyright(c) 2006-2007, Ext JS, LLC.
5044  *
5045  * Originally Released Under LGPL - original licence link has changed is not relivant.
5046  *
5047  * Fork - LGPL
5048  * <script type="text/javascript">
5049  */
5050 /**
5051  * @class Roo.form.VTypes
5052  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5053  * @singleton
5054  */
5055 Roo.form.VTypes = function(){
5056     // closure these in so they are only created once.
5057     var alpha = /^[a-zA-Z_]+$/;
5058     var alphanum = /^[a-zA-Z0-9_]+$/;
5059     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5060     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5061
5062     // All these messages and functions are configurable
5063     return {
5064         /**
5065          * The function used to validate email addresses
5066          * @param {String} value The email address
5067          */
5068         'email' : function(v){
5069             return email.test(v);
5070         },
5071         /**
5072          * The error text to display when the email validation function returns false
5073          * @type String
5074          */
5075         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5076         /**
5077          * The keystroke filter mask to be applied on email input
5078          * @type RegExp
5079          */
5080         'emailMask' : /[a-z0-9_\.\-@]/i,
5081
5082         /**
5083          * The function used to validate URLs
5084          * @param {String} value The URL
5085          */
5086         'url' : function(v){
5087             return url.test(v);
5088         },
5089         /**
5090          * The error text to display when the url validation function returns false
5091          * @type String
5092          */
5093         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5094         
5095         /**
5096          * The function used to validate alpha values
5097          * @param {String} value The value
5098          */
5099         'alpha' : function(v){
5100             return alpha.test(v);
5101         },
5102         /**
5103          * The error text to display when the alpha validation function returns false
5104          * @type String
5105          */
5106         'alphaText' : 'This field should only contain letters and _',
5107         /**
5108          * The keystroke filter mask to be applied on alpha input
5109          * @type RegExp
5110          */
5111         'alphaMask' : /[a-z_]/i,
5112
5113         /**
5114          * The function used to validate alphanumeric values
5115          * @param {String} value The value
5116          */
5117         'alphanum' : function(v){
5118             return alphanum.test(v);
5119         },
5120         /**
5121          * The error text to display when the alphanumeric validation function returns false
5122          * @type String
5123          */
5124         'alphanumText' : 'This field should only contain letters, numbers and _',
5125         /**
5126          * The keystroke filter mask to be applied on alphanumeric input
5127          * @type RegExp
5128          */
5129         'alphanumMask' : /[a-z0-9_]/i
5130     };
5131 }();/*
5132  * - LGPL
5133  *
5134  * Input
5135  * 
5136  */
5137
5138 /**
5139  * @class Roo.bootstrap.Input
5140  * @extends Roo.bootstrap.Component
5141  * Bootstrap Input class
5142  * @cfg {Boolean} disabled is it disabled
5143  * @cfg {String} fieldLabel - the label associated
5144  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5145  * @cfg {String} name name of the input
5146  * @cfg {string} fieldLabel - the label associated
5147  * @cfg {string}  inputType - input / file submit ...
5148  * @cfg {string} placeholder - placeholder to put in text.
5149  * @cfg {string}  before - input group add on before
5150  * @cfg {string} after - input group add on after
5151  * @cfg {string} size - (lg|sm) or leave empty..
5152  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5153  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5154  * @cfg {Number} md colspan out of 12 for computer-sized screens
5155  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5156  * @cfg {string} value default value of the input
5157  * @cfg {Number} labelWidth set the width of label (0-12)
5158  * @cfg {String} labelAlign (top|left)
5159  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5160  * 
5161  * 
5162  * @constructor
5163  * Create a new Input
5164  * @param {Object} config The config object
5165  */
5166
5167 Roo.bootstrap.Input = function(config){
5168     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5169    
5170         this.addEvents({
5171             /**
5172              * @event focus
5173              * Fires when this field receives input focus.
5174              * @param {Roo.form.Field} this
5175              */
5176             focus : true,
5177             /**
5178              * @event blur
5179              * Fires when this field loses input focus.
5180              * @param {Roo.form.Field} this
5181              */
5182             blur : true,
5183             /**
5184              * @event specialkey
5185              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5186              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5187              * @param {Roo.form.Field} this
5188              * @param {Roo.EventObject} e The event object
5189              */
5190             specialkey : true,
5191             /**
5192              * @event change
5193              * Fires just before the field blurs if the field value has changed.
5194              * @param {Roo.form.Field} this
5195              * @param {Mixed} newValue The new value
5196              * @param {Mixed} oldValue The original value
5197              */
5198             change : true,
5199             /**
5200              * @event invalid
5201              * Fires after the field has been marked as invalid.
5202              * @param {Roo.form.Field} this
5203              * @param {String} msg The validation message
5204              */
5205             invalid : true,
5206             /**
5207              * @event valid
5208              * Fires after the field has been validated with no errors.
5209              * @param {Roo.form.Field} this
5210              */
5211             valid : true,
5212              /**
5213              * @event keyup
5214              * Fires after the key up
5215              * @param {Roo.form.Field} this
5216              * @param {Roo.EventObject}  e The event Object
5217              */
5218             keyup : true
5219         });
5220 };
5221
5222 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5223      /**
5224      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5225       automatic validation (defaults to "keyup").
5226      */
5227     validationEvent : "keyup",
5228      /**
5229      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5230      */
5231     validateOnBlur : true,
5232     /**
5233      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5234      */
5235     validationDelay : 250,
5236      /**
5237      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5238      */
5239     focusClass : "x-form-focus",  // not needed???
5240     
5241        
5242     /**
5243      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5244      */
5245     invalidClass : "has-error",
5246     
5247     /**
5248      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5249      */
5250     selectOnFocus : false,
5251     
5252      /**
5253      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5254      */
5255     maskRe : null,
5256        /**
5257      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5258      */
5259     vtype : null,
5260     
5261       /**
5262      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5263      */
5264     disableKeyFilter : false,
5265     
5266        /**
5267      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5268      */
5269     disabled : false,
5270      /**
5271      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5272      */
5273     allowBlank : true,
5274     /**
5275      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5276      */
5277     blankText : "This field is required",
5278     
5279      /**
5280      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5281      */
5282     minLength : 0,
5283     /**
5284      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5285      */
5286     maxLength : Number.MAX_VALUE,
5287     /**
5288      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5289      */
5290     minLengthText : "The minimum length for this field is {0}",
5291     /**
5292      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5293      */
5294     maxLengthText : "The maximum length for this field is {0}",
5295   
5296     
5297     /**
5298      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5299      * If available, this function will be called only after the basic validators all return true, and will be passed the
5300      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5301      */
5302     validator : null,
5303     /**
5304      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5305      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5306      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5307      */
5308     regex : null,
5309     /**
5310      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5311      */
5312     regexText : "",
5313     
5314     
5315     
5316     fieldLabel : '',
5317     inputType : 'text',
5318     
5319     name : false,
5320     placeholder: false,
5321     before : false,
5322     after : false,
5323     size : false,
5324     // private
5325     hasFocus : false,
5326     preventMark: false,
5327     isFormField : true,
5328     value : '',
5329     labelWidth : 2,
5330     labelAlign : false,
5331     readOnly : false,
5332     
5333     parentLabelAlign : function()
5334     {
5335         var parent = this;
5336         while (parent.parent()) {
5337             parent = parent.parent();
5338             if (typeof(parent.labelAlign) !='undefined') {
5339                 return parent.labelAlign;
5340             }
5341         }
5342         return 'left';
5343         
5344     },
5345     
5346     getAutoCreate : function(){
5347         
5348         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5349         
5350         var id = Roo.id();
5351         
5352         var cfg = {};
5353         
5354         if(this.inputType != 'hidden'){
5355             cfg.cls = 'form-group' //input-group
5356         }
5357         
5358         var input =  {
5359             tag: 'input',
5360             id : id,
5361             type : this.inputType,
5362             value : this.value,
5363             cls : 'form-control',
5364             placeholder : this.placeholder || ''
5365             
5366         };
5367         
5368         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5369             input.maxLength = this.maxLength;
5370         }
5371         
5372         if (this.disabled) {
5373             input.disabled=true;
5374         }
5375         
5376         if (this.readOnly) {
5377             input.readonly=true;
5378         }
5379         
5380         if (this.name) {
5381             input.name = this.name;
5382         }
5383         if (this.size) {
5384             input.cls += ' input-' + this.size;
5385         }
5386         var settings=this;
5387         ['xs','sm','md','lg'].map(function(size){
5388             if (settings[size]) {
5389                 cfg.cls += ' col-' + size + '-' + settings[size];
5390             }
5391         });
5392         
5393         var inputblock = input;
5394         
5395         if (this.before || this.after) {
5396             
5397             inputblock = {
5398                 cls : 'input-group',
5399                 cn :  [] 
5400             };
5401             if (this.before) {
5402                 inputblock.cn.push({
5403                     tag :'span',
5404                     cls : 'input-group-addon',
5405                     html : this.before
5406                 });
5407             }
5408             inputblock.cn.push(input);
5409             if (this.after) {
5410                 inputblock.cn.push({
5411                     tag :'span',
5412                     cls : 'input-group-addon',
5413                     html : this.after
5414                 });
5415             }
5416             
5417         };
5418         
5419         if (align ==='left' && this.fieldLabel.length) {
5420                 Roo.log("left and has label");
5421                 cfg.cn = [
5422                     
5423                     {
5424                         tag: 'label',
5425                         'for' :  id,
5426                         cls : 'control-label col-sm-' + this.labelWidth,
5427                         html : this.fieldLabel
5428                         
5429                     },
5430                     {
5431                         cls : "col-sm-" + (12 - this.labelWidth), 
5432                         cn: [
5433                             inputblock
5434                         ]
5435                     }
5436                     
5437                 ];
5438         } else if ( this.fieldLabel.length) {
5439                 Roo.log(" label");
5440                  cfg.cn = [
5441                    
5442                     {
5443                         tag: 'label',
5444                         //cls : 'input-group-addon',
5445                         html : this.fieldLabel
5446                         
5447                     },
5448                     
5449                     inputblock
5450                     
5451                 ];
5452
5453         } else {
5454             
5455                 Roo.log(" no label && no align");
5456                 cfg.cn = [
5457                     
5458                         inputblock
5459                     
5460                 ];
5461                 
5462                 
5463         };
5464         Roo.log('input-parentType: ' + this.parentType);
5465         
5466         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5467            cfg.cls += ' navbar-form';
5468            Roo.log(cfg);
5469         }
5470         
5471         return cfg;
5472         
5473     },
5474     /**
5475      * return the real input element.
5476      */
5477     inputEl: function ()
5478     {
5479         return this.el.select('input.form-control',true).first();
5480     },
5481     setDisabled : function(v)
5482     {
5483         var i  = this.inputEl().dom;
5484         if (!v) {
5485             i.removeAttribute('disabled');
5486             return;
5487             
5488         }
5489         i.setAttribute('disabled','true');
5490     },
5491     initEvents : function()
5492     {
5493         
5494         this.inputEl().on("keydown" , this.fireKey,  this);
5495         this.inputEl().on("focus", this.onFocus,  this);
5496         this.inputEl().on("blur", this.onBlur,  this);
5497         
5498         this.inputEl().relayEvent('keyup', this);
5499
5500         // reference to original value for reset
5501         this.originalValue = this.getValue();
5502         //Roo.form.TextField.superclass.initEvents.call(this);
5503         if(this.validationEvent == 'keyup'){
5504             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5505             this.inputEl().on('keyup', this.filterValidation, this);
5506         }
5507         else if(this.validationEvent !== false){
5508             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5509         }
5510         
5511         if(this.selectOnFocus){
5512             this.on("focus", this.preFocus, this);
5513             
5514         }
5515         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5516             this.inputEl().on("keypress", this.filterKeys, this);
5517         }
5518        /* if(this.grow){
5519             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5520             this.el.on("click", this.autoSize,  this);
5521         }
5522         */
5523         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5524             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5525         }
5526         
5527     },
5528     filterValidation : function(e){
5529         if(!e.isNavKeyPress()){
5530             this.validationTask.delay(this.validationDelay);
5531         }
5532     },
5533      /**
5534      * Validates the field value
5535      * @return {Boolean} True if the value is valid, else false
5536      */
5537     validate : function(){
5538         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5539         if(this.disabled || this.validateValue(this.getRawValue())){
5540             this.clearInvalid();
5541             return true;
5542         }
5543         return false;
5544     },
5545     
5546     
5547     /**
5548      * Validates a value according to the field's validation rules and marks the field as invalid
5549      * if the validation fails
5550      * @param {Mixed} value The value to validate
5551      * @return {Boolean} True if the value is valid, else false
5552      */
5553     validateValue : function(value){
5554         if(value.length < 1)  { // if it's blank
5555              if(this.allowBlank){
5556                 this.clearInvalid();
5557                 return true;
5558              }else{
5559                 this.markInvalid(this.blankText);
5560                 return false;
5561              }
5562         }
5563         if(value.length < this.minLength){
5564             this.markInvalid(String.format(this.minLengthText, this.minLength));
5565             return false;
5566         }
5567         if(value.length > this.maxLength){
5568             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5569             return false;
5570         }
5571         if(this.vtype){
5572             var vt = Roo.form.VTypes;
5573             if(!vt[this.vtype](value, this)){
5574                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5575                 return false;
5576             }
5577         }
5578         if(typeof this.validator == "function"){
5579             var msg = this.validator(value);
5580             if(msg !== true){
5581                 this.markInvalid(msg);
5582                 return false;
5583             }
5584         }
5585         if(this.regex && !this.regex.test(value)){
5586             this.markInvalid(this.regexText);
5587             return false;
5588         }
5589         return true;
5590     },
5591
5592     
5593     
5594      // private
5595     fireKey : function(e){
5596         //Roo.log('field ' + e.getKey());
5597         if(e.isNavKeyPress()){
5598             this.fireEvent("specialkey", this, e);
5599         }
5600     },
5601     focus : function (selectText){
5602         if(this.rendered){
5603             this.inputEl().focus();
5604             if(selectText === true){
5605                 this.inputEl().dom.select();
5606             }
5607         }
5608         return this;
5609     } ,
5610     
5611     onFocus : function(){
5612         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5613            // this.el.addClass(this.focusClass);
5614         }
5615         if(!this.hasFocus){
5616             this.hasFocus = true;
5617             this.startValue = this.getValue();
5618             this.fireEvent("focus", this);
5619         }
5620     },
5621     
5622     beforeBlur : Roo.emptyFn,
5623
5624     
5625     // private
5626     onBlur : function(){
5627         this.beforeBlur();
5628         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5629             //this.el.removeClass(this.focusClass);
5630         }
5631         this.hasFocus = false;
5632         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5633             this.validate();
5634         }
5635         var v = this.getValue();
5636         if(String(v) !== String(this.startValue)){
5637             this.fireEvent('change', this, v, this.startValue);
5638         }
5639         this.fireEvent("blur", this);
5640     },
5641     
5642     /**
5643      * Resets the current field value to the originally loaded value and clears any validation messages
5644      */
5645     reset : function(){
5646         this.setValue(this.originalValue);
5647         this.clearInvalid();
5648     },
5649      /**
5650      * Returns the name of the field
5651      * @return {Mixed} name The name field
5652      */
5653     getName: function(){
5654         return this.name;
5655     },
5656      /**
5657      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
5658      * @return {Mixed} value The field value
5659      */
5660     getValue : function(){
5661         return this.inputEl().getValue();
5662     },
5663     /**
5664      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
5665      * @return {Mixed} value The field value
5666      */
5667     getRawValue : function(){
5668         var v = this.inputEl().getValue();
5669         
5670         return v;
5671     },
5672     
5673     /**
5674      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
5675      * @param {Mixed} value The value to set
5676      */
5677     setRawValue : function(v){
5678         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5679     },
5680     
5681     selectText : function(start, end){
5682         var v = this.getRawValue();
5683         if(v.length > 0){
5684             start = start === undefined ? 0 : start;
5685             end = end === undefined ? v.length : end;
5686             var d = this.inputEl().dom;
5687             if(d.setSelectionRange){
5688                 d.setSelectionRange(start, end);
5689             }else if(d.createTextRange){
5690                 var range = d.createTextRange();
5691                 range.moveStart("character", start);
5692                 range.moveEnd("character", v.length-end);
5693                 range.select();
5694             }
5695         }
5696     },
5697     
5698     /**
5699      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
5700      * @param {Mixed} value The value to set
5701      */
5702     setValue : function(v){
5703         this.value = v;
5704         if(this.rendered){
5705             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5706             this.validate();
5707         }
5708     },
5709     
5710     /*
5711     processValue : function(value){
5712         if(this.stripCharsRe){
5713             var newValue = value.replace(this.stripCharsRe, '');
5714             if(newValue !== value){
5715                 this.setRawValue(newValue);
5716                 return newValue;
5717             }
5718         }
5719         return value;
5720     },
5721   */
5722     preFocus : function(){
5723         
5724         if(this.selectOnFocus){
5725             this.inputEl().dom.select();
5726         }
5727     },
5728     filterKeys : function(e){
5729         var k = e.getKey();
5730         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5731             return;
5732         }
5733         var c = e.getCharCode(), cc = String.fromCharCode(c);
5734         if(Roo.isIE && (e.isSpecialKey() || !cc)){
5735             return;
5736         }
5737         if(!this.maskRe.test(cc)){
5738             e.stopEvent();
5739         }
5740     },
5741      /**
5742      * Clear any invalid styles/messages for this field
5743      */
5744     clearInvalid : function(){
5745         
5746         if(!this.el || this.preventMark){ // not rendered
5747             return;
5748         }
5749         this.el.removeClass(this.invalidClass);
5750         /*
5751         switch(this.msgTarget){
5752             case 'qtip':
5753                 this.el.dom.qtip = '';
5754                 break;
5755             case 'title':
5756                 this.el.dom.title = '';
5757                 break;
5758             case 'under':
5759                 if(this.errorEl){
5760                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5761                 }
5762                 break;
5763             case 'side':
5764                 if(this.errorIcon){
5765                     this.errorIcon.dom.qtip = '';
5766                     this.errorIcon.hide();
5767                     this.un('resize', this.alignErrorIcon, this);
5768                 }
5769                 break;
5770             default:
5771                 var t = Roo.getDom(this.msgTarget);
5772                 t.innerHTML = '';
5773                 t.style.display = 'none';
5774                 break;
5775         }
5776         */
5777         this.fireEvent('valid', this);
5778     },
5779      /**
5780      * Mark this field as invalid
5781      * @param {String} msg The validation message
5782      */
5783     markInvalid : function(msg){
5784         if(!this.el  || this.preventMark){ // not rendered
5785             return;
5786         }
5787         this.el.addClass(this.invalidClass);
5788         /*
5789         msg = msg || this.invalidText;
5790         switch(this.msgTarget){
5791             case 'qtip':
5792                 this.el.dom.qtip = msg;
5793                 this.el.dom.qclass = 'x-form-invalid-tip';
5794                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5795                     Roo.QuickTips.enable();
5796                 }
5797                 break;
5798             case 'title':
5799                 this.el.dom.title = msg;
5800                 break;
5801             case 'under':
5802                 if(!this.errorEl){
5803                     var elp = this.el.findParent('.x-form-element', 5, true);
5804                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5805                     this.errorEl.setWidth(elp.getWidth(true)-20);
5806                 }
5807                 this.errorEl.update(msg);
5808                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5809                 break;
5810             case 'side':
5811                 if(!this.errorIcon){
5812                     var elp = this.el.findParent('.x-form-element', 5, true);
5813                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5814                 }
5815                 this.alignErrorIcon();
5816                 this.errorIcon.dom.qtip = msg;
5817                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5818                 this.errorIcon.show();
5819                 this.on('resize', this.alignErrorIcon, this);
5820                 break;
5821             default:
5822                 var t = Roo.getDom(this.msgTarget);
5823                 t.innerHTML = msg;
5824                 t.style.display = this.msgDisplay;
5825                 break;
5826         }
5827         */
5828         this.fireEvent('invalid', this, msg);
5829     },
5830     // private
5831     SafariOnKeyDown : function(event)
5832     {
5833         // this is a workaround for a password hang bug on chrome/ webkit.
5834         
5835         var isSelectAll = false;
5836         
5837         if(this.inputEl().dom.selectionEnd > 0){
5838             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5839         }
5840         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5841             event.preventDefault();
5842             this.setValue('');
5843             return;
5844         }
5845         
5846         if(isSelectAll){ // backspace and delete key
5847             
5848             event.preventDefault();
5849             // this is very hacky as keydown always get's upper case.
5850             //
5851             var cc = String.fromCharCode(event.getCharCode());
5852             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5853             
5854         }
5855     },
5856     adjustWidth : function(tag, w){
5857         tag = tag.toLowerCase();
5858         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5859             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5860                 if(tag == 'input'){
5861                     return w + 2;
5862                 }
5863                 if(tag == 'textarea'){
5864                     return w-2;
5865                 }
5866             }else if(Roo.isOpera){
5867                 if(tag == 'input'){
5868                     return w + 2;
5869                 }
5870                 if(tag == 'textarea'){
5871                     return w-2;
5872                 }
5873             }
5874         }
5875         return w;
5876     }
5877     
5878 });
5879
5880  
5881 /*
5882  * - LGPL
5883  *
5884  * Input
5885  * 
5886  */
5887
5888 /**
5889  * @class Roo.bootstrap.TextArea
5890  * @extends Roo.bootstrap.Input
5891  * Bootstrap TextArea class
5892  * @cfg {Number} cols Specifies the visible width of a text area
5893  * @cfg {Number} rows Specifies the visible number of lines in a text area
5894  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5895  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5896  * @cfg {string} html text
5897  * 
5898  * @constructor
5899  * Create a new TextArea
5900  * @param {Object} config The config object
5901  */
5902
5903 Roo.bootstrap.TextArea = function(config){
5904     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5905    
5906 };
5907
5908 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5909      
5910     cols : false,
5911     rows : 5,
5912     readOnly : false,
5913     warp : 'soft',
5914     resize : false,
5915     value: false,
5916     html: false,
5917     
5918     getAutoCreate : function(){
5919         
5920         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5921         
5922         var id = Roo.id();
5923         
5924         var cfg = {};
5925         
5926         var input =  {
5927             tag: 'textarea',
5928             id : id,
5929             warp : this.warp,
5930             rows : this.rows,
5931             value : this.value || '',
5932             html: this.html || '',
5933             cls : 'form-control',
5934             placeholder : this.placeholder || '' 
5935             
5936         };
5937         
5938         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5939             input.maxLength = this.maxLength;
5940         }
5941         
5942         if(this.resize){
5943             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5944         }
5945         
5946         if(this.cols){
5947             input.cols = this.cols;
5948         }
5949         
5950         if (this.readOnly) {
5951             input.readonly = true;
5952         }
5953         
5954         if (this.name) {
5955             input.name = this.name;
5956         }
5957         
5958         if (this.size) {
5959             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5960         }
5961         
5962         var settings=this;
5963         ['xs','sm','md','lg'].map(function(size){
5964             if (settings[size]) {
5965                 cfg.cls += ' col-' + size + '-' + settings[size];
5966             }
5967         });
5968         
5969         var inputblock = input;
5970         
5971         if (this.before || this.after) {
5972             
5973             inputblock = {
5974                 cls : 'input-group',
5975                 cn :  [] 
5976             };
5977             if (this.before) {
5978                 inputblock.cn.push({
5979                     tag :'span',
5980                     cls : 'input-group-addon',
5981                     html : this.before
5982                 });
5983             }
5984             inputblock.cn.push(input);
5985             if (this.after) {
5986                 inputblock.cn.push({
5987                     tag :'span',
5988                     cls : 'input-group-addon',
5989                     html : this.after
5990                 });
5991             }
5992             
5993         }
5994         
5995         if (align ==='left' && this.fieldLabel.length) {
5996                 Roo.log("left and has label");
5997                 cfg.cn = [
5998                     
5999                     {
6000                         tag: 'label',
6001                         'for' :  id,
6002                         cls : 'control-label col-sm-' + this.labelWidth,
6003                         html : this.fieldLabel
6004                         
6005                     },
6006                     {
6007                         cls : "col-sm-" + (12 - this.labelWidth), 
6008                         cn: [
6009                             inputblock
6010                         ]
6011                     }
6012                     
6013                 ];
6014         } else if ( this.fieldLabel.length) {
6015                 Roo.log(" label");
6016                  cfg.cn = [
6017                    
6018                     {
6019                         tag: 'label',
6020                         //cls : 'input-group-addon',
6021                         html : this.fieldLabel
6022                         
6023                     },
6024                     
6025                     inputblock
6026                     
6027                 ];
6028
6029         } else {
6030             
6031                    Roo.log(" no label && no align");
6032                 cfg.cn = [
6033                     
6034                         inputblock
6035                     
6036                 ];
6037                 
6038                 
6039         }
6040         
6041         if (this.disabled) {
6042             input.disabled=true;
6043         }
6044         
6045         return cfg;
6046         
6047     },
6048     /**
6049      * return the real textarea element.
6050      */
6051     inputEl: function ()
6052     {
6053         return this.el.select('textarea.form-control',true).first();
6054     }
6055 });
6056
6057  
6058 /*
6059  * - LGPL
6060  *
6061  * trigger field - base class for combo..
6062  * 
6063  */
6064  
6065 /**
6066  * @class Roo.bootstrap.TriggerField
6067  * @extends Roo.bootstrap.Input
6068  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6069  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6070  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6071  * for which you can provide a custom implementation.  For example:
6072  * <pre><code>
6073 var trigger = new Roo.bootstrap.TriggerField();
6074 trigger.onTriggerClick = myTriggerFn;
6075 trigger.applyTo('my-field');
6076 </code></pre>
6077  *
6078  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6079  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6080  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6081  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6082  * @constructor
6083  * Create a new TriggerField.
6084  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6085  * to the base TextField)
6086  */
6087 Roo.bootstrap.TriggerField = function(config){
6088     this.mimicing = false;
6089     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6090 };
6091
6092 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6093     /**
6094      * @cfg {String} triggerClass A CSS class to apply to the trigger
6095      */
6096      /**
6097      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6098      */
6099     hideTrigger:false,
6100
6101     /** @cfg {Boolean} grow @hide */
6102     /** @cfg {Number} growMin @hide */
6103     /** @cfg {Number} growMax @hide */
6104
6105     /**
6106      * @hide 
6107      * @method
6108      */
6109     autoSize: Roo.emptyFn,
6110     // private
6111     monitorTab : true,
6112     // private
6113     deferHeight : true,
6114
6115     
6116     actionMode : 'wrap',
6117     
6118     
6119     
6120     getAutoCreate : function(){
6121        
6122         var parent = this.parent();
6123         
6124         var align = this.parentLabelAlign();
6125         
6126         var id = Roo.id();
6127         
6128         var cfg = {
6129             cls: 'form-group' //input-group
6130         };
6131         
6132         
6133         var input =  {
6134             tag: 'input',
6135             id : id,
6136             type : this.inputType,
6137             cls : 'form-control',
6138             autocomplete: 'off',
6139             placeholder : this.placeholder || '' 
6140             
6141         };
6142         if (this.name) {
6143             input.name = this.name;
6144         }
6145         if (this.size) {
6146             input.cls += ' input-' + this.size;
6147         }
6148         
6149         if (this.disabled) {
6150             input.disabled=true;
6151         }
6152         
6153         var inputblock = input;
6154         
6155         if (this.before || this.after) {
6156             
6157             inputblock = {
6158                 cls : 'input-group',
6159                 cn :  [] 
6160             };
6161             if (this.before) {
6162                 inputblock.cn.push({
6163                     tag :'span',
6164                     cls : 'input-group-addon',
6165                     html : this.before
6166                 });
6167             }
6168             inputblock.cn.push(input);
6169             if (this.after) {
6170                 inputblock.cn.push({
6171                     tag :'span',
6172                     cls : 'input-group-addon',
6173                     html : this.after
6174                 });
6175             }
6176             
6177         };
6178         
6179         var box = {
6180             tag: 'div',
6181             cn: [
6182                 {
6183                     tag: 'input',
6184                     type : 'hidden',
6185                     cls: 'form-hidden-field'
6186                 },
6187                 inputblock
6188             ]
6189             
6190         };
6191         
6192         if(this.multiple){
6193             Roo.log('multiple');
6194             
6195             box = {
6196                 tag: 'div',
6197                 cn: [
6198                     {
6199                         tag: 'input',
6200                         type : 'hidden',
6201                         cls: 'form-hidden-field'
6202                     },
6203                     {
6204                         tag: 'ul',
6205                         cls: 'select2-choices',
6206                         cn:[
6207                             {
6208                                 tag: 'li',
6209                                 cls: 'select2-search-field',
6210                                 cn: [
6211
6212                                     inputblock
6213                                 ]
6214                             }
6215                         ]
6216                     }
6217                 ]
6218             }
6219         };
6220         
6221         var combobox = {
6222             cls: 'select2-container input-group',
6223             cn: [
6224                 box,
6225                 {
6226                     tag: 'ul',
6227                     cls: 'typeahead typeahead-long dropdown-menu',
6228                     style: 'display:none'
6229                 }
6230             ]
6231         };
6232         
6233         if(!this.multiple){
6234             combobox.cn.push({
6235                 tag :'span',
6236                 cls : 'input-group-addon btn dropdown-toggle',
6237                 cn : [
6238                     {
6239                         tag: 'span',
6240                         cls: 'caret'
6241                     },
6242                     {
6243                         tag: 'span',
6244                         cls: 'combobox-clear',
6245                         cn  : [
6246                             {
6247                                 tag : 'i',
6248                                 cls: 'icon-remove'
6249                             }
6250                         ]
6251                     }
6252                 ]
6253
6254             })
6255         }
6256         
6257         if(this.multiple){
6258             combobox.cls += ' select2-container-multi';
6259         }
6260         
6261         if (align ==='left' && this.fieldLabel.length) {
6262             
6263                 Roo.log("left and has label");
6264                 cfg.cn = [
6265                     
6266                     {
6267                         tag: 'label',
6268                         'for' :  id,
6269                         cls : 'control-label col-sm-' + this.labelWidth,
6270                         html : this.fieldLabel
6271                         
6272                     },
6273                     {
6274                         cls : "col-sm-" + (12 - this.labelWidth), 
6275                         cn: [
6276                             combobox
6277                         ]
6278                     }
6279                     
6280                 ];
6281         } else if ( this.fieldLabel.length) {
6282                 Roo.log(" label");
6283                  cfg.cn = [
6284                    
6285                     {
6286                         tag: 'label',
6287                         //cls : 'input-group-addon',
6288                         html : this.fieldLabel
6289                         
6290                     },
6291                     
6292                     combobox
6293                     
6294                 ];
6295
6296         } else {
6297             
6298                 Roo.log(" no label && no align");
6299                 cfg = combobox
6300                      
6301                 
6302         }
6303          
6304         var settings=this;
6305         ['xs','sm','md','lg'].map(function(size){
6306             if (settings[size]) {
6307                 cfg.cls += ' col-' + size + '-' + settings[size];
6308             }
6309         });
6310         
6311         return cfg;
6312         
6313     },
6314     
6315     
6316     
6317     // private
6318     onResize : function(w, h){
6319 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6320 //        if(typeof w == 'number'){
6321 //            var x = w - this.trigger.getWidth();
6322 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6323 //            this.trigger.setStyle('left', x+'px');
6324 //        }
6325     },
6326
6327     // private
6328     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6329
6330     // private
6331     getResizeEl : function(){
6332         return this.inputEl();
6333     },
6334
6335     // private
6336     getPositionEl : function(){
6337         return this.inputEl();
6338     },
6339
6340     // private
6341     alignErrorIcon : function(){
6342         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6343     },
6344
6345     // private
6346     initEvents : function(){
6347         
6348         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6349         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6350         if(!this.multiple){
6351             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6352             if(this.hideTrigger){
6353                 this.trigger.setDisplayed(false);
6354             }
6355             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6356         }
6357         
6358         if(this.multiple){
6359             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6360         }
6361         
6362         //this.trigger.addClassOnOver('x-form-trigger-over');
6363         //this.trigger.addClassOnClick('x-form-trigger-click');
6364         
6365         //if(!this.width){
6366         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6367         //}
6368     },
6369
6370     // private
6371     initTrigger : function(){
6372        
6373     },
6374
6375     // private
6376     onDestroy : function(){
6377         if(this.trigger){
6378             this.trigger.removeAllListeners();
6379           //  this.trigger.remove();
6380         }
6381         //if(this.wrap){
6382         //    this.wrap.remove();
6383         //}
6384         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6385     },
6386
6387     // private
6388     onFocus : function(){
6389         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6390         /*
6391         if(!this.mimicing){
6392             this.wrap.addClass('x-trigger-wrap-focus');
6393             this.mimicing = true;
6394             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6395             if(this.monitorTab){
6396                 this.el.on("keydown", this.checkTab, this);
6397             }
6398         }
6399         */
6400     },
6401
6402     // private
6403     checkTab : function(e){
6404         if(e.getKey() == e.TAB){
6405             this.triggerBlur();
6406         }
6407     },
6408
6409     // private
6410     onBlur : function(){
6411         // do nothing
6412     },
6413
6414     // private
6415     mimicBlur : function(e, t){
6416         /*
6417         if(!this.wrap.contains(t) && this.validateBlur()){
6418             this.triggerBlur();
6419         }
6420         */
6421     },
6422
6423     // private
6424     triggerBlur : function(){
6425         this.mimicing = false;
6426         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6427         if(this.monitorTab){
6428             this.el.un("keydown", this.checkTab, this);
6429         }
6430         //this.wrap.removeClass('x-trigger-wrap-focus');
6431         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6432     },
6433
6434     // private
6435     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6436     validateBlur : function(e, t){
6437         return true;
6438     },
6439
6440     // private
6441     onDisable : function(){
6442         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6443         //if(this.wrap){
6444         //    this.wrap.addClass('x-item-disabled');
6445         //}
6446     },
6447
6448     // private
6449     onEnable : function(){
6450         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6451         //if(this.wrap){
6452         //    this.el.removeClass('x-item-disabled');
6453         //}
6454     },
6455
6456     // private
6457     onShow : function(){
6458         var ae = this.getActionEl();
6459         
6460         if(ae){
6461             ae.dom.style.display = '';
6462             ae.dom.style.visibility = 'visible';
6463         }
6464     },
6465
6466     // private
6467     
6468     onHide : function(){
6469         var ae = this.getActionEl();
6470         ae.dom.style.display = 'none';
6471     },
6472
6473     /**
6474      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6475      * by an implementing function.
6476      * @method
6477      * @param {EventObject} e
6478      */
6479     onTriggerClick : Roo.emptyFn
6480 });
6481  /*
6482  * Based on:
6483  * Ext JS Library 1.1.1
6484  * Copyright(c) 2006-2007, Ext JS, LLC.
6485  *
6486  * Originally Released Under LGPL - original licence link has changed is not relivant.
6487  *
6488  * Fork - LGPL
6489  * <script type="text/javascript">
6490  */
6491
6492
6493 /**
6494  * @class Roo.data.SortTypes
6495  * @singleton
6496  * Defines the default sorting (casting?) comparison functions used when sorting data.
6497  */
6498 Roo.data.SortTypes = {
6499     /**
6500      * Default sort that does nothing
6501      * @param {Mixed} s The value being converted
6502      * @return {Mixed} The comparison value
6503      */
6504     none : function(s){
6505         return s;
6506     },
6507     
6508     /**
6509      * The regular expression used to strip tags
6510      * @type {RegExp}
6511      * @property
6512      */
6513     stripTagsRE : /<\/?[^>]+>/gi,
6514     
6515     /**
6516      * Strips all HTML tags to sort on text only
6517      * @param {Mixed} s The value being converted
6518      * @return {String} The comparison value
6519      */
6520     asText : function(s){
6521         return String(s).replace(this.stripTagsRE, "");
6522     },
6523     
6524     /**
6525      * Strips all HTML tags to sort on text only - Case insensitive
6526      * @param {Mixed} s The value being converted
6527      * @return {String} The comparison value
6528      */
6529     asUCText : function(s){
6530         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6531     },
6532     
6533     /**
6534      * Case insensitive string
6535      * @param {Mixed} s The value being converted
6536      * @return {String} The comparison value
6537      */
6538     asUCString : function(s) {
6539         return String(s).toUpperCase();
6540     },
6541     
6542     /**
6543      * Date sorting
6544      * @param {Mixed} s The value being converted
6545      * @return {Number} The comparison value
6546      */
6547     asDate : function(s) {
6548         if(!s){
6549             return 0;
6550         }
6551         if(s instanceof Date){
6552             return s.getTime();
6553         }
6554         return Date.parse(String(s));
6555     },
6556     
6557     /**
6558      * Float sorting
6559      * @param {Mixed} s The value being converted
6560      * @return {Float} The comparison value
6561      */
6562     asFloat : function(s) {
6563         var val = parseFloat(String(s).replace(/,/g, ""));
6564         if(isNaN(val)) val = 0;
6565         return val;
6566     },
6567     
6568     /**
6569      * Integer sorting
6570      * @param {Mixed} s The value being converted
6571      * @return {Number} The comparison value
6572      */
6573     asInt : function(s) {
6574         var val = parseInt(String(s).replace(/,/g, ""));
6575         if(isNaN(val)) val = 0;
6576         return val;
6577     }
6578 };/*
6579  * Based on:
6580  * Ext JS Library 1.1.1
6581  * Copyright(c) 2006-2007, Ext JS, LLC.
6582  *
6583  * Originally Released Under LGPL - original licence link has changed is not relivant.
6584  *
6585  * Fork - LGPL
6586  * <script type="text/javascript">
6587  */
6588
6589 /**
6590 * @class Roo.data.Record
6591  * Instances of this class encapsulate both record <em>definition</em> information, and record
6592  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6593  * to access Records cached in an {@link Roo.data.Store} object.<br>
6594  * <p>
6595  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6596  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6597  * objects.<br>
6598  * <p>
6599  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6600  * @constructor
6601  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6602  * {@link #create}. The parameters are the same.
6603  * @param {Array} data An associative Array of data values keyed by the field name.
6604  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6605  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6606  * not specified an integer id is generated.
6607  */
6608 Roo.data.Record = function(data, id){
6609     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6610     this.data = data;
6611 };
6612
6613 /**
6614  * Generate a constructor for a specific record layout.
6615  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6616  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6617  * Each field definition object may contain the following properties: <ul>
6618  * <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,
6619  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6620  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6621  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6622  * is being used, then this is a string containing the javascript expression to reference the data relative to 
6623  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6624  * to the data item relative to the record element. If the mapping expression is the same as the field name,
6625  * this may be omitted.</p></li>
6626  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6627  * <ul><li>auto (Default, implies no conversion)</li>
6628  * <li>string</li>
6629  * <li>int</li>
6630  * <li>float</li>
6631  * <li>boolean</li>
6632  * <li>date</li></ul></p></li>
6633  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6634  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6635  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6636  * by the Reader into an object that will be stored in the Record. It is passed the
6637  * following parameters:<ul>
6638  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6639  * </ul></p></li>
6640  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6641  * </ul>
6642  * <br>usage:<br><pre><code>
6643 var TopicRecord = Roo.data.Record.create(
6644     {name: 'title', mapping: 'topic_title'},
6645     {name: 'author', mapping: 'username'},
6646     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6647     {name: 'lastPost', mapping: 'post_time', type: 'date'},
6648     {name: 'lastPoster', mapping: 'user2'},
6649     {name: 'excerpt', mapping: 'post_text'}
6650 );
6651
6652 var myNewRecord = new TopicRecord({
6653     title: 'Do my job please',
6654     author: 'noobie',
6655     totalPosts: 1,
6656     lastPost: new Date(),
6657     lastPoster: 'Animal',
6658     excerpt: 'No way dude!'
6659 });
6660 myStore.add(myNewRecord);
6661 </code></pre>
6662  * @method create
6663  * @static
6664  */
6665 Roo.data.Record.create = function(o){
6666     var f = function(){
6667         f.superclass.constructor.apply(this, arguments);
6668     };
6669     Roo.extend(f, Roo.data.Record);
6670     var p = f.prototype;
6671     p.fields = new Roo.util.MixedCollection(false, function(field){
6672         return field.name;
6673     });
6674     for(var i = 0, len = o.length; i < len; i++){
6675         p.fields.add(new Roo.data.Field(o[i]));
6676     }
6677     f.getField = function(name){
6678         return p.fields.get(name);  
6679     };
6680     return f;
6681 };
6682
6683 Roo.data.Record.AUTO_ID = 1000;
6684 Roo.data.Record.EDIT = 'edit';
6685 Roo.data.Record.REJECT = 'reject';
6686 Roo.data.Record.COMMIT = 'commit';
6687
6688 Roo.data.Record.prototype = {
6689     /**
6690      * Readonly flag - true if this record has been modified.
6691      * @type Boolean
6692      */
6693     dirty : false,
6694     editing : false,
6695     error: null,
6696     modified: null,
6697
6698     // private
6699     join : function(store){
6700         this.store = store;
6701     },
6702
6703     /**
6704      * Set the named field to the specified value.
6705      * @param {String} name The name of the field to set.
6706      * @param {Object} value The value to set the field to.
6707      */
6708     set : function(name, value){
6709         if(this.data[name] == value){
6710             return;
6711         }
6712         this.dirty = true;
6713         if(!this.modified){
6714             this.modified = {};
6715         }
6716         if(typeof this.modified[name] == 'undefined'){
6717             this.modified[name] = this.data[name];
6718         }
6719         this.data[name] = value;
6720         if(!this.editing && this.store){
6721             this.store.afterEdit(this);
6722         }       
6723     },
6724
6725     /**
6726      * Get the value of the named field.
6727      * @param {String} name The name of the field to get the value of.
6728      * @return {Object} The value of the field.
6729      */
6730     get : function(name){
6731         return this.data[name]; 
6732     },
6733
6734     // private
6735     beginEdit : function(){
6736         this.editing = true;
6737         this.modified = {}; 
6738     },
6739
6740     // private
6741     cancelEdit : function(){
6742         this.editing = false;
6743         delete this.modified;
6744     },
6745
6746     // private
6747     endEdit : function(){
6748         this.editing = false;
6749         if(this.dirty && this.store){
6750             this.store.afterEdit(this);
6751         }
6752     },
6753
6754     /**
6755      * Usually called by the {@link Roo.data.Store} which owns the Record.
6756      * Rejects all changes made to the Record since either creation, or the last commit operation.
6757      * Modified fields are reverted to their original values.
6758      * <p>
6759      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6760      * of reject operations.
6761      */
6762     reject : function(){
6763         var m = this.modified;
6764         for(var n in m){
6765             if(typeof m[n] != "function"){
6766                 this.data[n] = m[n];
6767             }
6768         }
6769         this.dirty = false;
6770         delete this.modified;
6771         this.editing = false;
6772         if(this.store){
6773             this.store.afterReject(this);
6774         }
6775     },
6776
6777     /**
6778      * Usually called by the {@link Roo.data.Store} which owns the Record.
6779      * Commits all changes made to the Record since either creation, or the last commit operation.
6780      * <p>
6781      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6782      * of commit operations.
6783      */
6784     commit : function(){
6785         this.dirty = false;
6786         delete this.modified;
6787         this.editing = false;
6788         if(this.store){
6789             this.store.afterCommit(this);
6790         }
6791     },
6792
6793     // private
6794     hasError : function(){
6795         return this.error != null;
6796     },
6797
6798     // private
6799     clearError : function(){
6800         this.error = null;
6801     },
6802
6803     /**
6804      * Creates a copy of this record.
6805      * @param {String} id (optional) A new record id if you don't want to use this record's id
6806      * @return {Record}
6807      */
6808     copy : function(newId) {
6809         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6810     }
6811 };/*
6812  * Based on:
6813  * Ext JS Library 1.1.1
6814  * Copyright(c) 2006-2007, Ext JS, LLC.
6815  *
6816  * Originally Released Under LGPL - original licence link has changed is not relivant.
6817  *
6818  * Fork - LGPL
6819  * <script type="text/javascript">
6820  */
6821
6822
6823
6824 /**
6825  * @class Roo.data.Store
6826  * @extends Roo.util.Observable
6827  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6828  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6829  * <p>
6830  * 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
6831  * has no knowledge of the format of the data returned by the Proxy.<br>
6832  * <p>
6833  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6834  * instances from the data object. These records are cached and made available through accessor functions.
6835  * @constructor
6836  * Creates a new Store.
6837  * @param {Object} config A config object containing the objects needed for the Store to access data,
6838  * and read the data into Records.
6839  */
6840 Roo.data.Store = function(config){
6841     this.data = new Roo.util.MixedCollection(false);
6842     this.data.getKey = function(o){
6843         return o.id;
6844     };
6845     this.baseParams = {};
6846     // private
6847     this.paramNames = {
6848         "start" : "start",
6849         "limit" : "limit",
6850         "sort" : "sort",
6851         "dir" : "dir",
6852         "multisort" : "_multisort"
6853     };
6854
6855     if(config && config.data){
6856         this.inlineData = config.data;
6857         delete config.data;
6858     }
6859
6860     Roo.apply(this, config);
6861     
6862     if(this.reader){ // reader passed
6863         this.reader = Roo.factory(this.reader, Roo.data);
6864         this.reader.xmodule = this.xmodule || false;
6865         if(!this.recordType){
6866             this.recordType = this.reader.recordType;
6867         }
6868         if(this.reader.onMetaChange){
6869             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6870         }
6871     }
6872
6873     if(this.recordType){
6874         this.fields = this.recordType.prototype.fields;
6875     }
6876     this.modified = [];
6877
6878     this.addEvents({
6879         /**
6880          * @event datachanged
6881          * Fires when the data cache has changed, and a widget which is using this Store
6882          * as a Record cache should refresh its view.
6883          * @param {Store} this
6884          */
6885         datachanged : true,
6886         /**
6887          * @event metachange
6888          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6889          * @param {Store} this
6890          * @param {Object} meta The JSON metadata
6891          */
6892         metachange : true,
6893         /**
6894          * @event add
6895          * Fires when Records have been added to the Store
6896          * @param {Store} this
6897          * @param {Roo.data.Record[]} records The array of Records added
6898          * @param {Number} index The index at which the record(s) were added
6899          */
6900         add : true,
6901         /**
6902          * @event remove
6903          * Fires when a Record has been removed from the Store
6904          * @param {Store} this
6905          * @param {Roo.data.Record} record The Record that was removed
6906          * @param {Number} index The index at which the record was removed
6907          */
6908         remove : true,
6909         /**
6910          * @event update
6911          * Fires when a Record has been updated
6912          * @param {Store} this
6913          * @param {Roo.data.Record} record The Record that was updated
6914          * @param {String} operation The update operation being performed.  Value may be one of:
6915          * <pre><code>
6916  Roo.data.Record.EDIT
6917  Roo.data.Record.REJECT
6918  Roo.data.Record.COMMIT
6919          * </code></pre>
6920          */
6921         update : true,
6922         /**
6923          * @event clear
6924          * Fires when the data cache has been cleared.
6925          * @param {Store} this
6926          */
6927         clear : true,
6928         /**
6929          * @event beforeload
6930          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6931          * the load action will be canceled.
6932          * @param {Store} this
6933          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6934          */
6935         beforeload : true,
6936         /**
6937          * @event beforeloadadd
6938          * Fires after a new set of Records has been loaded.
6939          * @param {Store} this
6940          * @param {Roo.data.Record[]} records The Records that were loaded
6941          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6942          */
6943         beforeloadadd : true,
6944         /**
6945          * @event load
6946          * Fires after a new set of Records has been loaded, before they are added to the store.
6947          * @param {Store} this
6948          * @param {Roo.data.Record[]} records The Records that were loaded
6949          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6950          * @params {Object} return from reader
6951          */
6952         load : true,
6953         /**
6954          * @event loadexception
6955          * Fires if an exception occurs in the Proxy during loading.
6956          * Called with the signature of the Proxy's "loadexception" event.
6957          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6958          * 
6959          * @param {Proxy} 
6960          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6961          * @param {Object} load options 
6962          * @param {Object} jsonData from your request (normally this contains the Exception)
6963          */
6964         loadexception : true
6965     });
6966     
6967     if(this.proxy){
6968         this.proxy = Roo.factory(this.proxy, Roo.data);
6969         this.proxy.xmodule = this.xmodule || false;
6970         this.relayEvents(this.proxy,  ["loadexception"]);
6971     }
6972     this.sortToggle = {};
6973     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6974
6975     Roo.data.Store.superclass.constructor.call(this);
6976
6977     if(this.inlineData){
6978         this.loadData(this.inlineData);
6979         delete this.inlineData;
6980     }
6981 };
6982
6983 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6984      /**
6985     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6986     * without a remote query - used by combo/forms at present.
6987     */
6988     
6989     /**
6990     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6991     */
6992     /**
6993     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6994     */
6995     /**
6996     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6997     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6998     */
6999     /**
7000     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7001     * on any HTTP request
7002     */
7003     /**
7004     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7005     */
7006     /**
7007     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7008     */
7009     multiSort: false,
7010     /**
7011     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7012     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7013     */
7014     remoteSort : false,
7015
7016     /**
7017     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7018      * loaded or when a record is removed. (defaults to false).
7019     */
7020     pruneModifiedRecords : false,
7021
7022     // private
7023     lastOptions : null,
7024
7025     /**
7026      * Add Records to the Store and fires the add event.
7027      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7028      */
7029     add : function(records){
7030         records = [].concat(records);
7031         for(var i = 0, len = records.length; i < len; i++){
7032             records[i].join(this);
7033         }
7034         var index = this.data.length;
7035         this.data.addAll(records);
7036         this.fireEvent("add", this, records, index);
7037     },
7038
7039     /**
7040      * Remove a Record from the Store and fires the remove event.
7041      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7042      */
7043     remove : function(record){
7044         var index = this.data.indexOf(record);
7045         this.data.removeAt(index);
7046         if(this.pruneModifiedRecords){
7047             this.modified.remove(record);
7048         }
7049         this.fireEvent("remove", this, record, index);
7050     },
7051
7052     /**
7053      * Remove all Records from the Store and fires the clear event.
7054      */
7055     removeAll : function(){
7056         this.data.clear();
7057         if(this.pruneModifiedRecords){
7058             this.modified = [];
7059         }
7060         this.fireEvent("clear", this);
7061     },
7062
7063     /**
7064      * Inserts Records to the Store at the given index and fires the add event.
7065      * @param {Number} index The start index at which to insert the passed Records.
7066      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7067      */
7068     insert : function(index, records){
7069         records = [].concat(records);
7070         for(var i = 0, len = records.length; i < len; i++){
7071             this.data.insert(index, records[i]);
7072             records[i].join(this);
7073         }
7074         this.fireEvent("add", this, records, index);
7075     },
7076
7077     /**
7078      * Get the index within the cache of the passed Record.
7079      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7080      * @return {Number} The index of the passed Record. Returns -1 if not found.
7081      */
7082     indexOf : function(record){
7083         return this.data.indexOf(record);
7084     },
7085
7086     /**
7087      * Get the index within the cache of the Record with the passed id.
7088      * @param {String} id The id of the Record to find.
7089      * @return {Number} The index of the Record. Returns -1 if not found.
7090      */
7091     indexOfId : function(id){
7092         return this.data.indexOfKey(id);
7093     },
7094
7095     /**
7096      * Get the Record with the specified id.
7097      * @param {String} id The id of the Record to find.
7098      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7099      */
7100     getById : function(id){
7101         return this.data.key(id);
7102     },
7103
7104     /**
7105      * Get the Record at the specified index.
7106      * @param {Number} index The index of the Record to find.
7107      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7108      */
7109     getAt : function(index){
7110         return this.data.itemAt(index);
7111     },
7112
7113     /**
7114      * Returns a range of Records between specified indices.
7115      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7116      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7117      * @return {Roo.data.Record[]} An array of Records
7118      */
7119     getRange : function(start, end){
7120         return this.data.getRange(start, end);
7121     },
7122
7123     // private
7124     storeOptions : function(o){
7125         o = Roo.apply({}, o);
7126         delete o.callback;
7127         delete o.scope;
7128         this.lastOptions = o;
7129     },
7130
7131     /**
7132      * Loads the Record cache from the configured Proxy using the configured Reader.
7133      * <p>
7134      * If using remote paging, then the first load call must specify the <em>start</em>
7135      * and <em>limit</em> properties in the options.params property to establish the initial
7136      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7137      * <p>
7138      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7139      * and this call will return before the new data has been loaded. Perform any post-processing
7140      * in a callback function, or in a "load" event handler.</strong>
7141      * <p>
7142      * @param {Object} options An object containing properties which control loading options:<ul>
7143      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7144      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7145      * passed the following arguments:<ul>
7146      * <li>r : Roo.data.Record[]</li>
7147      * <li>options: Options object from the load call</li>
7148      * <li>success: Boolean success indicator</li></ul></li>
7149      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7150      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7151      * </ul>
7152      */
7153     load : function(options){
7154         options = options || {};
7155         if(this.fireEvent("beforeload", this, options) !== false){
7156             this.storeOptions(options);
7157             var p = Roo.apply(options.params || {}, this.baseParams);
7158             // if meta was not loaded from remote source.. try requesting it.
7159             if (!this.reader.metaFromRemote) {
7160                 p._requestMeta = 1;
7161             }
7162             if(this.sortInfo && this.remoteSort){
7163                 var pn = this.paramNames;
7164                 p[pn["sort"]] = this.sortInfo.field;
7165                 p[pn["dir"]] = this.sortInfo.direction;
7166             }
7167             if (this.multiSort) {
7168                 var pn = this.paramNames;
7169                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7170             }
7171             
7172             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7173         }
7174     },
7175
7176     /**
7177      * Reloads the Record cache from the configured Proxy using the configured Reader and
7178      * the options from the last load operation performed.
7179      * @param {Object} options (optional) An object containing properties which may override the options
7180      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7181      * the most recently used options are reused).
7182      */
7183     reload : function(options){
7184         this.load(Roo.applyIf(options||{}, this.lastOptions));
7185     },
7186
7187     // private
7188     // Called as a callback by the Reader during a load operation.
7189     loadRecords : function(o, options, success){
7190         if(!o || success === false){
7191             if(success !== false){
7192                 this.fireEvent("load", this, [], options, o);
7193             }
7194             if(options.callback){
7195                 options.callback.call(options.scope || this, [], options, false);
7196             }
7197             return;
7198         }
7199         // if data returned failure - throw an exception.
7200         if (o.success === false) {
7201             // show a message if no listener is registered.
7202             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7203                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7204             }
7205             // loadmask wil be hooked into this..
7206             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7207             return;
7208         }
7209         var r = o.records, t = o.totalRecords || r.length;
7210         
7211         this.fireEvent("beforeloadadd", this, r, options, o);
7212         
7213         if(!options || options.add !== true){
7214             if(this.pruneModifiedRecords){
7215                 this.modified = [];
7216             }
7217             for(var i = 0, len = r.length; i < len; i++){
7218                 r[i].join(this);
7219             }
7220             if(this.snapshot){
7221                 this.data = this.snapshot;
7222                 delete this.snapshot;
7223             }
7224             this.data.clear();
7225             this.data.addAll(r);
7226             this.totalLength = t;
7227             this.applySort();
7228             this.fireEvent("datachanged", this);
7229         }else{
7230             this.totalLength = Math.max(t, this.data.length+r.length);
7231             this.add(r);
7232         }
7233         this.fireEvent("load", this, r, options, o);
7234         if(options.callback){
7235             options.callback.call(options.scope || this, r, options, true);
7236         }
7237     },
7238
7239
7240     /**
7241      * Loads data from a passed data block. A Reader which understands the format of the data
7242      * must have been configured in the constructor.
7243      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7244      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7245      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7246      */
7247     loadData : function(o, append){
7248         var r = this.reader.readRecords(o);
7249         this.loadRecords(r, {add: append}, true);
7250     },
7251
7252     /**
7253      * Gets the number of cached records.
7254      * <p>
7255      * <em>If using paging, this may not be the total size of the dataset. If the data object
7256      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7257      * the data set size</em>
7258      */
7259     getCount : function(){
7260         return this.data.length || 0;
7261     },
7262
7263     /**
7264      * Gets the total number of records in the dataset as returned by the server.
7265      * <p>
7266      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7267      * the dataset size</em>
7268      */
7269     getTotalCount : function(){
7270         return this.totalLength || 0;
7271     },
7272
7273     /**
7274      * Returns the sort state of the Store as an object with two properties:
7275      * <pre><code>
7276  field {String} The name of the field by which the Records are sorted
7277  direction {String} The sort order, "ASC" or "DESC"
7278      * </code></pre>
7279      */
7280     getSortState : function(){
7281         return this.sortInfo;
7282     },
7283
7284     // private
7285     applySort : function(){
7286         if(this.sortInfo && !this.remoteSort){
7287             var s = this.sortInfo, f = s.field;
7288             var st = this.fields.get(f).sortType;
7289             var fn = function(r1, r2){
7290                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7291                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7292             };
7293             this.data.sort(s.direction, fn);
7294             if(this.snapshot && this.snapshot != this.data){
7295                 this.snapshot.sort(s.direction, fn);
7296             }
7297         }
7298     },
7299
7300     /**
7301      * Sets the default sort column and order to be used by the next load operation.
7302      * @param {String} fieldName The name of the field to sort by.
7303      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7304      */
7305     setDefaultSort : function(field, dir){
7306         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7307     },
7308
7309     /**
7310      * Sort the Records.
7311      * If remote sorting is used, the sort is performed on the server, and the cache is
7312      * reloaded. If local sorting is used, the cache is sorted internally.
7313      * @param {String} fieldName The name of the field to sort by.
7314      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7315      */
7316     sort : function(fieldName, dir){
7317         var f = this.fields.get(fieldName);
7318         if(!dir){
7319             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7320             
7321             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7322                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7323             }else{
7324                 dir = f.sortDir;
7325             }
7326         }
7327         this.sortToggle[f.name] = dir;
7328         this.sortInfo = {field: f.name, direction: dir};
7329         if(!this.remoteSort){
7330             this.applySort();
7331             this.fireEvent("datachanged", this);
7332         }else{
7333             this.load(this.lastOptions);
7334         }
7335     },
7336
7337     /**
7338      * Calls the specified function for each of the Records in the cache.
7339      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7340      * Returning <em>false</em> aborts and exits the iteration.
7341      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7342      */
7343     each : function(fn, scope){
7344         this.data.each(fn, scope);
7345     },
7346
7347     /**
7348      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7349      * (e.g., during paging).
7350      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7351      */
7352     getModifiedRecords : function(){
7353         return this.modified;
7354     },
7355
7356     // private
7357     createFilterFn : function(property, value, anyMatch){
7358         if(!value.exec){ // not a regex
7359             value = String(value);
7360             if(value.length == 0){
7361                 return false;
7362             }
7363             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7364         }
7365         return function(r){
7366             return value.test(r.data[property]);
7367         };
7368     },
7369
7370     /**
7371      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7372      * @param {String} property A field on your records
7373      * @param {Number} start The record index to start at (defaults to 0)
7374      * @param {Number} end The last record index to include (defaults to length - 1)
7375      * @return {Number} The sum
7376      */
7377     sum : function(property, start, end){
7378         var rs = this.data.items, v = 0;
7379         start = start || 0;
7380         end = (end || end === 0) ? end : rs.length-1;
7381
7382         for(var i = start; i <= end; i++){
7383             v += (rs[i].data[property] || 0);
7384         }
7385         return v;
7386     },
7387
7388     /**
7389      * Filter the records by a specified property.
7390      * @param {String} field A field on your records
7391      * @param {String/RegExp} value Either a string that the field
7392      * should start with or a RegExp to test against the field
7393      * @param {Boolean} anyMatch True to match any part not just the beginning
7394      */
7395     filter : function(property, value, anyMatch){
7396         var fn = this.createFilterFn(property, value, anyMatch);
7397         return fn ? this.filterBy(fn) : this.clearFilter();
7398     },
7399
7400     /**
7401      * Filter by a function. The specified function will be called with each
7402      * record in this data source. If the function returns true the record is included,
7403      * otherwise it is filtered.
7404      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7405      * @param {Object} scope (optional) The scope of the function (defaults to this)
7406      */
7407     filterBy : function(fn, scope){
7408         this.snapshot = this.snapshot || this.data;
7409         this.data = this.queryBy(fn, scope||this);
7410         this.fireEvent("datachanged", this);
7411     },
7412
7413     /**
7414      * Query the records by a specified property.
7415      * @param {String} field A field on your records
7416      * @param {String/RegExp} value Either a string that the field
7417      * should start with or a RegExp to test against the field
7418      * @param {Boolean} anyMatch True to match any part not just the beginning
7419      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7420      */
7421     query : function(property, value, anyMatch){
7422         var fn = this.createFilterFn(property, value, anyMatch);
7423         return fn ? this.queryBy(fn) : this.data.clone();
7424     },
7425
7426     /**
7427      * Query by a function. The specified function will be called with each
7428      * record in this data source. If the function returns true the record is included
7429      * in the results.
7430      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7431      * @param {Object} scope (optional) The scope of the function (defaults to this)
7432       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7433      **/
7434     queryBy : function(fn, scope){
7435         var data = this.snapshot || this.data;
7436         return data.filterBy(fn, scope||this);
7437     },
7438
7439     /**
7440      * Collects unique values for a particular dataIndex from this store.
7441      * @param {String} dataIndex The property to collect
7442      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7443      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7444      * @return {Array} An array of the unique values
7445      **/
7446     collect : function(dataIndex, allowNull, bypassFilter){
7447         var d = (bypassFilter === true && this.snapshot) ?
7448                 this.snapshot.items : this.data.items;
7449         var v, sv, r = [], l = {};
7450         for(var i = 0, len = d.length; i < len; i++){
7451             v = d[i].data[dataIndex];
7452             sv = String(v);
7453             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7454                 l[sv] = true;
7455                 r[r.length] = v;
7456             }
7457         }
7458         return r;
7459     },
7460
7461     /**
7462      * Revert to a view of the Record cache with no filtering applied.
7463      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7464      */
7465     clearFilter : function(suppressEvent){
7466         if(this.snapshot && this.snapshot != this.data){
7467             this.data = this.snapshot;
7468             delete this.snapshot;
7469             if(suppressEvent !== true){
7470                 this.fireEvent("datachanged", this);
7471             }
7472         }
7473     },
7474
7475     // private
7476     afterEdit : function(record){
7477         if(this.modified.indexOf(record) == -1){
7478             this.modified.push(record);
7479         }
7480         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7481     },
7482     
7483     // private
7484     afterReject : function(record){
7485         this.modified.remove(record);
7486         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7487     },
7488
7489     // private
7490     afterCommit : function(record){
7491         this.modified.remove(record);
7492         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7493     },
7494
7495     /**
7496      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7497      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7498      */
7499     commitChanges : function(){
7500         var m = this.modified.slice(0);
7501         this.modified = [];
7502         for(var i = 0, len = m.length; i < len; i++){
7503             m[i].commit();
7504         }
7505     },
7506
7507     /**
7508      * Cancel outstanding changes on all changed records.
7509      */
7510     rejectChanges : function(){
7511         var m = this.modified.slice(0);
7512         this.modified = [];
7513         for(var i = 0, len = m.length; i < len; i++){
7514             m[i].reject();
7515         }
7516     },
7517
7518     onMetaChange : function(meta, rtype, o){
7519         this.recordType = rtype;
7520         this.fields = rtype.prototype.fields;
7521         delete this.snapshot;
7522         this.sortInfo = meta.sortInfo || this.sortInfo;
7523         this.modified = [];
7524         this.fireEvent('metachange', this, this.reader.meta);
7525     },
7526     
7527     moveIndex : function(data, type)
7528     {
7529         var index = this.indexOf(data);
7530         
7531         var newIndex = index + type;
7532         
7533         this.remove(data);
7534         
7535         this.insert(newIndex, data);
7536         
7537     }
7538 });/*
7539  * Based on:
7540  * Ext JS Library 1.1.1
7541  * Copyright(c) 2006-2007, Ext JS, LLC.
7542  *
7543  * Originally Released Under LGPL - original licence link has changed is not relivant.
7544  *
7545  * Fork - LGPL
7546  * <script type="text/javascript">
7547  */
7548
7549 /**
7550  * @class Roo.data.SimpleStore
7551  * @extends Roo.data.Store
7552  * Small helper class to make creating Stores from Array data easier.
7553  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7554  * @cfg {Array} fields An array of field definition objects, or field name strings.
7555  * @cfg {Array} data The multi-dimensional array of data
7556  * @constructor
7557  * @param {Object} config
7558  */
7559 Roo.data.SimpleStore = function(config){
7560     Roo.data.SimpleStore.superclass.constructor.call(this, {
7561         isLocal : true,
7562         reader: new Roo.data.ArrayReader({
7563                 id: config.id
7564             },
7565             Roo.data.Record.create(config.fields)
7566         ),
7567         proxy : new Roo.data.MemoryProxy(config.data)
7568     });
7569     this.load();
7570 };
7571 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7572  * Based on:
7573  * Ext JS Library 1.1.1
7574  * Copyright(c) 2006-2007, Ext JS, LLC.
7575  *
7576  * Originally Released Under LGPL - original licence link has changed is not relivant.
7577  *
7578  * Fork - LGPL
7579  * <script type="text/javascript">
7580  */
7581
7582 /**
7583 /**
7584  * @extends Roo.data.Store
7585  * @class Roo.data.JsonStore
7586  * Small helper class to make creating Stores for JSON data easier. <br/>
7587 <pre><code>
7588 var store = new Roo.data.JsonStore({
7589     url: 'get-images.php',
7590     root: 'images',
7591     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7592 });
7593 </code></pre>
7594  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7595  * JsonReader and HttpProxy (unless inline data is provided).</b>
7596  * @cfg {Array} fields An array of field definition objects, or field name strings.
7597  * @constructor
7598  * @param {Object} config
7599  */
7600 Roo.data.JsonStore = function(c){
7601     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7602         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7603         reader: new Roo.data.JsonReader(c, c.fields)
7604     }));
7605 };
7606 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7607  * Based on:
7608  * Ext JS Library 1.1.1
7609  * Copyright(c) 2006-2007, Ext JS, LLC.
7610  *
7611  * Originally Released Under LGPL - original licence link has changed is not relivant.
7612  *
7613  * Fork - LGPL
7614  * <script type="text/javascript">
7615  */
7616
7617  
7618 Roo.data.Field = function(config){
7619     if(typeof config == "string"){
7620         config = {name: config};
7621     }
7622     Roo.apply(this, config);
7623     
7624     if(!this.type){
7625         this.type = "auto";
7626     }
7627     
7628     var st = Roo.data.SortTypes;
7629     // named sortTypes are supported, here we look them up
7630     if(typeof this.sortType == "string"){
7631         this.sortType = st[this.sortType];
7632     }
7633     
7634     // set default sortType for strings and dates
7635     if(!this.sortType){
7636         switch(this.type){
7637             case "string":
7638                 this.sortType = st.asUCString;
7639                 break;
7640             case "date":
7641                 this.sortType = st.asDate;
7642                 break;
7643             default:
7644                 this.sortType = st.none;
7645         }
7646     }
7647
7648     // define once
7649     var stripRe = /[\$,%]/g;
7650
7651     // prebuilt conversion function for this field, instead of
7652     // switching every time we're reading a value
7653     if(!this.convert){
7654         var cv, dateFormat = this.dateFormat;
7655         switch(this.type){
7656             case "":
7657             case "auto":
7658             case undefined:
7659                 cv = function(v){ return v; };
7660                 break;
7661             case "string":
7662                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7663                 break;
7664             case "int":
7665                 cv = function(v){
7666                     return v !== undefined && v !== null && v !== '' ?
7667                            parseInt(String(v).replace(stripRe, ""), 10) : '';
7668                     };
7669                 break;
7670             case "float":
7671                 cv = function(v){
7672                     return v !== undefined && v !== null && v !== '' ?
7673                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
7674                     };
7675                 break;
7676             case "bool":
7677             case "boolean":
7678                 cv = function(v){ return v === true || v === "true" || v == 1; };
7679                 break;
7680             case "date":
7681                 cv = function(v){
7682                     if(!v){
7683                         return '';
7684                     }
7685                     if(v instanceof Date){
7686                         return v;
7687                     }
7688                     if(dateFormat){
7689                         if(dateFormat == "timestamp"){
7690                             return new Date(v*1000);
7691                         }
7692                         return Date.parseDate(v, dateFormat);
7693                     }
7694                     var parsed = Date.parse(v);
7695                     return parsed ? new Date(parsed) : null;
7696                 };
7697              break;
7698             
7699         }
7700         this.convert = cv;
7701     }
7702 };
7703
7704 Roo.data.Field.prototype = {
7705     dateFormat: null,
7706     defaultValue: "",
7707     mapping: null,
7708     sortType : null,
7709     sortDir : "ASC"
7710 };/*
7711  * Based on:
7712  * Ext JS Library 1.1.1
7713  * Copyright(c) 2006-2007, Ext JS, LLC.
7714  *
7715  * Originally Released Under LGPL - original licence link has changed is not relivant.
7716  *
7717  * Fork - LGPL
7718  * <script type="text/javascript">
7719  */
7720  
7721 // Base class for reading structured data from a data source.  This class is intended to be
7722 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7723
7724 /**
7725  * @class Roo.data.DataReader
7726  * Base class for reading structured data from a data source.  This class is intended to be
7727  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7728  */
7729
7730 Roo.data.DataReader = function(meta, recordType){
7731     
7732     this.meta = meta;
7733     
7734     this.recordType = recordType instanceof Array ? 
7735         Roo.data.Record.create(recordType) : recordType;
7736 };
7737
7738 Roo.data.DataReader.prototype = {
7739      /**
7740      * Create an empty record
7741      * @param {Object} data (optional) - overlay some values
7742      * @return {Roo.data.Record} record created.
7743      */
7744     newRow :  function(d) {
7745         var da =  {};
7746         this.recordType.prototype.fields.each(function(c) {
7747             switch( c.type) {
7748                 case 'int' : da[c.name] = 0; break;
7749                 case 'date' : da[c.name] = new Date(); break;
7750                 case 'float' : da[c.name] = 0.0; break;
7751                 case 'boolean' : da[c.name] = false; break;
7752                 default : da[c.name] = ""; break;
7753             }
7754             
7755         });
7756         return new this.recordType(Roo.apply(da, d));
7757     }
7758     
7759 };/*
7760  * Based on:
7761  * Ext JS Library 1.1.1
7762  * Copyright(c) 2006-2007, Ext JS, LLC.
7763  *
7764  * Originally Released Under LGPL - original licence link has changed is not relivant.
7765  *
7766  * Fork - LGPL
7767  * <script type="text/javascript">
7768  */
7769
7770 /**
7771  * @class Roo.data.DataProxy
7772  * @extends Roo.data.Observable
7773  * This class is an abstract base class for implementations which provide retrieval of
7774  * unformatted data objects.<br>
7775  * <p>
7776  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7777  * (of the appropriate type which knows how to parse the data object) to provide a block of
7778  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7779  * <p>
7780  * Custom implementations must implement the load method as described in
7781  * {@link Roo.data.HttpProxy#load}.
7782  */
7783 Roo.data.DataProxy = function(){
7784     this.addEvents({
7785         /**
7786          * @event beforeload
7787          * Fires before a network request is made to retrieve a data object.
7788          * @param {Object} This DataProxy object.
7789          * @param {Object} params The params parameter to the load function.
7790          */
7791         beforeload : true,
7792         /**
7793          * @event load
7794          * Fires before the load method's callback is called.
7795          * @param {Object} This DataProxy object.
7796          * @param {Object} o The data object.
7797          * @param {Object} arg The callback argument object passed to the load function.
7798          */
7799         load : true,
7800         /**
7801          * @event loadexception
7802          * Fires if an Exception occurs during data retrieval.
7803          * @param {Object} This DataProxy object.
7804          * @param {Object} o The data object.
7805          * @param {Object} arg The callback argument object passed to the load function.
7806          * @param {Object} e The Exception.
7807          */
7808         loadexception : true
7809     });
7810     Roo.data.DataProxy.superclass.constructor.call(this);
7811 };
7812
7813 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7814
7815     /**
7816      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7817      */
7818 /*
7819  * Based on:
7820  * Ext JS Library 1.1.1
7821  * Copyright(c) 2006-2007, Ext JS, LLC.
7822  *
7823  * Originally Released Under LGPL - original licence link has changed is not relivant.
7824  *
7825  * Fork - LGPL
7826  * <script type="text/javascript">
7827  */
7828 /**
7829  * @class Roo.data.MemoryProxy
7830  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7831  * to the Reader when its load method is called.
7832  * @constructor
7833  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7834  */
7835 Roo.data.MemoryProxy = function(data){
7836     if (data.data) {
7837         data = data.data;
7838     }
7839     Roo.data.MemoryProxy.superclass.constructor.call(this);
7840     this.data = data;
7841 };
7842
7843 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7844     /**
7845      * Load data from the requested source (in this case an in-memory
7846      * data object passed to the constructor), read the data object into
7847      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7848      * process that block using the passed callback.
7849      * @param {Object} params This parameter is not used by the MemoryProxy class.
7850      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7851      * object into a block of Roo.data.Records.
7852      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7853      * The function must be passed <ul>
7854      * <li>The Record block object</li>
7855      * <li>The "arg" argument from the load function</li>
7856      * <li>A boolean success indicator</li>
7857      * </ul>
7858      * @param {Object} scope The scope in which to call the callback
7859      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7860      */
7861     load : function(params, reader, callback, scope, arg){
7862         params = params || {};
7863         var result;
7864         try {
7865             result = reader.readRecords(this.data);
7866         }catch(e){
7867             this.fireEvent("loadexception", this, arg, null, e);
7868             callback.call(scope, null, arg, false);
7869             return;
7870         }
7871         callback.call(scope, result, arg, true);
7872     },
7873     
7874     // private
7875     update : function(params, records){
7876         
7877     }
7878 });/*
7879  * Based on:
7880  * Ext JS Library 1.1.1
7881  * Copyright(c) 2006-2007, Ext JS, LLC.
7882  *
7883  * Originally Released Under LGPL - original licence link has changed is not relivant.
7884  *
7885  * Fork - LGPL
7886  * <script type="text/javascript">
7887  */
7888 /**
7889  * @class Roo.data.HttpProxy
7890  * @extends Roo.data.DataProxy
7891  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7892  * configured to reference a certain URL.<br><br>
7893  * <p>
7894  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7895  * from which the running page was served.<br><br>
7896  * <p>
7897  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7898  * <p>
7899  * Be aware that to enable the browser to parse an XML document, the server must set
7900  * the Content-Type header in the HTTP response to "text/xml".
7901  * @constructor
7902  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7903  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7904  * will be used to make the request.
7905  */
7906 Roo.data.HttpProxy = function(conn){
7907     Roo.data.HttpProxy.superclass.constructor.call(this);
7908     // is conn a conn config or a real conn?
7909     this.conn = conn;
7910     this.useAjax = !conn || !conn.events;
7911   
7912 };
7913
7914 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7915     // thse are take from connection...
7916     
7917     /**
7918      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7919      */
7920     /**
7921      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7922      * extra parameters to each request made by this object. (defaults to undefined)
7923      */
7924     /**
7925      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7926      *  to each request made by this object. (defaults to undefined)
7927      */
7928     /**
7929      * @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)
7930      */
7931     /**
7932      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7933      */
7934      /**
7935      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7936      * @type Boolean
7937      */
7938   
7939
7940     /**
7941      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7942      * @type Boolean
7943      */
7944     /**
7945      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7946      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7947      * a finer-grained basis than the DataProxy events.
7948      */
7949     getConnection : function(){
7950         return this.useAjax ? Roo.Ajax : this.conn;
7951     },
7952
7953     /**
7954      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7955      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7956      * process that block using the passed callback.
7957      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7958      * for the request to the remote server.
7959      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7960      * object into a block of Roo.data.Records.
7961      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7962      * The function must be passed <ul>
7963      * <li>The Record block object</li>
7964      * <li>The "arg" argument from the load function</li>
7965      * <li>A boolean success indicator</li>
7966      * </ul>
7967      * @param {Object} scope The scope in which to call the callback
7968      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7969      */
7970     load : function(params, reader, callback, scope, arg){
7971         if(this.fireEvent("beforeload", this, params) !== false){
7972             var  o = {
7973                 params : params || {},
7974                 request: {
7975                     callback : callback,
7976                     scope : scope,
7977                     arg : arg
7978                 },
7979                 reader: reader,
7980                 callback : this.loadResponse,
7981                 scope: this
7982             };
7983             if(this.useAjax){
7984                 Roo.applyIf(o, this.conn);
7985                 if(this.activeRequest){
7986                     Roo.Ajax.abort(this.activeRequest);
7987                 }
7988                 this.activeRequest = Roo.Ajax.request(o);
7989             }else{
7990                 this.conn.request(o);
7991             }
7992         }else{
7993             callback.call(scope||this, null, arg, false);
7994         }
7995     },
7996
7997     // private
7998     loadResponse : function(o, success, response){
7999         delete this.activeRequest;
8000         if(!success){
8001             this.fireEvent("loadexception", this, o, response);
8002             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8003             return;
8004         }
8005         var result;
8006         try {
8007             result = o.reader.read(response);
8008         }catch(e){
8009             this.fireEvent("loadexception", this, o, response, e);
8010             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8011             return;
8012         }
8013         
8014         this.fireEvent("load", this, o, o.request.arg);
8015         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8016     },
8017
8018     // private
8019     update : function(dataSet){
8020
8021     },
8022
8023     // private
8024     updateResponse : function(dataSet){
8025
8026     }
8027 });/*
8028  * Based on:
8029  * Ext JS Library 1.1.1
8030  * Copyright(c) 2006-2007, Ext JS, LLC.
8031  *
8032  * Originally Released Under LGPL - original licence link has changed is not relivant.
8033  *
8034  * Fork - LGPL
8035  * <script type="text/javascript">
8036  */
8037
8038 /**
8039  * @class Roo.data.ScriptTagProxy
8040  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8041  * other than the originating domain of the running page.<br><br>
8042  * <p>
8043  * <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
8044  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8045  * <p>
8046  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8047  * source code that is used as the source inside a &lt;script> tag.<br><br>
8048  * <p>
8049  * In order for the browser to process the returned data, the server must wrap the data object
8050  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8051  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8052  * depending on whether the callback name was passed:
8053  * <p>
8054  * <pre><code>
8055 boolean scriptTag = false;
8056 String cb = request.getParameter("callback");
8057 if (cb != null) {
8058     scriptTag = true;
8059     response.setContentType("text/javascript");
8060 } else {
8061     response.setContentType("application/x-json");
8062 }
8063 Writer out = response.getWriter();
8064 if (scriptTag) {
8065     out.write(cb + "(");
8066 }
8067 out.print(dataBlock.toJsonString());
8068 if (scriptTag) {
8069     out.write(");");
8070 }
8071 </pre></code>
8072  *
8073  * @constructor
8074  * @param {Object} config A configuration object.
8075  */
8076 Roo.data.ScriptTagProxy = function(config){
8077     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8078     Roo.apply(this, config);
8079     this.head = document.getElementsByTagName("head")[0];
8080 };
8081
8082 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8083
8084 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8085     /**
8086      * @cfg {String} url The URL from which to request the data object.
8087      */
8088     /**
8089      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8090      */
8091     timeout : 30000,
8092     /**
8093      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8094      * the server the name of the callback function set up by the load call to process the returned data object.
8095      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8096      * javascript output which calls this named function passing the data object as its only parameter.
8097      */
8098     callbackParam : "callback",
8099     /**
8100      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8101      * name to the request.
8102      */
8103     nocache : true,
8104
8105     /**
8106      * Load data from the configured URL, read the data object into
8107      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8108      * process that block using the passed callback.
8109      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8110      * for the request to the remote server.
8111      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8112      * object into a block of Roo.data.Records.
8113      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8114      * The function must be passed <ul>
8115      * <li>The Record block object</li>
8116      * <li>The "arg" argument from the load function</li>
8117      * <li>A boolean success indicator</li>
8118      * </ul>
8119      * @param {Object} scope The scope in which to call the callback
8120      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8121      */
8122     load : function(params, reader, callback, scope, arg){
8123         if(this.fireEvent("beforeload", this, params) !== false){
8124
8125             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8126
8127             var url = this.url;
8128             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8129             if(this.nocache){
8130                 url += "&_dc=" + (new Date().getTime());
8131             }
8132             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8133             var trans = {
8134                 id : transId,
8135                 cb : "stcCallback"+transId,
8136                 scriptId : "stcScript"+transId,
8137                 params : params,
8138                 arg : arg,
8139                 url : url,
8140                 callback : callback,
8141                 scope : scope,
8142                 reader : reader
8143             };
8144             var conn = this;
8145
8146             window[trans.cb] = function(o){
8147                 conn.handleResponse(o, trans);
8148             };
8149
8150             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8151
8152             if(this.autoAbort !== false){
8153                 this.abort();
8154             }
8155
8156             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8157
8158             var script = document.createElement("script");
8159             script.setAttribute("src", url);
8160             script.setAttribute("type", "text/javascript");
8161             script.setAttribute("id", trans.scriptId);
8162             this.head.appendChild(script);
8163
8164             this.trans = trans;
8165         }else{
8166             callback.call(scope||this, null, arg, false);
8167         }
8168     },
8169
8170     // private
8171     isLoading : function(){
8172         return this.trans ? true : false;
8173     },
8174
8175     /**
8176      * Abort the current server request.
8177      */
8178     abort : function(){
8179         if(this.isLoading()){
8180             this.destroyTrans(this.trans);
8181         }
8182     },
8183
8184     // private
8185     destroyTrans : function(trans, isLoaded){
8186         this.head.removeChild(document.getElementById(trans.scriptId));
8187         clearTimeout(trans.timeoutId);
8188         if(isLoaded){
8189             window[trans.cb] = undefined;
8190             try{
8191                 delete window[trans.cb];
8192             }catch(e){}
8193         }else{
8194             // if hasn't been loaded, wait for load to remove it to prevent script error
8195             window[trans.cb] = function(){
8196                 window[trans.cb] = undefined;
8197                 try{
8198                     delete window[trans.cb];
8199                 }catch(e){}
8200             };
8201         }
8202     },
8203
8204     // private
8205     handleResponse : function(o, trans){
8206         this.trans = false;
8207         this.destroyTrans(trans, true);
8208         var result;
8209         try {
8210             result = trans.reader.readRecords(o);
8211         }catch(e){
8212             this.fireEvent("loadexception", this, o, trans.arg, e);
8213             trans.callback.call(trans.scope||window, null, trans.arg, false);
8214             return;
8215         }
8216         this.fireEvent("load", this, o, trans.arg);
8217         trans.callback.call(trans.scope||window, result, trans.arg, true);
8218     },
8219
8220     // private
8221     handleFailure : function(trans){
8222         this.trans = false;
8223         this.destroyTrans(trans, false);
8224         this.fireEvent("loadexception", this, null, trans.arg);
8225         trans.callback.call(trans.scope||window, null, trans.arg, false);
8226     }
8227 });/*
8228  * Based on:
8229  * Ext JS Library 1.1.1
8230  * Copyright(c) 2006-2007, Ext JS, LLC.
8231  *
8232  * Originally Released Under LGPL - original licence link has changed is not relivant.
8233  *
8234  * Fork - LGPL
8235  * <script type="text/javascript">
8236  */
8237
8238 /**
8239  * @class Roo.data.JsonReader
8240  * @extends Roo.data.DataReader
8241  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8242  * based on mappings in a provided Roo.data.Record constructor.
8243  * 
8244  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8245  * in the reply previously. 
8246  * 
8247  * <p>
8248  * Example code:
8249  * <pre><code>
8250 var RecordDef = Roo.data.Record.create([
8251     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8252     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8253 ]);
8254 var myReader = new Roo.data.JsonReader({
8255     totalProperty: "results",    // The property which contains the total dataset size (optional)
8256     root: "rows",                // The property which contains an Array of row objects
8257     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8258 }, RecordDef);
8259 </code></pre>
8260  * <p>
8261  * This would consume a JSON file like this:
8262  * <pre><code>
8263 { 'results': 2, 'rows': [
8264     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8265     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8266 }
8267 </code></pre>
8268  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8269  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8270  * paged from the remote server.
8271  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8272  * @cfg {String} root name of the property which contains the Array of row objects.
8273  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8274  * @constructor
8275  * Create a new JsonReader
8276  * @param {Object} meta Metadata configuration options
8277  * @param {Object} recordType Either an Array of field definition objects,
8278  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8279  */
8280 Roo.data.JsonReader = function(meta, recordType){
8281     
8282     meta = meta || {};
8283     // set some defaults:
8284     Roo.applyIf(meta, {
8285         totalProperty: 'total',
8286         successProperty : 'success',
8287         root : 'data',
8288         id : 'id'
8289     });
8290     
8291     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8292 };
8293 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8294     
8295     /**
8296      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8297      * Used by Store query builder to append _requestMeta to params.
8298      * 
8299      */
8300     metaFromRemote : false,
8301     /**
8302      * This method is only used by a DataProxy which has retrieved data from a remote server.
8303      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8304      * @return {Object} data A data block which is used by an Roo.data.Store object as
8305      * a cache of Roo.data.Records.
8306      */
8307     read : function(response){
8308         var json = response.responseText;
8309        
8310         var o = /* eval:var:o */ eval("("+json+")");
8311         if(!o) {
8312             throw {message: "JsonReader.read: Json object not found"};
8313         }
8314         
8315         if(o.metaData){
8316             
8317             delete this.ef;
8318             this.metaFromRemote = true;
8319             this.meta = o.metaData;
8320             this.recordType = Roo.data.Record.create(o.metaData.fields);
8321             this.onMetaChange(this.meta, this.recordType, o);
8322         }
8323         return this.readRecords(o);
8324     },
8325
8326     // private function a store will implement
8327     onMetaChange : function(meta, recordType, o){
8328
8329     },
8330
8331     /**
8332          * @ignore
8333          */
8334     simpleAccess: function(obj, subsc) {
8335         return obj[subsc];
8336     },
8337
8338         /**
8339          * @ignore
8340          */
8341     getJsonAccessor: function(){
8342         var re = /[\[\.]/;
8343         return function(expr) {
8344             try {
8345                 return(re.test(expr))
8346                     ? new Function("obj", "return obj." + expr)
8347                     : function(obj){
8348                         return obj[expr];
8349                     };
8350             } catch(e){}
8351             return Roo.emptyFn;
8352         };
8353     }(),
8354
8355     /**
8356      * Create a data block containing Roo.data.Records from an XML document.
8357      * @param {Object} o An object which contains an Array of row objects in the property specified
8358      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8359      * which contains the total size of the dataset.
8360      * @return {Object} data A data block which is used by an Roo.data.Store object as
8361      * a cache of Roo.data.Records.
8362      */
8363     readRecords : function(o){
8364         /**
8365          * After any data loads, the raw JSON data is available for further custom processing.
8366          * @type Object
8367          */
8368         this.o = o;
8369         var s = this.meta, Record = this.recordType,
8370             f = Record.prototype.fields, fi = f.items, fl = f.length;
8371
8372 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8373         if (!this.ef) {
8374             if(s.totalProperty) {
8375                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8376                 }
8377                 if(s.successProperty) {
8378                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8379                 }
8380                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8381                 if (s.id) {
8382                         var g = this.getJsonAccessor(s.id);
8383                         this.getId = function(rec) {
8384                                 var r = g(rec);
8385                                 return (r === undefined || r === "") ? null : r;
8386                         };
8387                 } else {
8388                         this.getId = function(){return null;};
8389                 }
8390             this.ef = [];
8391             for(var jj = 0; jj < fl; jj++){
8392                 f = fi[jj];
8393                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8394                 this.ef[jj] = this.getJsonAccessor(map);
8395             }
8396         }
8397
8398         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8399         if(s.totalProperty){
8400             var vt = parseInt(this.getTotal(o), 10);
8401             if(!isNaN(vt)){
8402                 totalRecords = vt;
8403             }
8404         }
8405         if(s.successProperty){
8406             var vs = this.getSuccess(o);
8407             if(vs === false || vs === 'false'){
8408                 success = false;
8409             }
8410         }
8411         var records = [];
8412             for(var i = 0; i < c; i++){
8413                     var n = root[i];
8414                 var values = {};
8415                 var id = this.getId(n);
8416                 for(var j = 0; j < fl; j++){
8417                     f = fi[j];
8418                 var v = this.ef[j](n);
8419                 if (!f.convert) {
8420                     Roo.log('missing convert for ' + f.name);
8421                     Roo.log(f);
8422                     continue;
8423                 }
8424                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8425                 }
8426                 var record = new Record(values, id);
8427                 record.json = n;
8428                 records[i] = record;
8429             }
8430             return {
8431             raw : o,
8432                 success : success,
8433                 records : records,
8434                 totalRecords : totalRecords
8435             };
8436     }
8437 });/*
8438  * Based on:
8439  * Ext JS Library 1.1.1
8440  * Copyright(c) 2006-2007, Ext JS, LLC.
8441  *
8442  * Originally Released Under LGPL - original licence link has changed is not relivant.
8443  *
8444  * Fork - LGPL
8445  * <script type="text/javascript">
8446  */
8447
8448 /**
8449  * @class Roo.data.ArrayReader
8450  * @extends Roo.data.DataReader
8451  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8452  * Each element of that Array represents a row of data fields. The
8453  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8454  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8455  * <p>
8456  * Example code:.
8457  * <pre><code>
8458 var RecordDef = Roo.data.Record.create([
8459     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8460     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8461 ]);
8462 var myReader = new Roo.data.ArrayReader({
8463     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8464 }, RecordDef);
8465 </code></pre>
8466  * <p>
8467  * This would consume an Array like this:
8468  * <pre><code>
8469 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8470   </code></pre>
8471  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8472  * @constructor
8473  * Create a new JsonReader
8474  * @param {Object} meta Metadata configuration options.
8475  * @param {Object} recordType Either an Array of field definition objects
8476  * as specified to {@link Roo.data.Record#create},
8477  * or an {@link Roo.data.Record} object
8478  * created using {@link Roo.data.Record#create}.
8479  */
8480 Roo.data.ArrayReader = function(meta, recordType){
8481     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8482 };
8483
8484 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8485     /**
8486      * Create a data block containing Roo.data.Records from an XML document.
8487      * @param {Object} o An Array of row objects which represents the dataset.
8488      * @return {Object} data A data block which is used by an Roo.data.Store object as
8489      * a cache of Roo.data.Records.
8490      */
8491     readRecords : function(o){
8492         var sid = this.meta ? this.meta.id : null;
8493         var recordType = this.recordType, fields = recordType.prototype.fields;
8494         var records = [];
8495         var root = o;
8496             for(var i = 0; i < root.length; i++){
8497                     var n = root[i];
8498                 var values = {};
8499                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8500                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8501                 var f = fields.items[j];
8502                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8503                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8504                 v = f.convert(v);
8505                 values[f.name] = v;
8506             }
8507                 var record = new recordType(values, id);
8508                 record.json = n;
8509                 records[records.length] = record;
8510             }
8511             return {
8512                 records : records,
8513                 totalRecords : records.length
8514             };
8515     }
8516 });/*
8517  * - LGPL
8518  * * 
8519  */
8520
8521 /**
8522  * @class Roo.bootstrap.ComboBox
8523  * @extends Roo.bootstrap.TriggerField
8524  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8525  * @cfg {Boolean} append (true|false) default false
8526  * @constructor
8527  * Create a new ComboBox.
8528  * @param {Object} config Configuration options
8529  */
8530 Roo.bootstrap.ComboBox = function(config){
8531     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8532     this.addEvents({
8533         /**
8534          * @event expand
8535          * Fires when the dropdown list is expanded
8536              * @param {Roo.bootstrap.ComboBox} combo This combo box
8537              */
8538         'expand' : true,
8539         /**
8540          * @event collapse
8541          * Fires when the dropdown list is collapsed
8542              * @param {Roo.bootstrap.ComboBox} combo This combo box
8543              */
8544         'collapse' : true,
8545         /**
8546          * @event beforeselect
8547          * Fires before a list item is selected. Return false to cancel the selection.
8548              * @param {Roo.bootstrap.ComboBox} combo This combo box
8549              * @param {Roo.data.Record} record The data record returned from the underlying store
8550              * @param {Number} index The index of the selected item in the dropdown list
8551              */
8552         'beforeselect' : true,
8553         /**
8554          * @event select
8555          * Fires when a list item is selected
8556              * @param {Roo.bootstrap.ComboBox} combo This combo box
8557              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8558              * @param {Number} index The index of the selected item in the dropdown list
8559              */
8560         'select' : true,
8561         /**
8562          * @event beforequery
8563          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8564          * The event object passed has these properties:
8565              * @param {Roo.bootstrap.ComboBox} combo This combo box
8566              * @param {String} query The query
8567              * @param {Boolean} forceAll true to force "all" query
8568              * @param {Boolean} cancel true to cancel the query
8569              * @param {Object} e The query event object
8570              */
8571         'beforequery': true,
8572          /**
8573          * @event add
8574          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8575              * @param {Roo.bootstrap.ComboBox} combo This combo box
8576              */
8577         'add' : true,
8578         /**
8579          * @event edit
8580          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8581              * @param {Roo.bootstrap.ComboBox} combo This combo box
8582              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8583              */
8584         'edit' : true,
8585         /**
8586          * @event remove
8587          * Fires when the remove value from the combobox array
8588              * @param {Roo.bootstrap.ComboBox} combo This combo box
8589              */
8590         'remove' : true
8591         
8592     });
8593     
8594     
8595     this.selectedIndex = -1;
8596     if(this.mode == 'local'){
8597         if(config.queryDelay === undefined){
8598             this.queryDelay = 10;
8599         }
8600         if(config.minChars === undefined){
8601             this.minChars = 0;
8602         }
8603     }
8604 };
8605
8606 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8607      
8608     /**
8609      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8610      * rendering into an Roo.Editor, defaults to false)
8611      */
8612     /**
8613      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8614      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8615      */
8616     /**
8617      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8618      */
8619     /**
8620      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8621      * the dropdown list (defaults to undefined, with no header element)
8622      */
8623
8624      /**
8625      * @cfg {String/Roo.Template} tpl The template to use to render the output
8626      */
8627      
8628      /**
8629      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8630      */
8631     listWidth: undefined,
8632     /**
8633      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8634      * mode = 'remote' or 'text' if mode = 'local')
8635      */
8636     displayField: undefined,
8637     /**
8638      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8639      * mode = 'remote' or 'value' if mode = 'local'). 
8640      * Note: use of a valueField requires the user make a selection
8641      * in order for a value to be mapped.
8642      */
8643     valueField: undefined,
8644     
8645     
8646     /**
8647      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8648      * field's data value (defaults to the underlying DOM element's name)
8649      */
8650     hiddenName: undefined,
8651     /**
8652      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8653      */
8654     listClass: '',
8655     /**
8656      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8657      */
8658     selectedClass: 'active',
8659     
8660     /**
8661      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8662      */
8663     shadow:'sides',
8664     /**
8665      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8666      * anchor positions (defaults to 'tl-bl')
8667      */
8668     listAlign: 'tl-bl?',
8669     /**
8670      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8671      */
8672     maxHeight: 300,
8673     /**
8674      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
8675      * query specified by the allQuery config option (defaults to 'query')
8676      */
8677     triggerAction: 'query',
8678     /**
8679      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8680      * (defaults to 4, does not apply if editable = false)
8681      */
8682     minChars : 4,
8683     /**
8684      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8685      * delay (typeAheadDelay) if it matches a known value (defaults to false)
8686      */
8687     typeAhead: false,
8688     /**
8689      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8690      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8691      */
8692     queryDelay: 500,
8693     /**
8694      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8695      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
8696      */
8697     pageSize: 0,
8698     /**
8699      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
8700      * when editable = true (defaults to false)
8701      */
8702     selectOnFocus:false,
8703     /**
8704      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8705      */
8706     queryParam: 'query',
8707     /**
8708      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
8709      * when mode = 'remote' (defaults to 'Loading...')
8710      */
8711     loadingText: 'Loading...',
8712     /**
8713      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8714      */
8715     resizable: false,
8716     /**
8717      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8718      */
8719     handleHeight : 8,
8720     /**
8721      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8722      * traditional select (defaults to true)
8723      */
8724     editable: true,
8725     /**
8726      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8727      */
8728     allQuery: '',
8729     /**
8730      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8731      */
8732     mode: 'remote',
8733     /**
8734      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8735      * listWidth has a higher value)
8736      */
8737     minListWidth : 70,
8738     /**
8739      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8740      * allow the user to set arbitrary text into the field (defaults to false)
8741      */
8742     forceSelection:false,
8743     /**
8744      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8745      * if typeAhead = true (defaults to 250)
8746      */
8747     typeAheadDelay : 250,
8748     /**
8749      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8750      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8751      */
8752     valueNotFoundText : undefined,
8753     /**
8754      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8755      */
8756     blockFocus : false,
8757     
8758     /**
8759      * @cfg {Boolean} disableClear Disable showing of clear button.
8760      */
8761     disableClear : false,
8762     /**
8763      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8764      */
8765     alwaysQuery : false,
8766     
8767     /**
8768      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8769      */
8770     multiple : false,
8771     
8772     //private
8773     addicon : false,
8774     editicon: false,
8775     
8776     page: 0,
8777     hasQuery: false,
8778     append: false,
8779     loadNext: false,
8780     item: [],
8781     
8782     // element that contains real text value.. (when hidden is used..)
8783      
8784     // private
8785     initEvents: function(){
8786         
8787         if (!this.store) {
8788             throw "can not find store for combo";
8789         }
8790         this.store = Roo.factory(this.store, Roo.data);
8791         
8792         
8793         
8794         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8795         
8796         
8797         if(this.hiddenName){
8798             
8799             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8800             
8801             this.hiddenField.dom.value =
8802                 this.hiddenValue !== undefined ? this.hiddenValue :
8803                 this.value !== undefined ? this.value : '';
8804
8805             // prevent input submission
8806             this.el.dom.removeAttribute('name');
8807             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8808              
8809              
8810         }
8811         //if(Roo.isGecko){
8812         //    this.el.dom.setAttribute('autocomplete', 'off');
8813         //}
8814
8815         var cls = 'x-combo-list';
8816         this.list = this.el.select('ul.dropdown-menu',true).first();
8817
8818         //this.list = new Roo.Layer({
8819         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8820         //});
8821         
8822         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8823         this.list.setWidth(lw);
8824         
8825         this.list.on('mouseover', this.onViewOver, this);
8826         this.list.on('mousemove', this.onViewMove, this);
8827         
8828         this.list.on('scroll', this.onViewScroll, this);
8829         
8830         /*
8831         this.list.swallowEvent('mousewheel');
8832         this.assetHeight = 0;
8833
8834         if(this.title){
8835             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8836             this.assetHeight += this.header.getHeight();
8837         }
8838
8839         this.innerList = this.list.createChild({cls:cls+'-inner'});
8840         this.innerList.on('mouseover', this.onViewOver, this);
8841         this.innerList.on('mousemove', this.onViewMove, this);
8842         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8843         
8844         if(this.allowBlank && !this.pageSize && !this.disableClear){
8845             this.footer = this.list.createChild({cls:cls+'-ft'});
8846             this.pageTb = new Roo.Toolbar(this.footer);
8847            
8848         }
8849         if(this.pageSize){
8850             this.footer = this.list.createChild({cls:cls+'-ft'});
8851             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8852                     {pageSize: this.pageSize});
8853             
8854         }
8855         
8856         if (this.pageTb && this.allowBlank && !this.disableClear) {
8857             var _this = this;
8858             this.pageTb.add(new Roo.Toolbar.Fill(), {
8859                 cls: 'x-btn-icon x-btn-clear',
8860                 text: '&#160;',
8861                 handler: function()
8862                 {
8863                     _this.collapse();
8864                     _this.clearValue();
8865                     _this.onSelect(false, -1);
8866                 }
8867             });
8868         }
8869         if (this.footer) {
8870             this.assetHeight += this.footer.getHeight();
8871         }
8872         */
8873             
8874         if(!this.tpl){
8875             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8876         }
8877
8878         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8879             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8880         });
8881         //this.view.wrapEl.setDisplayed(false);
8882         this.view.on('click', this.onViewClick, this);
8883         
8884         
8885         
8886         this.store.on('beforeload', this.onBeforeLoad, this);
8887         this.store.on('load', this.onLoad, this);
8888         this.store.on('loadexception', this.onLoadException, this);
8889         /*
8890         if(this.resizable){
8891             this.resizer = new Roo.Resizable(this.list,  {
8892                pinned:true, handles:'se'
8893             });
8894             this.resizer.on('resize', function(r, w, h){
8895                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8896                 this.listWidth = w;
8897                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8898                 this.restrictHeight();
8899             }, this);
8900             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8901         }
8902         */
8903         if(!this.editable){
8904             this.editable = true;
8905             this.setEditable(false);
8906         }
8907         
8908         /*
8909         
8910         if (typeof(this.events.add.listeners) != 'undefined') {
8911             
8912             this.addicon = this.wrap.createChild(
8913                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8914        
8915             this.addicon.on('click', function(e) {
8916                 this.fireEvent('add', this);
8917             }, this);
8918         }
8919         if (typeof(this.events.edit.listeners) != 'undefined') {
8920             
8921             this.editicon = this.wrap.createChild(
8922                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8923             if (this.addicon) {
8924                 this.editicon.setStyle('margin-left', '40px');
8925             }
8926             this.editicon.on('click', function(e) {
8927                 
8928                 // we fire even  if inothing is selected..
8929                 this.fireEvent('edit', this, this.lastData );
8930                 
8931             }, this);
8932         }
8933         */
8934         
8935         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8936             "up" : function(e){
8937                 this.inKeyMode = true;
8938                 this.selectPrev();
8939             },
8940
8941             "down" : function(e){
8942                 if(!this.isExpanded()){
8943                     this.onTriggerClick();
8944                 }else{
8945                     this.inKeyMode = true;
8946                     this.selectNext();
8947                 }
8948             },
8949
8950             "enter" : function(e){
8951                 this.onViewClick();
8952                 //return true;
8953             },
8954
8955             "esc" : function(e){
8956                 this.collapse();
8957             },
8958
8959             "tab" : function(e){
8960                 this.collapse();
8961                 
8962                 if(this.fireEvent("specialkey", this, e)){
8963                     this.onViewClick(false);
8964                 }
8965                 
8966                 return true;
8967             },
8968
8969             scope : this,
8970
8971             doRelay : function(foo, bar, hname){
8972                 if(hname == 'down' || this.scope.isExpanded()){
8973                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8974                 }
8975                 return true;
8976             },
8977
8978             forceKeyDown: true
8979         });
8980         
8981         
8982         this.queryDelay = Math.max(this.queryDelay || 10,
8983                 this.mode == 'local' ? 10 : 250);
8984         
8985         
8986         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8987         
8988         if(this.typeAhead){
8989             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8990         }
8991         if(this.editable !== false){
8992             this.inputEl().on("keyup", this.onKeyUp, this);
8993         }
8994         if(this.forceSelection){
8995             this.on('blur', this.doForce, this);
8996         }
8997         
8998         if(this.multiple){
8999             this.choices = this.el.select('ul.select2-choices', true).first();
9000             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9001         }
9002     },
9003
9004     onDestroy : function(){
9005         if(this.view){
9006             this.view.setStore(null);
9007             this.view.el.removeAllListeners();
9008             this.view.el.remove();
9009             this.view.purgeListeners();
9010         }
9011         if(this.list){
9012             this.list.dom.innerHTML  = '';
9013         }
9014         if(this.store){
9015             this.store.un('beforeload', this.onBeforeLoad, this);
9016             this.store.un('load', this.onLoad, this);
9017             this.store.un('loadexception', this.onLoadException, this);
9018         }
9019         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9020     },
9021
9022     // private
9023     fireKey : function(e){
9024         if(e.isNavKeyPress() && !this.list.isVisible()){
9025             this.fireEvent("specialkey", this, e);
9026         }
9027     },
9028
9029     // private
9030     onResize: function(w, h){
9031 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9032 //        
9033 //        if(typeof w != 'number'){
9034 //            // we do not handle it!?!?
9035 //            return;
9036 //        }
9037 //        var tw = this.trigger.getWidth();
9038 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9039 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9040 //        var x = w - tw;
9041 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9042 //            
9043 //        //this.trigger.setStyle('left', x+'px');
9044 //        
9045 //        if(this.list && this.listWidth === undefined){
9046 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9047 //            this.list.setWidth(lw);
9048 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9049 //        }
9050         
9051     
9052         
9053     },
9054
9055     /**
9056      * Allow or prevent the user from directly editing the field text.  If false is passed,
9057      * the user will only be able to select from the items defined in the dropdown list.  This method
9058      * is the runtime equivalent of setting the 'editable' config option at config time.
9059      * @param {Boolean} value True to allow the user to directly edit the field text
9060      */
9061     setEditable : function(value){
9062         if(value == this.editable){
9063             return;
9064         }
9065         this.editable = value;
9066         if(!value){
9067             this.inputEl().dom.setAttribute('readOnly', true);
9068             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9069             this.inputEl().addClass('x-combo-noedit');
9070         }else{
9071             this.inputEl().dom.setAttribute('readOnly', false);
9072             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9073             this.inputEl().removeClass('x-combo-noedit');
9074         }
9075     },
9076
9077     // private
9078     
9079     onBeforeLoad : function(combo,opts){
9080         if(!this.hasFocus){
9081             return;
9082         }
9083          if (!opts.add) {
9084             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9085          }
9086         this.restrictHeight();
9087         this.selectedIndex = -1;
9088     },
9089
9090     // private
9091     onLoad : function(){
9092         
9093         this.hasQuery = false;
9094         
9095         if(!this.hasFocus){
9096             return;
9097         }
9098         
9099         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9100             this.loading.hide();
9101         }
9102         
9103         if(this.store.getCount() > 0){
9104             this.expand();
9105             this.restrictHeight();
9106             if(this.lastQuery == this.allQuery){
9107                 if(this.editable){
9108                     this.inputEl().dom.select();
9109                 }
9110                 if(!this.selectByValue(this.value, true)){
9111                     this.select(0, true);
9112                 }
9113             }else{
9114                 this.selectNext();
9115                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9116                     this.taTask.delay(this.typeAheadDelay);
9117                 }
9118             }
9119         }else{
9120             this.onEmptyResults();
9121         }
9122         
9123         //this.el.focus();
9124     },
9125     // private
9126     onLoadException : function()
9127     {
9128         this.hasQuery = false;
9129         
9130         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9131             this.loading.hide();
9132         }
9133         
9134         this.collapse();
9135         Roo.log(this.store.reader.jsonData);
9136         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9137             // fixme
9138             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9139         }
9140         
9141         
9142     },
9143     // private
9144     onTypeAhead : function(){
9145         if(this.store.getCount() > 0){
9146             var r = this.store.getAt(0);
9147             var newValue = r.data[this.displayField];
9148             var len = newValue.length;
9149             var selStart = this.getRawValue().length;
9150             
9151             if(selStart != len){
9152                 this.setRawValue(newValue);
9153                 this.selectText(selStart, newValue.length);
9154             }
9155         }
9156     },
9157
9158     // private
9159     onSelect : function(record, index){
9160         
9161         if(this.fireEvent('beforeselect', this, record, index) !== false){
9162         
9163             this.setFromData(index > -1 ? record.data : false);
9164             
9165             this.collapse();
9166             this.fireEvent('select', this, record, index);
9167         }
9168     },
9169
9170     /**
9171      * Returns the currently selected field value or empty string if no value is set.
9172      * @return {String} value The selected value
9173      */
9174     getValue : function(){
9175         
9176         if(this.multiple){
9177             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9178         }
9179         
9180         if(this.valueField){
9181             return typeof this.value != 'undefined' ? this.value : '';
9182         }else{
9183             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9184         }
9185     },
9186
9187     /**
9188      * Clears any text/value currently set in the field
9189      */
9190     clearValue : function(){
9191         if(this.hiddenField){
9192             this.hiddenField.dom.value = '';
9193         }
9194         this.value = '';
9195         this.setRawValue('');
9196         this.lastSelectionText = '';
9197         
9198     },
9199
9200     /**
9201      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9202      * will be displayed in the field.  If the value does not match the data value of an existing item,
9203      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9204      * Otherwise the field will be blank (although the value will still be set).
9205      * @param {String} value The value to match
9206      */
9207     setValue : function(v){
9208         if(this.multiple){
9209             this.syncValue();
9210             return;
9211         }
9212         
9213         var text = v;
9214         if(this.valueField){
9215             var r = this.findRecord(this.valueField, v);
9216             if(r){
9217                 text = r.data[this.displayField];
9218             }else if(this.valueNotFoundText !== undefined){
9219                 text = this.valueNotFoundText;
9220             }
9221         }
9222         this.lastSelectionText = text;
9223         if(this.hiddenField){
9224             this.hiddenField.dom.value = v;
9225         }
9226         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9227         this.value = v;
9228     },
9229     /**
9230      * @property {Object} the last set data for the element
9231      */
9232     
9233     lastData : false,
9234     /**
9235      * Sets the value of the field based on a object which is related to the record format for the store.
9236      * @param {Object} value the value to set as. or false on reset?
9237      */
9238     setFromData : function(o){
9239         
9240         if(this.multiple){
9241             this.addItem(o);
9242             return;
9243         }
9244             
9245         var dv = ''; // display value
9246         var vv = ''; // value value..
9247         this.lastData = o;
9248         if (this.displayField) {
9249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9250         } else {
9251             // this is an error condition!!!
9252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9253         }
9254         
9255         if(this.valueField){
9256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9257         }
9258         
9259         if(this.hiddenField){
9260             this.hiddenField.dom.value = vv;
9261             
9262             this.lastSelectionText = dv;
9263             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9264             this.value = vv;
9265             return;
9266         }
9267         // no hidden field.. - we store the value in 'value', but still display
9268         // display field!!!!
9269         this.lastSelectionText = dv;
9270         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9271         this.value = vv;
9272         
9273         
9274     },
9275     // private
9276     reset : function(){
9277         // overridden so that last data is reset..
9278         this.setValue(this.originalValue);
9279         this.clearInvalid();
9280         this.lastData = false;
9281         if (this.view) {
9282             this.view.clearSelections();
9283         }
9284     },
9285     // private
9286     findRecord : function(prop, value){
9287         var record;
9288         if(this.store.getCount() > 0){
9289             this.store.each(function(r){
9290                 if(r.data[prop] == value){
9291                     record = r;
9292                     return false;
9293                 }
9294                 return true;
9295             });
9296         }
9297         return record;
9298     },
9299     
9300     getName: function()
9301     {
9302         // returns hidden if it's set..
9303         if (!this.rendered) {return ''};
9304         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9305         
9306     },
9307     // private
9308     onViewMove : function(e, t){
9309         this.inKeyMode = false;
9310     },
9311
9312     // private
9313     onViewOver : function(e, t){
9314         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9315             return;
9316         }
9317         var item = this.view.findItemFromChild(t);
9318         if(item){
9319             var index = this.view.indexOf(item);
9320             this.select(index, false);
9321         }
9322     },
9323
9324     // private
9325     onViewClick : function(doFocus)
9326     {
9327         var index = this.view.getSelectedIndexes()[0];
9328         var r = this.store.getAt(index);
9329         if(r){
9330             this.onSelect(r, index);
9331         }
9332         if(doFocus !== false && !this.blockFocus){
9333             this.inputEl().focus();
9334         }
9335     },
9336
9337     // private
9338     restrictHeight : function(){
9339         //this.innerList.dom.style.height = '';
9340         //var inner = this.innerList.dom;
9341         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9342         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9343         //this.list.beginUpdate();
9344         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9345         this.list.alignTo(this.inputEl(), this.listAlign);
9346         //this.list.endUpdate();
9347     },
9348
9349     // private
9350     onEmptyResults : function(){
9351         this.collapse();
9352     },
9353
9354     /**
9355      * Returns true if the dropdown list is expanded, else false.
9356      */
9357     isExpanded : function(){
9358         return this.list.isVisible();
9359     },
9360
9361     /**
9362      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9363      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9364      * @param {String} value The data value of the item to select
9365      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9366      * selected item if it is not currently in view (defaults to true)
9367      * @return {Boolean} True if the value matched an item in the list, else false
9368      */
9369     selectByValue : function(v, scrollIntoView){
9370         if(v !== undefined && v !== null){
9371             var r = this.findRecord(this.valueField || this.displayField, v);
9372             if(r){
9373                 this.select(this.store.indexOf(r), scrollIntoView);
9374                 return true;
9375             }
9376         }
9377         return false;
9378     },
9379
9380     /**
9381      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9382      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9383      * @param {Number} index The zero-based index of the list item to select
9384      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9385      * selected item if it is not currently in view (defaults to true)
9386      */
9387     select : function(index, scrollIntoView){
9388         this.selectedIndex = index;
9389         this.view.select(index);
9390         if(scrollIntoView !== false){
9391             var el = this.view.getNode(index);
9392             if(el){
9393                 //this.innerList.scrollChildIntoView(el, false);
9394                 
9395             }
9396         }
9397     },
9398
9399     // private
9400     selectNext : function(){
9401         var ct = this.store.getCount();
9402         if(ct > 0){
9403             if(this.selectedIndex == -1){
9404                 this.select(0);
9405             }else if(this.selectedIndex < ct-1){
9406                 this.select(this.selectedIndex+1);
9407             }
9408         }
9409     },
9410
9411     // private
9412     selectPrev : function(){
9413         var ct = this.store.getCount();
9414         if(ct > 0){
9415             if(this.selectedIndex == -1){
9416                 this.select(0);
9417             }else if(this.selectedIndex != 0){
9418                 this.select(this.selectedIndex-1);
9419             }
9420         }
9421     },
9422
9423     // private
9424     onKeyUp : function(e){
9425         if(this.editable !== false && !e.isSpecialKey()){
9426             this.lastKey = e.getKey();
9427             this.dqTask.delay(this.queryDelay);
9428         }
9429     },
9430
9431     // private
9432     validateBlur : function(){
9433         return !this.list || !this.list.isVisible();   
9434     },
9435
9436     // private
9437     initQuery : function(){
9438         this.doQuery(this.getRawValue());
9439     },
9440
9441     // private
9442     doForce : function(){
9443         if(this.el.dom.value.length > 0){
9444             this.el.dom.value =
9445                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9446              
9447         }
9448     },
9449
9450     /**
9451      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9452      * query allowing the query action to be canceled if needed.
9453      * @param {String} query The SQL query to execute
9454      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9455      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9456      * saved in the current store (defaults to false)
9457      */
9458     doQuery : function(q, forceAll){
9459         
9460         if(q === undefined || q === null){
9461             q = '';
9462         }
9463         var qe = {
9464             query: q,
9465             forceAll: forceAll,
9466             combo: this,
9467             cancel:false
9468         };
9469         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9470             return false;
9471         }
9472         q = qe.query;
9473         
9474         forceAll = qe.forceAll;
9475         if(forceAll === true || (q.length >= this.minChars)){
9476             
9477             this.hasQuery = true;
9478             
9479             if(this.lastQuery != q || this.alwaysQuery){
9480                 this.lastQuery = q;
9481                 if(this.mode == 'local'){
9482                     this.selectedIndex = -1;
9483                     if(forceAll){
9484                         this.store.clearFilter();
9485                     }else{
9486                         this.store.filter(this.displayField, q);
9487                     }
9488                     this.onLoad();
9489                 }else{
9490                     this.store.baseParams[this.queryParam] = q;
9491                     
9492                     var options = {params : this.getParams(q)};
9493                     
9494                     if(this.loadNext){
9495                         options.add = true;
9496                         options.params.start = this.page * this.pageSize;
9497                     }
9498                     
9499                     this.store.load(options);
9500                     this.expand();
9501                 }
9502             }else{
9503                 this.selectedIndex = -1;
9504                 this.onLoad();   
9505             }
9506         }
9507         
9508         this.loadNext = false;
9509     },
9510
9511     // private
9512     getParams : function(q){
9513         var p = {};
9514         //p[this.queryParam] = q;
9515         
9516         if(this.pageSize){
9517             p.start = 0;
9518             p.limit = this.pageSize;
9519         }
9520         return p;
9521     },
9522
9523     /**
9524      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9525      */
9526     collapse : function(){
9527         if(!this.isExpanded()){
9528             return;
9529         }
9530         
9531         this.list.hide();
9532         Roo.get(document).un('mousedown', this.collapseIf, this);
9533         Roo.get(document).un('mousewheel', this.collapseIf, this);
9534         if (!this.editable) {
9535             Roo.get(document).un('keydown', this.listKeyPress, this);
9536         }
9537         this.fireEvent('collapse', this);
9538     },
9539
9540     // private
9541     collapseIf : function(e){
9542         var in_combo  = e.within(this.el);
9543         var in_list =  e.within(this.list);
9544         
9545         if (in_combo || in_list) {
9546             //e.stopPropagation();
9547             return;
9548         }
9549
9550         this.collapse();
9551         
9552     },
9553
9554     /**
9555      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9556      */
9557     expand : function(){
9558        
9559         if(this.isExpanded() || !this.hasFocus){
9560             return;
9561         }
9562          Roo.log('expand');
9563         this.list.alignTo(this.inputEl(), this.listAlign);
9564         this.list.show();
9565         Roo.get(document).on('mousedown', this.collapseIf, this);
9566         Roo.get(document).on('mousewheel', this.collapseIf, this);
9567         if (!this.editable) {
9568             Roo.get(document).on('keydown', this.listKeyPress, this);
9569         }
9570         
9571         this.fireEvent('expand', this);
9572     },
9573
9574     // private
9575     // Implements the default empty TriggerField.onTriggerClick function
9576     onTriggerClick : function()
9577     {
9578         Roo.log('trigger click');
9579         
9580         if(this.disabled){
9581             return;
9582         }
9583         
9584         this.page = 0;
9585         this.loadNext = false;
9586         
9587         if(this.isExpanded()){
9588             this.collapse();
9589             if (!this.blockFocus) {
9590                 this.inputEl().focus();
9591             }
9592             
9593         }else {
9594             this.hasFocus = true;
9595             if(this.triggerAction == 'all') {
9596                 this.doQuery(this.allQuery, true);
9597             } else {
9598                 this.doQuery(this.getRawValue());
9599             }
9600             if (!this.blockFocus) {
9601                 this.inputEl().focus();
9602             }
9603         }
9604     },
9605     listKeyPress : function(e)
9606     {
9607         //Roo.log('listkeypress');
9608         // scroll to first matching element based on key pres..
9609         if (e.isSpecialKey()) {
9610             return false;
9611         }
9612         var k = String.fromCharCode(e.getKey()).toUpperCase();
9613         //Roo.log(k);
9614         var match  = false;
9615         var csel = this.view.getSelectedNodes();
9616         var cselitem = false;
9617         if (csel.length) {
9618             var ix = this.view.indexOf(csel[0]);
9619             cselitem  = this.store.getAt(ix);
9620             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9621                 cselitem = false;
9622             }
9623             
9624         }
9625         
9626         this.store.each(function(v) { 
9627             if (cselitem) {
9628                 // start at existing selection.
9629                 if (cselitem.id == v.id) {
9630                     cselitem = false;
9631                 }
9632                 return true;
9633             }
9634                 
9635             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9636                 match = this.store.indexOf(v);
9637                 return false;
9638             }
9639             return true;
9640         }, this);
9641         
9642         if (match === false) {
9643             return true; // no more action?
9644         }
9645         // scroll to?
9646         this.view.select(match);
9647         var sn = Roo.get(this.view.getSelectedNodes()[0])
9648         //sn.scrollIntoView(sn.dom.parentNode, false);
9649     },
9650     
9651     onViewScroll : function(e, t){
9652         
9653         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9654             return;
9655         }
9656         
9657         this.hasQuery = true;
9658         
9659         this.loading = this.list.select('.loading', true).first();
9660         
9661         if(this.loading === null){
9662             this.list.createChild({
9663                 tag: 'div',
9664                 cls: 'loading select2-more-results select2-active',
9665                 html: 'Loading more results...'
9666             })
9667             
9668             this.loading = this.list.select('.loading', true).first();
9669             
9670             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9671             
9672             this.loading.hide();
9673         }
9674         
9675         this.loading.show();
9676         
9677         var _combo = this;
9678         
9679         this.page++;
9680         this.loadNext = true;
9681         
9682         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9683         
9684         return;
9685     },
9686     
9687     addItem : function(o)
9688     {   
9689         var dv = ''; // display value
9690         
9691         if (this.displayField) {
9692             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9693         } else {
9694             // this is an error condition!!!
9695             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9696         }
9697         
9698         if(!dv.length){
9699             return;
9700         }
9701         
9702         var choice = this.choices.createChild({
9703             tag: 'li',
9704             cls: 'select2-search-choice',
9705             cn: [
9706                 {
9707                     tag: 'div',
9708                     html: dv
9709                 },
9710                 {
9711                     tag: 'a',
9712                     href: '#',
9713                     cls: 'select2-search-choice-close',
9714                     tabindex: '-1'
9715                 }
9716             ]
9717             
9718         }, this.searchField);
9719         
9720         var close = choice.select('a.select2-search-choice-close', true).first()
9721         
9722         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9723         
9724         this.item.push(o);
9725         this.lastData = o;
9726         
9727         this.syncValue();
9728         
9729         this.inputEl().dom.value = '';
9730         
9731     },
9732     
9733     onRemoveItem : function(e, _self, o)
9734     {
9735         Roo.log('remove item');
9736         var index = this.item.indexOf(o.data) * 1;
9737         
9738         if( index < 0){
9739             Roo.log('not this item?!');
9740             return;
9741         }
9742         
9743         this.item.splice(index, 1);
9744         o.item.remove();
9745         
9746         this.syncValue();
9747         
9748         this.fireEvent('remove', this);
9749         
9750     },
9751     
9752     syncValue : function()
9753     {
9754         if(!this.item.length){
9755             this.clearValue();
9756             return;
9757         }
9758             
9759         var value = [];
9760         var _this = this;
9761         Roo.each(this.item, function(i){
9762             if(_this.valueField){
9763                 value.push(i[_this.valueField]);
9764                 return;
9765             }
9766
9767             value.push(i);
9768         });
9769
9770         this.value = value.join(',');
9771
9772         if(this.hiddenField){
9773             this.hiddenField.dom.value = this.value;
9774         }
9775     },
9776     
9777     clearItem : function()
9778     {
9779         if(!this.multiple){
9780             return;
9781         }
9782         
9783         this.item = [];
9784         
9785         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9786            c.remove();
9787         });
9788         
9789         this.syncValue();
9790     }
9791     
9792     
9793
9794     /** 
9795     * @cfg {Boolean} grow 
9796     * @hide 
9797     */
9798     /** 
9799     * @cfg {Number} growMin 
9800     * @hide 
9801     */
9802     /** 
9803     * @cfg {Number} growMax 
9804     * @hide 
9805     */
9806     /**
9807      * @hide
9808      * @method autoSize
9809      */
9810 });
9811 /*
9812  * Based on:
9813  * Ext JS Library 1.1.1
9814  * Copyright(c) 2006-2007, Ext JS, LLC.
9815  *
9816  * Originally Released Under LGPL - original licence link has changed is not relivant.
9817  *
9818  * Fork - LGPL
9819  * <script type="text/javascript">
9820  */
9821
9822 /**
9823  * @class Roo.View
9824  * @extends Roo.util.Observable
9825  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9826  * This class also supports single and multi selection modes. <br>
9827  * Create a data model bound view:
9828  <pre><code>
9829  var store = new Roo.data.Store(...);
9830
9831  var view = new Roo.View({
9832     el : "my-element",
9833     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9834  
9835     singleSelect: true,
9836     selectedClass: "ydataview-selected",
9837     store: store
9838  });
9839
9840  // listen for node click?
9841  view.on("click", function(vw, index, node, e){
9842  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9843  });
9844
9845  // load XML data
9846  dataModel.load("foobar.xml");
9847  </code></pre>
9848  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9849  * <br><br>
9850  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9851  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9852  * 
9853  * Note: old style constructor is still suported (container, template, config)
9854  * 
9855  * @constructor
9856  * Create a new View
9857  * @param {Object} config The config object
9858  * 
9859  */
9860 Roo.View = function(config, depreciated_tpl, depreciated_config){
9861     
9862     if (typeof(depreciated_tpl) == 'undefined') {
9863         // new way.. - universal constructor.
9864         Roo.apply(this, config);
9865         this.el  = Roo.get(this.el);
9866     } else {
9867         // old format..
9868         this.el  = Roo.get(config);
9869         this.tpl = depreciated_tpl;
9870         Roo.apply(this, depreciated_config);
9871     }
9872     this.wrapEl  = this.el.wrap().wrap();
9873     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9874     
9875     
9876     if(typeof(this.tpl) == "string"){
9877         this.tpl = new Roo.Template(this.tpl);
9878     } else {
9879         // support xtype ctors..
9880         this.tpl = new Roo.factory(this.tpl, Roo);
9881     }
9882     
9883     
9884     this.tpl.compile();
9885    
9886   
9887     
9888      
9889     /** @private */
9890     this.addEvents({
9891         /**
9892          * @event beforeclick
9893          * Fires before a click is processed. Returns false to cancel the default action.
9894          * @param {Roo.View} this
9895          * @param {Number} index The index of the target node
9896          * @param {HTMLElement} node The target node
9897          * @param {Roo.EventObject} e The raw event object
9898          */
9899             "beforeclick" : true,
9900         /**
9901          * @event click
9902          * Fires when a template node is clicked.
9903          * @param {Roo.View} this
9904          * @param {Number} index The index of the target node
9905          * @param {HTMLElement} node The target node
9906          * @param {Roo.EventObject} e The raw event object
9907          */
9908             "click" : true,
9909         /**
9910          * @event dblclick
9911          * Fires when a template node is double clicked.
9912          * @param {Roo.View} this
9913          * @param {Number} index The index of the target node
9914          * @param {HTMLElement} node The target node
9915          * @param {Roo.EventObject} e The raw event object
9916          */
9917             "dblclick" : true,
9918         /**
9919          * @event contextmenu
9920          * Fires when a template node is right clicked.
9921          * @param {Roo.View} this
9922          * @param {Number} index The index of the target node
9923          * @param {HTMLElement} node The target node
9924          * @param {Roo.EventObject} e The raw event object
9925          */
9926             "contextmenu" : true,
9927         /**
9928          * @event selectionchange
9929          * Fires when the selected nodes change.
9930          * @param {Roo.View} this
9931          * @param {Array} selections Array of the selected nodes
9932          */
9933             "selectionchange" : true,
9934     
9935         /**
9936          * @event beforeselect
9937          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9938          * @param {Roo.View} this
9939          * @param {HTMLElement} node The node to be selected
9940          * @param {Array} selections Array of currently selected nodes
9941          */
9942             "beforeselect" : true,
9943         /**
9944          * @event preparedata
9945          * Fires on every row to render, to allow you to change the data.
9946          * @param {Roo.View} this
9947          * @param {Object} data to be rendered (change this)
9948          */
9949           "preparedata" : true
9950           
9951           
9952         });
9953
9954
9955
9956     this.el.on({
9957         "click": this.onClick,
9958         "dblclick": this.onDblClick,
9959         "contextmenu": this.onContextMenu,
9960         scope:this
9961     });
9962
9963     this.selections = [];
9964     this.nodes = [];
9965     this.cmp = new Roo.CompositeElementLite([]);
9966     if(this.store){
9967         this.store = Roo.factory(this.store, Roo.data);
9968         this.setStore(this.store, true);
9969     }
9970     
9971     if ( this.footer && this.footer.xtype) {
9972            
9973          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9974         
9975         this.footer.dataSource = this.store
9976         this.footer.container = fctr;
9977         this.footer = Roo.factory(this.footer, Roo);
9978         fctr.insertFirst(this.el);
9979         
9980         // this is a bit insane - as the paging toolbar seems to detach the el..
9981 //        dom.parentNode.parentNode.parentNode
9982          // they get detached?
9983     }
9984     
9985     
9986     Roo.View.superclass.constructor.call(this);
9987     
9988     
9989 };
9990
9991 Roo.extend(Roo.View, Roo.util.Observable, {
9992     
9993      /**
9994      * @cfg {Roo.data.Store} store Data store to load data from.
9995      */
9996     store : false,
9997     
9998     /**
9999      * @cfg {String|Roo.Element} el The container element.
10000      */
10001     el : '',
10002     
10003     /**
10004      * @cfg {String|Roo.Template} tpl The template used by this View 
10005      */
10006     tpl : false,
10007     /**
10008      * @cfg {String} dataName the named area of the template to use as the data area
10009      *                          Works with domtemplates roo-name="name"
10010      */
10011     dataName: false,
10012     /**
10013      * @cfg {String} selectedClass The css class to add to selected nodes
10014      */
10015     selectedClass : "x-view-selected",
10016      /**
10017      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10018      */
10019     emptyText : "",
10020     
10021     /**
10022      * @cfg {String} text to display on mask (default Loading)
10023      */
10024     mask : false,
10025     /**
10026      * @cfg {Boolean} multiSelect Allow multiple selection
10027      */
10028     multiSelect : false,
10029     /**
10030      * @cfg {Boolean} singleSelect Allow single selection
10031      */
10032     singleSelect:  false,
10033     
10034     /**
10035      * @cfg {Boolean} toggleSelect - selecting 
10036      */
10037     toggleSelect : false,
10038     
10039     /**
10040      * Returns the element this view is bound to.
10041      * @return {Roo.Element}
10042      */
10043     getEl : function(){
10044         return this.wrapEl;
10045     },
10046     
10047     
10048
10049     /**
10050      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10051      */
10052     refresh : function(){
10053         Roo.log('refresh');
10054         var t = this.tpl;
10055         
10056         // if we are using something like 'domtemplate', then
10057         // the what gets used is:
10058         // t.applySubtemplate(NAME, data, wrapping data..)
10059         // the outer template then get' applied with
10060         //     the store 'extra data'
10061         // and the body get's added to the
10062         //      roo-name="data" node?
10063         //      <span class='roo-tpl-{name}'></span> ?????
10064         
10065         
10066         
10067         this.clearSelections();
10068         this.el.update("");
10069         var html = [];
10070         var records = this.store.getRange();
10071         if(records.length < 1) {
10072             
10073             // is this valid??  = should it render a template??
10074             
10075             this.el.update(this.emptyText);
10076             return;
10077         }
10078         var el = this.el;
10079         if (this.dataName) {
10080             this.el.update(t.apply(this.store.meta)); //????
10081             el = this.el.child('.roo-tpl-' + this.dataName);
10082         }
10083         
10084         for(var i = 0, len = records.length; i < len; i++){
10085             var data = this.prepareData(records[i].data, i, records[i]);
10086             this.fireEvent("preparedata", this, data, i, records[i]);
10087             html[html.length] = Roo.util.Format.trim(
10088                 this.dataName ?
10089                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10090                     t.apply(data)
10091             );
10092         }
10093         
10094         
10095         
10096         el.update(html.join(""));
10097         this.nodes = el.dom.childNodes;
10098         this.updateIndexes(0);
10099     },
10100     
10101
10102     /**
10103      * Function to override to reformat the data that is sent to
10104      * the template for each node.
10105      * DEPRICATED - use the preparedata event handler.
10106      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10107      * a JSON object for an UpdateManager bound view).
10108      */
10109     prepareData : function(data, index, record)
10110     {
10111         this.fireEvent("preparedata", this, data, index, record);
10112         return data;
10113     },
10114
10115     onUpdate : function(ds, record){
10116          Roo.log('on update');   
10117         this.clearSelections();
10118         var index = this.store.indexOf(record);
10119         var n = this.nodes[index];
10120         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10121         n.parentNode.removeChild(n);
10122         this.updateIndexes(index, index);
10123     },
10124
10125     
10126     
10127 // --------- FIXME     
10128     onAdd : function(ds, records, index)
10129     {
10130         Roo.log(['on Add', ds, records, index] );        
10131         this.clearSelections();
10132         if(this.nodes.length == 0){
10133             this.refresh();
10134             return;
10135         }
10136         var n = this.nodes[index];
10137         for(var i = 0, len = records.length; i < len; i++){
10138             var d = this.prepareData(records[i].data, i, records[i]);
10139             if(n){
10140                 this.tpl.insertBefore(n, d);
10141             }else{
10142                 
10143                 this.tpl.append(this.el, d);
10144             }
10145         }
10146         this.updateIndexes(index);
10147     },
10148
10149     onRemove : function(ds, record, index){
10150         Roo.log('onRemove');
10151         this.clearSelections();
10152         var el = this.dataName  ?
10153             this.el.child('.roo-tpl-' + this.dataName) :
10154             this.el; 
10155         
10156         el.dom.removeChild(this.nodes[index]);
10157         this.updateIndexes(index);
10158     },
10159
10160     /**
10161      * Refresh an individual node.
10162      * @param {Number} index
10163      */
10164     refreshNode : function(index){
10165         this.onUpdate(this.store, this.store.getAt(index));
10166     },
10167
10168     updateIndexes : function(startIndex, endIndex){
10169         var ns = this.nodes;
10170         startIndex = startIndex || 0;
10171         endIndex = endIndex || ns.length - 1;
10172         for(var i = startIndex; i <= endIndex; i++){
10173             ns[i].nodeIndex = i;
10174         }
10175     },
10176
10177     /**
10178      * Changes the data store this view uses and refresh the view.
10179      * @param {Store} store
10180      */
10181     setStore : function(store, initial){
10182         if(!initial && this.store){
10183             this.store.un("datachanged", this.refresh);
10184             this.store.un("add", this.onAdd);
10185             this.store.un("remove", this.onRemove);
10186             this.store.un("update", this.onUpdate);
10187             this.store.un("clear", this.refresh);
10188             this.store.un("beforeload", this.onBeforeLoad);
10189             this.store.un("load", this.onLoad);
10190             this.store.un("loadexception", this.onLoad);
10191         }
10192         if(store){
10193           
10194             store.on("datachanged", this.refresh, this);
10195             store.on("add", this.onAdd, this);
10196             store.on("remove", this.onRemove, this);
10197             store.on("update", this.onUpdate, this);
10198             store.on("clear", this.refresh, this);
10199             store.on("beforeload", this.onBeforeLoad, this);
10200             store.on("load", this.onLoad, this);
10201             store.on("loadexception", this.onLoad, this);
10202         }
10203         
10204         if(store){
10205             this.refresh();
10206         }
10207     },
10208     /**
10209      * onbeforeLoad - masks the loading area.
10210      *
10211      */
10212     onBeforeLoad : function(store,opts)
10213     {
10214          Roo.log('onBeforeLoad');   
10215         if (!opts.add) {
10216             this.el.update("");
10217         }
10218         this.el.mask(this.mask ? this.mask : "Loading" ); 
10219     },
10220     onLoad : function ()
10221     {
10222         this.el.unmask();
10223     },
10224     
10225
10226     /**
10227      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10228      * @param {HTMLElement} node
10229      * @return {HTMLElement} The template node
10230      */
10231     findItemFromChild : function(node){
10232         var el = this.dataName  ?
10233             this.el.child('.roo-tpl-' + this.dataName,true) :
10234             this.el.dom; 
10235         
10236         if(!node || node.parentNode == el){
10237                     return node;
10238             }
10239             var p = node.parentNode;
10240             while(p && p != el){
10241             if(p.parentNode == el){
10242                 return p;
10243             }
10244             p = p.parentNode;
10245         }
10246             return null;
10247     },
10248
10249     /** @ignore */
10250     onClick : function(e){
10251         var item = this.findItemFromChild(e.getTarget());
10252         if(item){
10253             var index = this.indexOf(item);
10254             if(this.onItemClick(item, index, e) !== false){
10255                 this.fireEvent("click", this, index, item, e);
10256             }
10257         }else{
10258             this.clearSelections();
10259         }
10260     },
10261
10262     /** @ignore */
10263     onContextMenu : function(e){
10264         var item = this.findItemFromChild(e.getTarget());
10265         if(item){
10266             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10267         }
10268     },
10269
10270     /** @ignore */
10271     onDblClick : function(e){
10272         var item = this.findItemFromChild(e.getTarget());
10273         if(item){
10274             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10275         }
10276     },
10277
10278     onItemClick : function(item, index, e)
10279     {
10280         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10281             return false;
10282         }
10283         if (this.toggleSelect) {
10284             var m = this.isSelected(item) ? 'unselect' : 'select';
10285             Roo.log(m);
10286             var _t = this;
10287             _t[m](item, true, false);
10288             return true;
10289         }
10290         if(this.multiSelect || this.singleSelect){
10291             if(this.multiSelect && e.shiftKey && this.lastSelection){
10292                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10293             }else{
10294                 this.select(item, this.multiSelect && e.ctrlKey);
10295                 this.lastSelection = item;
10296             }
10297             e.preventDefault();
10298         }
10299         return true;
10300     },
10301
10302     /**
10303      * Get the number of selected nodes.
10304      * @return {Number}
10305      */
10306     getSelectionCount : function(){
10307         return this.selections.length;
10308     },
10309
10310     /**
10311      * Get the currently selected nodes.
10312      * @return {Array} An array of HTMLElements
10313      */
10314     getSelectedNodes : function(){
10315         return this.selections;
10316     },
10317
10318     /**
10319      * Get the indexes of the selected nodes.
10320      * @return {Array}
10321      */
10322     getSelectedIndexes : function(){
10323         var indexes = [], s = this.selections;
10324         for(var i = 0, len = s.length; i < len; i++){
10325             indexes.push(s[i].nodeIndex);
10326         }
10327         return indexes;
10328     },
10329
10330     /**
10331      * Clear all selections
10332      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10333      */
10334     clearSelections : function(suppressEvent){
10335         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10336             this.cmp.elements = this.selections;
10337             this.cmp.removeClass(this.selectedClass);
10338             this.selections = [];
10339             if(!suppressEvent){
10340                 this.fireEvent("selectionchange", this, this.selections);
10341             }
10342         }
10343     },
10344
10345     /**
10346      * Returns true if the passed node is selected
10347      * @param {HTMLElement/Number} node The node or node index
10348      * @return {Boolean}
10349      */
10350     isSelected : function(node){
10351         var s = this.selections;
10352         if(s.length < 1){
10353             return false;
10354         }
10355         node = this.getNode(node);
10356         return s.indexOf(node) !== -1;
10357     },
10358
10359     /**
10360      * Selects nodes.
10361      * @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
10362      * @param {Boolean} keepExisting (optional) true to keep existing selections
10363      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10364      */
10365     select : function(nodeInfo, keepExisting, suppressEvent){
10366         if(nodeInfo instanceof Array){
10367             if(!keepExisting){
10368                 this.clearSelections(true);
10369             }
10370             for(var i = 0, len = nodeInfo.length; i < len; i++){
10371                 this.select(nodeInfo[i], true, true);
10372             }
10373             return;
10374         } 
10375         var node = this.getNode(nodeInfo);
10376         if(!node || this.isSelected(node)){
10377             return; // already selected.
10378         }
10379         if(!keepExisting){
10380             this.clearSelections(true);
10381         }
10382         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10383             Roo.fly(node).addClass(this.selectedClass);
10384             this.selections.push(node);
10385             if(!suppressEvent){
10386                 this.fireEvent("selectionchange", this, this.selections);
10387             }
10388         }
10389         
10390         
10391     },
10392       /**
10393      * Unselects nodes.
10394      * @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
10395      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10396      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10397      */
10398     unselect : function(nodeInfo, keepExisting, suppressEvent)
10399     {
10400         if(nodeInfo instanceof Array){
10401             Roo.each(this.selections, function(s) {
10402                 this.unselect(s, nodeInfo);
10403             }, this);
10404             return;
10405         }
10406         var node = this.getNode(nodeInfo);
10407         if(!node || !this.isSelected(node)){
10408             Roo.log("not selected");
10409             return; // not selected.
10410         }
10411         // fireevent???
10412         var ns = [];
10413         Roo.each(this.selections, function(s) {
10414             if (s == node ) {
10415                 Roo.fly(node).removeClass(this.selectedClass);
10416
10417                 return;
10418             }
10419             ns.push(s);
10420         },this);
10421         
10422         this.selections= ns;
10423         this.fireEvent("selectionchange", this, this.selections);
10424     },
10425
10426     /**
10427      * Gets a template node.
10428      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10429      * @return {HTMLElement} The node or null if it wasn't found
10430      */
10431     getNode : function(nodeInfo){
10432         if(typeof nodeInfo == "string"){
10433             return document.getElementById(nodeInfo);
10434         }else if(typeof nodeInfo == "number"){
10435             return this.nodes[nodeInfo];
10436         }
10437         return nodeInfo;
10438     },
10439
10440     /**
10441      * Gets a range template nodes.
10442      * @param {Number} startIndex
10443      * @param {Number} endIndex
10444      * @return {Array} An array of nodes
10445      */
10446     getNodes : function(start, end){
10447         var ns = this.nodes;
10448         start = start || 0;
10449         end = typeof end == "undefined" ? ns.length - 1 : end;
10450         var nodes = [];
10451         if(start <= end){
10452             for(var i = start; i <= end; i++){
10453                 nodes.push(ns[i]);
10454             }
10455         } else{
10456             for(var i = start; i >= end; i--){
10457                 nodes.push(ns[i]);
10458             }
10459         }
10460         return nodes;
10461     },
10462
10463     /**
10464      * Finds the index of the passed node
10465      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10466      * @return {Number} The index of the node or -1
10467      */
10468     indexOf : function(node){
10469         node = this.getNode(node);
10470         if(typeof node.nodeIndex == "number"){
10471             return node.nodeIndex;
10472         }
10473         var ns = this.nodes;
10474         for(var i = 0, len = ns.length; i < len; i++){
10475             if(ns[i] == node){
10476                 return i;
10477             }
10478         }
10479         return -1;
10480     }
10481 });
10482 /*
10483  * - LGPL
10484  *
10485  * based on jquery fullcalendar
10486  * 
10487  */
10488
10489 Roo.bootstrap = Roo.bootstrap || {};
10490 /**
10491  * @class Roo.bootstrap.Calendar
10492  * @extends Roo.bootstrap.Component
10493  * Bootstrap Calendar class
10494  * @cfg {Boolean} loadMask (true|false) default false
10495  * @cfg {Object} header generate the user specific header of the calendar, default false
10496
10497  * @constructor
10498  * Create a new Container
10499  * @param {Object} config The config object
10500  */
10501
10502
10503
10504 Roo.bootstrap.Calendar = function(config){
10505     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10506      this.addEvents({
10507         /**
10508              * @event select
10509              * Fires when a date is selected
10510              * @param {DatePicker} this
10511              * @param {Date} date The selected date
10512              */
10513         'select': true,
10514         /**
10515              * @event monthchange
10516              * Fires when the displayed month changes 
10517              * @param {DatePicker} this
10518              * @param {Date} date The selected month
10519              */
10520         'monthchange': true,
10521         /**
10522              * @event evententer
10523              * Fires when mouse over an event
10524              * @param {Calendar} this
10525              * @param {event} Event
10526              */
10527         'evententer': true,
10528         /**
10529              * @event eventleave
10530              * Fires when the mouse leaves an
10531              * @param {Calendar} this
10532              * @param {event}
10533              */
10534         'eventleave': true,
10535         /**
10536              * @event eventclick
10537              * Fires when the mouse click an
10538              * @param {Calendar} this
10539              * @param {event}
10540              */
10541         'eventclick': true
10542         
10543     });
10544
10545 };
10546
10547 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10548     
10549      /**
10550      * @cfg {Number} startDay
10551      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10552      */
10553     startDay : 0,
10554     
10555     loadMask : false,
10556     
10557     header : false,
10558       
10559     getAutoCreate : function(){
10560         
10561         
10562         var fc_button = function(name, corner, style, content ) {
10563             return Roo.apply({},{
10564                 tag : 'span',
10565                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10566                          (corner.length ?
10567                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10568                             ''
10569                         ),
10570                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10571                 unselectable: 'on'
10572             });
10573         };
10574         
10575         var header = {};
10576         
10577         if(!this.header){
10578             header = {
10579                 tag : 'table',
10580                 cls : 'fc-header',
10581                 style : 'width:100%',
10582                 cn : [
10583                     {
10584                         tag: 'tr',
10585                         cn : [
10586                             {
10587                                 tag : 'td',
10588                                 cls : 'fc-header-left',
10589                                 cn : [
10590                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
10591                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
10592                                     { tag: 'span', cls: 'fc-header-space' },
10593                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
10594
10595
10596                                 ]
10597                             },
10598
10599                             {
10600                                 tag : 'td',
10601                                 cls : 'fc-header-center',
10602                                 cn : [
10603                                     {
10604                                         tag: 'span',
10605                                         cls: 'fc-header-title',
10606                                         cn : {
10607                                             tag: 'H2',
10608                                             html : 'month / year'
10609                                         }
10610                                     }
10611
10612                                 ]
10613                             },
10614                             {
10615                                 tag : 'td',
10616                                 cls : 'fc-header-right',
10617                                 cn : [
10618                               /*      fc_button('month', 'left', '', 'month' ),
10619                                     fc_button('week', '', '', 'week' ),
10620                                     fc_button('day', 'right', '', 'day' )
10621                                 */    
10622
10623                                 ]
10624                             }
10625
10626                         ]
10627                     }
10628                 ]
10629             };
10630         }
10631         
10632         header = this.header;
10633         
10634        
10635         var cal_heads = function() {
10636             var ret = [];
10637             // fixme - handle this.
10638             
10639             for (var i =0; i < Date.dayNames.length; i++) {
10640                 var d = Date.dayNames[i];
10641                 ret.push({
10642                     tag: 'th',
10643                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10644                     html : d.substring(0,3)
10645                 });
10646                 
10647             }
10648             ret[0].cls += ' fc-first';
10649             ret[6].cls += ' fc-last';
10650             return ret;
10651         };
10652         var cal_cell = function(n) {
10653             return  {
10654                 tag: 'td',
10655                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10656                 cn : [
10657                     {
10658                         cn : [
10659                             {
10660                                 cls: 'fc-day-number',
10661                                 html: 'D'
10662                             },
10663                             {
10664                                 cls: 'fc-day-content',
10665                              
10666                                 cn : [
10667                                      {
10668                                         style: 'position: relative;' // height: 17px;
10669                                     }
10670                                 ]
10671                             }
10672                             
10673                             
10674                         ]
10675                     }
10676                 ]
10677                 
10678             }
10679         };
10680         var cal_rows = function() {
10681             
10682             var ret = []
10683             for (var r = 0; r < 6; r++) {
10684                 var row= {
10685                     tag : 'tr',
10686                     cls : 'fc-week',
10687                     cn : []
10688                 };
10689                 
10690                 for (var i =0; i < Date.dayNames.length; i++) {
10691                     var d = Date.dayNames[i];
10692                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10693
10694                 }
10695                 row.cn[0].cls+=' fc-first';
10696                 row.cn[0].cn[0].style = 'min-height:90px';
10697                 row.cn[6].cls+=' fc-last';
10698                 ret.push(row);
10699                 
10700             }
10701             ret[0].cls += ' fc-first';
10702             ret[4].cls += ' fc-prev-last';
10703             ret[5].cls += ' fc-last';
10704             return ret;
10705             
10706         };
10707         
10708         var cal_table = {
10709             tag: 'table',
10710             cls: 'fc-border-separate',
10711             style : 'width:100%',
10712             cellspacing  : 0,
10713             cn : [
10714                 { 
10715                     tag: 'thead',
10716                     cn : [
10717                         { 
10718                             tag: 'tr',
10719                             cls : 'fc-first fc-last',
10720                             cn : cal_heads()
10721                         }
10722                     ]
10723                 },
10724                 { 
10725                     tag: 'tbody',
10726                     cn : cal_rows()
10727                 }
10728                   
10729             ]
10730         };
10731          
10732          var cfg = {
10733             cls : 'fc fc-ltr',
10734             cn : [
10735                 header,
10736                 {
10737                     cls : 'fc-content',
10738                     style : "position: relative;",
10739                     cn : [
10740                         {
10741                             cls : 'fc-view fc-view-month fc-grid',
10742                             style : 'position: relative',
10743                             unselectable : 'on',
10744                             cn : [
10745                                 {
10746                                     cls : 'fc-event-container',
10747                                     style : 'position:absolute;z-index:8;top:0;left:0;'
10748                                 },
10749                                 cal_table
10750                             ]
10751                         }
10752                     ]
10753     
10754                 }
10755            ] 
10756             
10757         };
10758         
10759          
10760         
10761         return cfg;
10762     },
10763     
10764     
10765     initEvents : function()
10766     {
10767         if(!this.store){
10768             throw "can not find store for calendar";
10769         }
10770         
10771         var mark = {
10772             tag: "div",
10773             cls:"x-dlg-mask",
10774             style: "text-align:center",
10775             cn: [
10776                 {
10777                     tag: "div",
10778                     style: "background-color:white;width:50%;margin:250 auto",
10779                     cn: [
10780                         {
10781                             tag: "img",
10782                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10783                         },
10784                         {
10785                             tag: "span",
10786                             html: "Loading"
10787                         }
10788                         
10789                     ]
10790                 }
10791             ]
10792         }
10793         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10794         
10795         var size = this.el.select('.fc-content', true).first().getSize();
10796         this.maskEl.setSize(size.width, size.height);
10797         this.maskEl.enableDisplayMode("block");
10798         if(!this.loadMask){
10799             this.maskEl.hide();
10800         }
10801         
10802         this.store = Roo.factory(this.store, Roo.data);
10803         this.store.on('load', this.onLoad, this);
10804         this.store.on('beforeload', this.onBeforeLoad, this);
10805         
10806         this.resize();
10807         
10808         this.cells = this.el.select('.fc-day',true);
10809         //Roo.log(this.cells);
10810         this.textNodes = this.el.query('.fc-day-number');
10811         this.cells.addClassOnOver('fc-state-hover');
10812         
10813         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10814         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10815         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10816         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10817         
10818         this.on('monthchange', this.onMonthChange, this);
10819         
10820         this.update(new Date().clearTime());
10821     },
10822     
10823     resize : function() {
10824         var sz  = this.el.getSize();
10825         
10826         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10827         this.el.select('.fc-day-content div',true).setHeight(34);
10828     },
10829     
10830     
10831     // private
10832     showPrevMonth : function(e){
10833         this.update(this.activeDate.add("mo", -1));
10834     },
10835     showToday : function(e){
10836         this.update(new Date().clearTime());
10837     },
10838     // private
10839     showNextMonth : function(e){
10840         this.update(this.activeDate.add("mo", 1));
10841     },
10842
10843     // private
10844     showPrevYear : function(){
10845         this.update(this.activeDate.add("y", -1));
10846     },
10847
10848     // private
10849     showNextYear : function(){
10850         this.update(this.activeDate.add("y", 1));
10851     },
10852
10853     
10854    // private
10855     update : function(date)
10856     {
10857         var vd = this.activeDate;
10858         this.activeDate = date;
10859 //        if(vd && this.el){
10860 //            var t = date.getTime();
10861 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10862 //                Roo.log('using add remove');
10863 //                
10864 //                this.fireEvent('monthchange', this, date);
10865 //                
10866 //                this.cells.removeClass("fc-state-highlight");
10867 //                this.cells.each(function(c){
10868 //                   if(c.dateValue == t){
10869 //                       c.addClass("fc-state-highlight");
10870 //                       setTimeout(function(){
10871 //                            try{c.dom.firstChild.focus();}catch(e){}
10872 //                       }, 50);
10873 //                       return false;
10874 //                   }
10875 //                   return true;
10876 //                });
10877 //                return;
10878 //            }
10879 //        }
10880         
10881         var days = date.getDaysInMonth();
10882         
10883         var firstOfMonth = date.getFirstDateOfMonth();
10884         var startingPos = firstOfMonth.getDay()-this.startDay;
10885         
10886         if(startingPos < this.startDay){
10887             startingPos += 7;
10888         }
10889         
10890         var pm = date.add(Date.MONTH, -1);
10891         var prevStart = pm.getDaysInMonth()-startingPos;
10892 //        
10893         this.cells = this.el.select('.fc-day',true);
10894         this.textNodes = this.el.query('.fc-day-number');
10895         this.cells.addClassOnOver('fc-state-hover');
10896         
10897         var cells = this.cells.elements;
10898         var textEls = this.textNodes;
10899         
10900         Roo.each(cells, function(cell){
10901             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10902         });
10903         
10904         days += startingPos;
10905
10906         // convert everything to numbers so it's fast
10907         var day = 86400000;
10908         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10909         //Roo.log(d);
10910         //Roo.log(pm);
10911         //Roo.log(prevStart);
10912         
10913         var today = new Date().clearTime().getTime();
10914         var sel = date.clearTime().getTime();
10915         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10916         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10917         var ddMatch = this.disabledDatesRE;
10918         var ddText = this.disabledDatesText;
10919         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10920         var ddaysText = this.disabledDaysText;
10921         var format = this.format;
10922         
10923         var setCellClass = function(cal, cell){
10924             
10925             //Roo.log('set Cell Class');
10926             cell.title = "";
10927             var t = d.getTime();
10928             
10929             //Roo.log(d);
10930             
10931             cell.dateValue = t;
10932             if(t == today){
10933                 cell.className += " fc-today";
10934                 cell.className += " fc-state-highlight";
10935                 cell.title = cal.todayText;
10936             }
10937             if(t == sel){
10938                 // disable highlight in other month..
10939                 //cell.className += " fc-state-highlight";
10940                 
10941             }
10942             // disabling
10943             if(t < min) {
10944                 cell.className = " fc-state-disabled";
10945                 cell.title = cal.minText;
10946                 return;
10947             }
10948             if(t > max) {
10949                 cell.className = " fc-state-disabled";
10950                 cell.title = cal.maxText;
10951                 return;
10952             }
10953             if(ddays){
10954                 if(ddays.indexOf(d.getDay()) != -1){
10955                     cell.title = ddaysText;
10956                     cell.className = " fc-state-disabled";
10957                 }
10958             }
10959             if(ddMatch && format){
10960                 var fvalue = d.dateFormat(format);
10961                 if(ddMatch.test(fvalue)){
10962                     cell.title = ddText.replace("%0", fvalue);
10963                     cell.className = " fc-state-disabled";
10964                 }
10965             }
10966             
10967             if (!cell.initialClassName) {
10968                 cell.initialClassName = cell.dom.className;
10969             }
10970             
10971             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10972         };
10973
10974         var i = 0;
10975         
10976         for(; i < startingPos; i++) {
10977             textEls[i].innerHTML = (++prevStart);
10978             d.setDate(d.getDate()+1);
10979             
10980             cells[i].className = "fc-past fc-other-month";
10981             setCellClass(this, cells[i]);
10982         }
10983         
10984         var intDay = 0;
10985         
10986         for(; i < days; i++){
10987             intDay = i - startingPos + 1;
10988             textEls[i].innerHTML = (intDay);
10989             d.setDate(d.getDate()+1);
10990             
10991             cells[i].className = ''; // "x-date-active";
10992             setCellClass(this, cells[i]);
10993         }
10994         var extraDays = 0;
10995         
10996         for(; i < 42; i++) {
10997             textEls[i].innerHTML = (++extraDays);
10998             d.setDate(d.getDate()+1);
10999             
11000             cells[i].className = "fc-future fc-other-month";
11001             setCellClass(this, cells[i]);
11002         }
11003         
11004         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11005         
11006         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11007         
11008         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11009         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11010         
11011         if(totalRows != 6){
11012             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11013             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11014         }
11015         
11016         this.fireEvent('monthchange', this, date);
11017         
11018         
11019         /*
11020         if(!this.internalRender){
11021             var main = this.el.dom.firstChild;
11022             var w = main.offsetWidth;
11023             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11024             Roo.fly(main).setWidth(w);
11025             this.internalRender = true;
11026             // opera does not respect the auto grow header center column
11027             // then, after it gets a width opera refuses to recalculate
11028             // without a second pass
11029             if(Roo.isOpera && !this.secondPass){
11030                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11031                 this.secondPass = true;
11032                 this.update.defer(10, this, [date]);
11033             }
11034         }
11035         */
11036         
11037     },
11038     
11039     findCell : function(dt) {
11040         dt = dt.clearTime().getTime();
11041         var ret = false;
11042         this.cells.each(function(c){
11043             //Roo.log("check " +c.dateValue + '?=' + dt);
11044             if(c.dateValue == dt){
11045                 ret = c;
11046                 return false;
11047             }
11048             return true;
11049         });
11050         
11051         return ret;
11052     },
11053     
11054     findCells : function(ev) {
11055         var s = ev.start.clone().clearTime().getTime();
11056        // Roo.log(s);
11057         var e= ev.end.clone().clearTime().getTime();
11058        // Roo.log(e);
11059         var ret = [];
11060         this.cells.each(function(c){
11061              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11062             
11063             if(c.dateValue > e){
11064                 return ;
11065             }
11066             if(c.dateValue < s){
11067                 return ;
11068             }
11069             ret.push(c);
11070         });
11071         
11072         return ret;    
11073     },
11074     
11075     findBestRow: function(cells)
11076     {
11077         var ret = 0;
11078         
11079         for (var i =0 ; i < cells.length;i++) {
11080             ret  = Math.max(cells[i].rows || 0,ret);
11081         }
11082         return ret;
11083         
11084     },
11085     
11086     
11087     addItem : function(ev)
11088     {
11089         // look for vertical location slot in
11090         var cells = this.findCells(ev);
11091         
11092         ev.row = this.findBestRow(cells);
11093         
11094         // work out the location.
11095         
11096         var crow = false;
11097         var rows = [];
11098         for(var i =0; i < cells.length; i++) {
11099             if (!crow) {
11100                 crow = {
11101                     start : cells[i],
11102                     end :  cells[i]
11103                 };
11104                 continue;
11105             }
11106             if (crow.start.getY() == cells[i].getY()) {
11107                 // on same row.
11108                 crow.end = cells[i];
11109                 continue;
11110             }
11111             // different row.
11112             rows.push(crow);
11113             crow = {
11114                 start: cells[i],
11115                 end : cells[i]
11116             };
11117             
11118         }
11119         
11120         rows.push(crow);
11121         ev.els = [];
11122         ev.rows = rows;
11123         ev.cells = cells;
11124         for (var i = 0; i < cells.length;i++) {
11125             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11126             
11127         }
11128         
11129         this.calevents.push(ev);
11130     },
11131     
11132     clearEvents: function() {
11133         
11134         if(!this.calevents){
11135             return;
11136         }
11137         
11138         Roo.each(this.cells.elements, function(c){
11139             c.rows = 0;
11140         });
11141         
11142         Roo.each(this.calevents, function(e) {
11143             Roo.each(e.els, function(el) {
11144                 el.un('mouseenter' ,this.onEventEnter, this);
11145                 el.un('mouseleave' ,this.onEventLeave, this);
11146                 el.remove();
11147             },this);
11148         },this);
11149         
11150     },
11151     
11152     renderEvents: function()
11153     {   
11154         // first make sure there is enough space..
11155         
11156         this.cells.each(function(c) {
11157         
11158             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11159         });
11160         
11161         for (var e = 0; e < this.calevents.length; e++) {
11162             var ev = this.calevents[e];
11163             var cells = ev.cells;
11164             var rows = ev.rows;
11165             
11166             for(var i =0; i < rows.length; i++) {
11167                 
11168                  
11169                 // how many rows should it span..
11170                 
11171                 var  cfg = {
11172                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11173                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11174                     
11175                     unselectable : "on",
11176                     cn : [
11177                         {
11178                             cls: 'fc-event-inner',
11179                             cn : [
11180 //                                {
11181 //                                  tag:'span',
11182 //                                  cls: 'fc-event-time',
11183 //                                  html : cells.length > 1 ? '' : ev.time
11184 //                                },
11185                                 {
11186                                   tag:'span',
11187                                   cls: 'fc-event-title',
11188                                   html : String.format('{0}', ev.title)
11189                                 }
11190                                 
11191                                 
11192                             ]
11193                         },
11194                         {
11195                             cls: 'ui-resizable-handle ui-resizable-e',
11196                             html : '&nbsp;&nbsp;&nbsp'
11197                         }
11198                         
11199                     ]
11200                 };
11201                 if (i == 0) {
11202                     cfg.cls += ' fc-event-start';
11203                 }
11204                 if ((i+1) == rows.length) {
11205                     cfg.cls += ' fc-event-end';
11206                 }
11207                 
11208                 var ctr = this.el.select('.fc-event-container',true).first();
11209                 var cg = ctr.createChild(cfg);
11210                 
11211                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11212                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11213                 cg.on('click', this.onEventClick, this, ev);
11214                 
11215                 ev.els.push(cg);
11216                 
11217                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11218                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11219                 //Roo.log(cg);
11220                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
11221                 cg.setWidth(ebox.right - sbox.x -2);
11222             }
11223             
11224             
11225         }
11226         
11227     },
11228     
11229     onEventEnter: function (e, el,event,d) {
11230         this.fireEvent('evententer', this, el, event);
11231     },
11232     
11233     onEventLeave: function (e, el,event,d) {
11234         this.fireEvent('eventleave', this, el, event);
11235     },
11236     
11237     onEventClick: function (e, el,event,d) {
11238         this.fireEvent('eventclick', this, el, event);
11239     },
11240     
11241     onMonthChange: function () {
11242         this.store.load();
11243     },
11244     
11245     onLoad: function () 
11246     {   
11247         this.calevents = [];
11248         var cal = this;
11249         
11250         if(this.store.getCount() > 0){
11251             this.store.data.each(function(d){
11252                cal.addItem({
11253                     id : d.data.id,
11254                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11255                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11256                     time : d.data.start_time,
11257                     title : d.data.title,
11258                     description : d.data.description,
11259                     venue : d.data.venue
11260                 });
11261             });
11262         }
11263         
11264         this.renderEvents();
11265         
11266         if(this.loadMask){
11267             this.maskEl.hide();
11268         }
11269     },
11270     
11271     onBeforeLoad: function()
11272     {
11273         this.clearEvents();
11274         
11275         if(this.loadMask){
11276             this.maskEl.show();
11277         }
11278     }
11279 });
11280
11281  
11282  /*
11283  * - LGPL
11284  *
11285  * element
11286  * 
11287  */
11288
11289 /**
11290  * @class Roo.bootstrap.Popover
11291  * @extends Roo.bootstrap.Component
11292  * Bootstrap Popover class
11293  * @cfg {String} html contents of the popover   (or false to use children..)
11294  * @cfg {String} title of popover (or false to hide)
11295  * @cfg {String} placement how it is placed
11296  * @cfg {String} trigger click || hover (or false to trigger manually)
11297  * @cfg {String} over what (parent or false to trigger manually.)
11298  * 
11299  * @constructor
11300  * Create a new Popover
11301  * @param {Object} config The config object
11302  */
11303
11304 Roo.bootstrap.Popover = function(config){
11305     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11306 };
11307
11308 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11309     
11310     title: 'Fill in a title',
11311     html: false,
11312     
11313     placement : 'right',
11314     trigger : 'hover', // hover
11315     
11316     over: 'parent',
11317     
11318     can_build_overlaid : false,
11319     
11320     getChildContainer : function()
11321     {
11322         return this.el.select('.popover-content',true).first();
11323     },
11324     
11325     getAutoCreate : function(){
11326          Roo.log('make popover?');
11327         var cfg = {
11328            cls : 'popover roo-dynamic',
11329            style: 'display:block',
11330            cn : [
11331                 {
11332                     cls : 'arrow'
11333                 },
11334                 {
11335                     cls : 'popover-inner',
11336                     cn : [
11337                         {
11338                             tag: 'h3',
11339                             cls: 'popover-title',
11340                             html : this.title
11341                         },
11342                         {
11343                             cls : 'popover-content',
11344                             html : this.html
11345                         }
11346                     ]
11347                     
11348                 }
11349            ]
11350         };
11351         
11352         return cfg;
11353     },
11354     setTitle: function(str)
11355     {
11356         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11357     },
11358     setContent: function(str)
11359     {
11360         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11361     },
11362     // as it get's added to the bottom of the page.
11363     onRender : function(ct, position)
11364     {
11365         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11366         if(!this.el){
11367             var cfg = Roo.apply({},  this.getAutoCreate());
11368             cfg.id = Roo.id();
11369             
11370             if (this.cls) {
11371                 cfg.cls += ' ' + this.cls;
11372             }
11373             if (this.style) {
11374                 cfg.style = this.style;
11375             }
11376             Roo.log("adding to ")
11377             this.el = Roo.get(document.body).createChild(cfg, position);
11378             Roo.log(this.el);
11379         }
11380         this.initEvents();
11381     },
11382     
11383     initEvents : function()
11384     {
11385         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11386         this.el.enableDisplayMode('block');
11387         this.el.hide();
11388         if (this.over === false) {
11389             return; 
11390         }
11391         if (this.triggers === false) {
11392             return;
11393         }
11394         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11395         var triggers = this.trigger ? this.trigger.split(' ') : [];
11396         Roo.each(triggers, function(trigger) {
11397         
11398             if (trigger == 'click') {
11399                 on_el.on('click', this.toggle, this);
11400             } else if (trigger != 'manual') {
11401                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
11402                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11403       
11404                 on_el.on(eventIn  ,this.enter, this);
11405                 on_el.on(eventOut, this.leave, this);
11406             }
11407         }, this);
11408         
11409     },
11410     
11411     
11412     // private
11413     timeout : null,
11414     hoverState : null,
11415     
11416     toggle : function () {
11417         this.hoverState == 'in' ? this.leave() : this.enter();
11418     },
11419     
11420     enter : function () {
11421        
11422     
11423         clearTimeout(this.timeout);
11424     
11425         this.hoverState = 'in'
11426     
11427         if (!this.delay || !this.delay.show) {
11428             this.show();
11429             return 
11430         }
11431         var _t = this;
11432         this.timeout = setTimeout(function () {
11433             if (_t.hoverState == 'in') {
11434                 _t.show();
11435             }
11436         }, this.delay.show)
11437     },
11438     leave : function() {
11439         clearTimeout(this.timeout);
11440     
11441         this.hoverState = 'out'
11442     
11443         if (!this.delay || !this.delay.hide) {
11444             this.hide();
11445             return 
11446         }
11447         var _t = this;
11448         this.timeout = setTimeout(function () {
11449             if (_t.hoverState == 'out') {
11450                 _t.hide();
11451             }
11452         }, this.delay.hide)
11453     },
11454     
11455     show : function (on_el)
11456     {
11457         if (!on_el) {
11458             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11459         }
11460         // set content.
11461         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11462         if (this.html !== false) {
11463             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11464         }
11465         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11466         if (!this.title.length) {
11467             this.el.select('.popover-title',true).hide();
11468         }
11469         
11470         var placement = typeof this.placement == 'function' ?
11471             this.placement.call(this, this.el, on_el) :
11472             this.placement;
11473             
11474         var autoToken = /\s?auto?\s?/i;
11475         var autoPlace = autoToken.test(placement);
11476         if (autoPlace) {
11477             placement = placement.replace(autoToken, '') || 'top';
11478         }
11479         
11480         //this.el.detach()
11481         //this.el.setXY([0,0]);
11482         this.el.show();
11483         this.el.dom.style.display='block';
11484         this.el.addClass(placement);
11485         
11486         //this.el.appendTo(on_el);
11487         
11488         var p = this.getPosition();
11489         var box = this.el.getBox();
11490         
11491         if (autoPlace) {
11492             // fixme..
11493         }
11494         var align = Roo.bootstrap.Popover.alignment[placement]
11495         this.el.alignTo(on_el, align[0],align[1]);
11496         //var arrow = this.el.select('.arrow',true).first();
11497         //arrow.set(align[2], 
11498         
11499         this.el.addClass('in');
11500         this.hoverState = null;
11501         
11502         if (this.el.hasClass('fade')) {
11503             // fade it?
11504         }
11505         
11506     },
11507     hide : function()
11508     {
11509         this.el.setXY([0,0]);
11510         this.el.removeClass('in');
11511         this.el.hide();
11512         
11513     }
11514     
11515 });
11516
11517 Roo.bootstrap.Popover.alignment = {
11518     'left' : ['r-l', [-10,0], 'right'],
11519     'right' : ['l-r', [10,0], 'left'],
11520     'bottom' : ['t-b', [0,10], 'top'],
11521     'top' : [ 'b-t', [0,-10], 'bottom']
11522 };
11523
11524  /*
11525  * - LGPL
11526  *
11527  * Progress
11528  * 
11529  */
11530
11531 /**
11532  * @class Roo.bootstrap.Progress
11533  * @extends Roo.bootstrap.Component
11534  * Bootstrap Progress class
11535  * @cfg {Boolean} striped striped of the progress bar
11536  * @cfg {Boolean} active animated of the progress bar
11537  * 
11538  * 
11539  * @constructor
11540  * Create a new Progress
11541  * @param {Object} config The config object
11542  */
11543
11544 Roo.bootstrap.Progress = function(config){
11545     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11546 };
11547
11548 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
11549     
11550     striped : false,
11551     active: false,
11552     
11553     getAutoCreate : function(){
11554         var cfg = {
11555             tag: 'div',
11556             cls: 'progress'
11557         };
11558         
11559         
11560         if(this.striped){
11561             cfg.cls += ' progress-striped';
11562         }
11563       
11564         if(this.active){
11565             cfg.cls += ' active';
11566         }
11567         
11568         
11569         return cfg;
11570     }
11571    
11572 });
11573
11574  
11575
11576  /*
11577  * - LGPL
11578  *
11579  * ProgressBar
11580  * 
11581  */
11582
11583 /**
11584  * @class Roo.bootstrap.ProgressBar
11585  * @extends Roo.bootstrap.Component
11586  * Bootstrap ProgressBar class
11587  * @cfg {Number} aria_valuenow aria-value now
11588  * @cfg {Number} aria_valuemin aria-value min
11589  * @cfg {Number} aria_valuemax aria-value max
11590  * @cfg {String} label label for the progress bar
11591  * @cfg {String} panel (success | info | warning | danger )
11592  * @cfg {String} role role of the progress bar
11593  * @cfg {String} sr_only text
11594  * 
11595  * 
11596  * @constructor
11597  * Create a new ProgressBar
11598  * @param {Object} config The config object
11599  */
11600
11601 Roo.bootstrap.ProgressBar = function(config){
11602     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11603 };
11604
11605 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
11606     
11607     aria_valuenow : 0,
11608     aria_valuemin : 0,
11609     aria_valuemax : 100,
11610     label : false,
11611     panel : false,
11612     role : false,
11613     sr_only: false,
11614     
11615     getAutoCreate : function()
11616     {
11617         
11618         var cfg = {
11619             tag: 'div',
11620             cls: 'progress-bar',
11621             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11622         };
11623         
11624         if(this.sr_only){
11625             cfg.cn = {
11626                 tag: 'span',
11627                 cls: 'sr-only',
11628                 html: this.sr_only
11629             }
11630         }
11631         
11632         if(this.role){
11633             cfg.role = this.role;
11634         }
11635         
11636         if(this.aria_valuenow){
11637             cfg['aria-valuenow'] = this.aria_valuenow;
11638         }
11639         
11640         if(this.aria_valuemin){
11641             cfg['aria-valuemin'] = this.aria_valuemin;
11642         }
11643         
11644         if(this.aria_valuemax){
11645             cfg['aria-valuemax'] = this.aria_valuemax;
11646         }
11647         
11648         if(this.label && !this.sr_only){
11649             cfg.html = this.label;
11650         }
11651         
11652         if(this.panel){
11653             cfg.cls += ' progress-bar-' + this.panel;
11654         }
11655         
11656         return cfg;
11657     },
11658     
11659     update : function(aria_valuenow)
11660     {
11661         this.aria_valuenow = aria_valuenow;
11662         
11663         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11664     }
11665    
11666 });
11667
11668  
11669
11670  /*
11671  * - LGPL
11672  *
11673  * TabPanel
11674  * 
11675  */
11676
11677 /**
11678  * @class Roo.bootstrap.TabPanel
11679  * @extends Roo.bootstrap.Component
11680  * Bootstrap TabPanel class
11681  * @cfg {Boolean} active panel active
11682  * @cfg {String} html panel content
11683  * @cfg {String} tabId tab relate id
11684  * @cfg {String} navId The navbar which triggers show hide
11685  * 
11686  * 
11687  * @constructor
11688  * Create a new TabPanel
11689  * @param {Object} config The config object
11690  */
11691
11692 Roo.bootstrap.TabPanel = function(config){
11693     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11694      this.addEvents({
11695         /**
11696              * @event changed
11697              * Fires when the active status changes
11698              * @param {Roo.bootstrap.TabPanel} this
11699              * @param {Boolean} state the new state
11700             
11701          */
11702         'changed': true
11703      });
11704 };
11705
11706 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
11707     
11708     active: false,
11709     html: false,
11710     tabId: false,
11711     navId : false,
11712     
11713     getAutoCreate : function(){
11714         var cfg = {
11715             tag: 'div',
11716             cls: 'tab-pane',
11717             html: this.html || ''
11718         };
11719         
11720         if(this.active){
11721             cfg.cls += ' active';
11722         }
11723         
11724         if(this.tabId){
11725             cfg.tabId = this.tabId;
11726         }
11727         
11728         return cfg;
11729     },
11730     onRender : function(ct, position)
11731     {
11732        // Roo.log("Call onRender: " + this.xtype);
11733         
11734         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11735         
11736         if (this.navId && this.tabId) {
11737             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11738             if (!item) {
11739                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
11740             } else {
11741                 item.on('changed', function(item, state) {
11742                     this.setActive(state);
11743                 }, this);
11744             }
11745         }
11746         
11747     },
11748     setActive: function(state)
11749     {
11750         Roo.log("panel - set active " + this.tabId + "=" + state);
11751         
11752         this.active = state;
11753         if (!state) {
11754             this.el.removeClass('active');
11755             
11756         } else  if (!this.el.hasClass('active')) {
11757             this.el.addClass('active');
11758         }
11759         this.fireEvent('changed', this, state);
11760     }
11761     
11762     
11763 });
11764  
11765
11766  
11767
11768  /*
11769  * - LGPL
11770  *
11771  * DateField
11772  * 
11773  */
11774
11775 /**
11776  * @class Roo.bootstrap.DateField
11777  * @extends Roo.bootstrap.Input
11778  * Bootstrap DateField class
11779  * @cfg {Number} weekStart default 0
11780  * @cfg {Number} weekStart default 0
11781  * @cfg {Number} viewMode default empty, (months|years)
11782  * @cfg {Number} minViewMode default empty, (months|years)
11783  * @cfg {Number} startDate default -Infinity
11784  * @cfg {Number} endDate default Infinity
11785  * @cfg {Boolean} todayHighlight default false
11786  * @cfg {Boolean} todayBtn default false
11787  * @cfg {Boolean} calendarWeeks default false
11788  * @cfg {Object} daysOfWeekDisabled default empty
11789  * 
11790  * @cfg {Boolean} keyboardNavigation default true
11791  * @cfg {String} language default en
11792  * 
11793  * @constructor
11794  * Create a new DateField
11795  * @param {Object} config The config object
11796  */
11797
11798 Roo.bootstrap.DateField = function(config){
11799     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11800      this.addEvents({
11801             /**
11802              * @event show
11803              * Fires when this field show.
11804              * @param {Roo.bootstrap.DateField} this
11805              * @param {Mixed} date The date value
11806              */
11807             show : true,
11808             /**
11809              * @event show
11810              * Fires when this field hide.
11811              * @param {Roo.bootstrap.DateField} this
11812              * @param {Mixed} date The date value
11813              */
11814             hide : true,
11815             /**
11816              * @event select
11817              * Fires when select a date.
11818              * @param {Roo.bootstrap.DateField} this
11819              * @param {Mixed} date The date value
11820              */
11821             select : true
11822         });
11823 };
11824
11825 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11826     
11827     /**
11828      * @cfg {String} format
11829      * The default date format string which can be overriden for localization support.  The format must be
11830      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11831      */
11832     format : "m/d/y",
11833     /**
11834      * @cfg {String} altFormats
11835      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11836      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11837      */
11838     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11839     
11840     weekStart : 0,
11841     
11842     viewMode : '',
11843     
11844     minViewMode : '',
11845     
11846     todayHighlight : false,
11847     
11848     todayBtn: false,
11849     
11850     language: 'en',
11851     
11852     keyboardNavigation: true,
11853     
11854     calendarWeeks: false,
11855     
11856     startDate: -Infinity,
11857     
11858     endDate: Infinity,
11859     
11860     daysOfWeekDisabled: [],
11861     
11862     _events: [],
11863     
11864     UTCDate: function()
11865     {
11866         return new Date(Date.UTC.apply(Date, arguments));
11867     },
11868     
11869     UTCToday: function()
11870     {
11871         var today = new Date();
11872         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11873     },
11874     
11875     getDate: function() {
11876             var d = this.getUTCDate();
11877             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11878     },
11879     
11880     getUTCDate: function() {
11881             return this.date;
11882     },
11883     
11884     setDate: function(d) {
11885             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11886     },
11887     
11888     setUTCDate: function(d) {
11889             this.date = d;
11890             this.setValue(this.formatDate(this.date));
11891     },
11892         
11893     onRender: function(ct, position)
11894     {
11895         
11896         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11897         
11898         this.language = this.language || 'en';
11899         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11900         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11901         
11902         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11903         this.format = this.format || 'm/d/y';
11904         this.isInline = false;
11905         this.isInput = true;
11906         this.component = this.el.select('.add-on', true).first() || false;
11907         this.component = (this.component && this.component.length === 0) ? false : this.component;
11908         this.hasInput = this.component && this.inputEL().length;
11909         
11910         if (typeof(this.minViewMode === 'string')) {
11911             switch (this.minViewMode) {
11912                 case 'months':
11913                     this.minViewMode = 1;
11914                     break;
11915                 case 'years':
11916                     this.minViewMode = 2;
11917                     break;
11918                 default:
11919                     this.minViewMode = 0;
11920                     break;
11921             }
11922         }
11923         
11924         if (typeof(this.viewMode === 'string')) {
11925             switch (this.viewMode) {
11926                 case 'months':
11927                     this.viewMode = 1;
11928                     break;
11929                 case 'years':
11930                     this.viewMode = 2;
11931                     break;
11932                 default:
11933                     this.viewMode = 0;
11934                     break;
11935             }
11936         }
11937                 
11938         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11939         
11940         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11941         
11942         this.picker().on('mousedown', this.onMousedown, this);
11943         this.picker().on('click', this.onClick, this);
11944         
11945         this.picker().addClass('datepicker-dropdown');
11946         
11947         this.startViewMode = this.viewMode;
11948         
11949         
11950         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11951             if(!this.calendarWeeks){
11952                 v.remove();
11953                 return;
11954             };
11955             
11956             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11957             v.attr('colspan', function(i, val){
11958                 return parseInt(val) + 1;
11959             });
11960         })
11961                         
11962         
11963         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11964         
11965         this.setStartDate(this.startDate);
11966         this.setEndDate(this.endDate);
11967         
11968         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11969         
11970         this.fillDow();
11971         this.fillMonths();
11972         this.update();
11973         this.showMode();
11974         
11975         if(this.isInline) {
11976             this.show();
11977         }
11978     },
11979     
11980     picker : function()
11981     {
11982         return this.el.select('.datepicker', true).first();
11983     },
11984     
11985     fillDow: function()
11986     {
11987         var dowCnt = this.weekStart;
11988         
11989         var dow = {
11990             tag: 'tr',
11991             cn: [
11992                 
11993             ]
11994         };
11995         
11996         if(this.calendarWeeks){
11997             dow.cn.push({
11998                 tag: 'th',
11999                 cls: 'cw',
12000                 html: '&nbsp;'
12001             })
12002         }
12003         
12004         while (dowCnt < this.weekStart + 7) {
12005             dow.cn.push({
12006                 tag: 'th',
12007                 cls: 'dow',
12008                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12009             });
12010         }
12011         
12012         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12013     },
12014     
12015     fillMonths: function()
12016     {    
12017         var i = 0
12018         var months = this.picker().select('>.datepicker-months td', true).first();
12019         
12020         months.dom.innerHTML = '';
12021         
12022         while (i < 12) {
12023             var month = {
12024                 tag: 'span',
12025                 cls: 'month',
12026                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12027             }
12028             
12029             months.createChild(month);
12030         }
12031         
12032     },
12033     
12034     update: function(){
12035         
12036         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12037         
12038         if (this.date < this.startDate) {
12039             this.viewDate = new Date(this.startDate);
12040         } else if (this.date > this.endDate) {
12041             this.viewDate = new Date(this.endDate);
12042         } else {
12043             this.viewDate = new Date(this.date);
12044         }
12045         
12046         this.fill();
12047     },
12048     
12049     fill: function() {
12050         var d = new Date(this.viewDate),
12051                 year = d.getUTCFullYear(),
12052                 month = d.getUTCMonth(),
12053                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12054                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12055                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12056                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12057                 currentDate = this.date && this.date.valueOf(),
12058                 today = this.UTCToday();
12059         
12060         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12061         
12062 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12063         
12064 //        this.picker.select('>tfoot th.today').
12065 //                                              .text(dates[this.language].today)
12066 //                                              .toggle(this.todayBtn !== false);
12067     
12068         this.updateNavArrows();
12069         this.fillMonths();
12070                                                 
12071         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12072         
12073         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12074          
12075         prevMonth.setUTCDate(day);
12076         
12077         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12078         
12079         var nextMonth = new Date(prevMonth);
12080         
12081         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12082         
12083         nextMonth = nextMonth.valueOf();
12084         
12085         var fillMonths = false;
12086         
12087         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12088         
12089         while(prevMonth.valueOf() < nextMonth) {
12090             var clsName = '';
12091             
12092             if (prevMonth.getUTCDay() === this.weekStart) {
12093                 if(fillMonths){
12094                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12095                 }
12096                     
12097                 fillMonths = {
12098                     tag: 'tr',
12099                     cn: []
12100                 };
12101                 
12102                 if(this.calendarWeeks){
12103                     // ISO 8601: First week contains first thursday.
12104                     // ISO also states week starts on Monday, but we can be more abstract here.
12105                     var
12106                     // Start of current week: based on weekstart/current date
12107                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12108                     // Thursday of this week
12109                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12110                     // First Thursday of year, year from thursday
12111                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12112                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12113                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12114                     
12115                     fillMonths.cn.push({
12116                         tag: 'td',
12117                         cls: 'cw',
12118                         html: calWeek
12119                     });
12120                 }
12121             }
12122             
12123             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12124                 clsName += ' old';
12125             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12126                 clsName += ' new';
12127             }
12128             if (this.todayHighlight &&
12129                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12130                 prevMonth.getUTCMonth() == today.getMonth() &&
12131                 prevMonth.getUTCDate() == today.getDate()) {
12132                 clsName += ' today';
12133             }
12134             
12135             if (currentDate && prevMonth.valueOf() === currentDate) {
12136                 clsName += ' active';
12137             }
12138             
12139             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12140                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12141                     clsName += ' disabled';
12142             }
12143             
12144             fillMonths.cn.push({
12145                 tag: 'td',
12146                 cls: 'day ' + clsName,
12147                 html: prevMonth.getDate()
12148             })
12149             
12150             prevMonth.setDate(prevMonth.getDate()+1);
12151         }
12152           
12153         var currentYear = this.date && this.date.getUTCFullYear();
12154         var currentMonth = this.date && this.date.getUTCMonth();
12155         
12156         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12157         
12158         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12159             v.removeClass('active');
12160             
12161             if(currentYear === year && k === currentMonth){
12162                 v.addClass('active');
12163             }
12164             
12165             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12166                 v.addClass('disabled');
12167             }
12168             
12169         });
12170         
12171         
12172         year = parseInt(year/10, 10) * 10;
12173         
12174         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12175         
12176         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12177         
12178         year -= 1;
12179         for (var i = -1; i < 11; i++) {
12180             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12181                 tag: 'span',
12182                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12183                 html: year
12184             })
12185             
12186             year += 1;
12187         }
12188     },
12189     
12190     showMode: function(dir) {
12191         if (dir) {
12192             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12193         }
12194         Roo.each(this.picker().select('>div',true).elements, function(v){
12195             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12196             v.hide();
12197         });
12198         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12199     },
12200     
12201     place: function()
12202     {
12203         if(this.isInline) return;
12204         
12205         this.picker().removeClass(['bottom', 'top']);
12206         
12207         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12208             /*
12209              * place to the top of element!
12210              *
12211              */
12212             
12213             this.picker().addClass('top');
12214             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12215             
12216             return;
12217         }
12218         
12219         this.picker().addClass('bottom');
12220         
12221         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12222     },
12223     
12224     parseDate : function(value){
12225         if(!value || value instanceof Date){
12226             return value;
12227         }
12228         var v = Date.parseDate(value, this.format);
12229         if (!v && this.useIso) {
12230             v = Date.parseDate(value, 'Y-m-d');
12231         }
12232         if(!v && this.altFormats){
12233             if(!this.altFormatsArray){
12234                 this.altFormatsArray = this.altFormats.split("|");
12235             }
12236             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12237                 v = Date.parseDate(value, this.altFormatsArray[i]);
12238             }
12239         }
12240         return v;
12241     },
12242     
12243     formatDate : function(date, fmt){
12244         return (!date || !(date instanceof Date)) ?
12245         date : date.dateFormat(fmt || this.format);
12246     },
12247     
12248     onFocus : function()
12249     {
12250         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12251         this.show();
12252     },
12253     
12254     onBlur : function()
12255     {
12256         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12257         this.hide();
12258     },
12259     
12260     show : function()
12261     {
12262         this.picker().show();
12263         this.update();
12264         this.place();
12265         
12266         this.fireEvent('show', this, this.date);
12267     },
12268     
12269     hide : function()
12270     {
12271         if(this.isInline) return;
12272         this.picker().hide();
12273         this.viewMode = this.startViewMode;
12274         this.showMode();
12275         
12276         this.fireEvent('hide', this, this.date);
12277         
12278     },
12279     
12280     onMousedown: function(e){
12281         e.stopPropagation();
12282         e.preventDefault();
12283     },
12284     
12285     keyup: function(e){
12286         Roo.bootstrap.DateField.superclass.keyup.call(this);
12287         this.update();
12288         
12289     },
12290
12291     setValue: function(v){
12292         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12293         
12294         this.fireEvent('select', this, this.date);
12295         
12296     },
12297     
12298     fireKey: function(e){
12299         if (!this.picker().isVisible()){
12300             if (e.keyCode == 27) // allow escape to hide and re-show picker
12301                 this.show();
12302             return;
12303         }
12304         var dateChanged = false,
12305         dir, day, month,
12306         newDate, newViewDate;
12307         switch(e.keyCode){
12308             case 27: // escape
12309                 this.hide();
12310                 e.preventDefault();
12311                 break;
12312             case 37: // left
12313             case 39: // right
12314                 if (!this.keyboardNavigation) break;
12315                 dir = e.keyCode == 37 ? -1 : 1;
12316                 
12317                 if (e.ctrlKey){
12318                     newDate = this.moveYear(this.date, dir);
12319                     newViewDate = this.moveYear(this.viewDate, dir);
12320                 } else if (e.shiftKey){
12321                     newDate = this.moveMonth(this.date, dir);
12322                     newViewDate = this.moveMonth(this.viewDate, dir);
12323                 } else {
12324                     newDate = new Date(this.date);
12325                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12326                     newViewDate = new Date(this.viewDate);
12327                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12328                 }
12329                 if (this.dateWithinRange(newDate)){
12330                     this.date = newDate;
12331                     this.viewDate = newViewDate;
12332                     this.setValue(this.formatDate(this.date));
12333                     this.update();
12334                     e.preventDefault();
12335                     dateChanged = true;
12336                 }
12337                 break;
12338             case 38: // up
12339             case 40: // down
12340                 if (!this.keyboardNavigation) break;
12341                 dir = e.keyCode == 38 ? -1 : 1;
12342                 if (e.ctrlKey){
12343                     newDate = this.moveYear(this.date, dir);
12344                     newViewDate = this.moveYear(this.viewDate, dir);
12345                 } else if (e.shiftKey){
12346                     newDate = this.moveMonth(this.date, dir);
12347                     newViewDate = this.moveMonth(this.viewDate, dir);
12348                 } else {
12349                     newDate = new Date(this.date);
12350                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12351                     newViewDate = new Date(this.viewDate);
12352                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12353                 }
12354                 if (this.dateWithinRange(newDate)){
12355                     this.date = newDate;
12356                     this.viewDate = newViewDate;
12357                     this.setValue(this.formatDate(this.date));
12358                     this.update();
12359                     e.preventDefault();
12360                     dateChanged = true;
12361                 }
12362                 break;
12363             case 13: // enter
12364                 this.setValue(this.formatDate(this.date));
12365                 this.hide();
12366                 e.preventDefault();
12367                 break;
12368             case 9: // tab
12369                 this.setValue(this.formatDate(this.date));
12370                 this.hide();
12371                 break;
12372         }
12373     },
12374     
12375     
12376     onClick: function(e) {
12377         e.stopPropagation();
12378         e.preventDefault();
12379         
12380         var target = e.getTarget();
12381         
12382         if(target.nodeName.toLowerCase() === 'i'){
12383             target = Roo.get(target).dom.parentNode;
12384         }
12385         
12386         var nodeName = target.nodeName;
12387         var className = target.className;
12388         var html = target.innerHTML;
12389         
12390         switch(nodeName.toLowerCase()) {
12391             case 'th':
12392                 switch(className) {
12393                     case 'switch':
12394                         this.showMode(1);
12395                         break;
12396                     case 'prev':
12397                     case 'next':
12398                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12399                         switch(this.viewMode){
12400                                 case 0:
12401                                         this.viewDate = this.moveMonth(this.viewDate, dir);
12402                                         break;
12403                                 case 1:
12404                                 case 2:
12405                                         this.viewDate = this.moveYear(this.viewDate, dir);
12406                                         break;
12407                         }
12408                         this.fill();
12409                         break;
12410                     case 'today':
12411                         var date = new Date();
12412                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12413                         this.fill()
12414                         this.setValue(this.formatDate(this.date));
12415                         this.hide();
12416                         break;
12417                 }
12418                 break;
12419             case 'span':
12420                 if (className.indexOf('disabled') === -1) {
12421                     this.viewDate.setUTCDate(1);
12422                     if (className.indexOf('month') !== -1) {
12423                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12424                     } else {
12425                         var year = parseInt(html, 10) || 0;
12426                         this.viewDate.setUTCFullYear(year);
12427                         
12428                     }
12429                     this.showMode(-1);
12430                     this.fill();
12431                 }
12432                 break;
12433                 
12434             case 'td':
12435                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12436                     var day = parseInt(html, 10) || 1;
12437                     var year = this.viewDate.getUTCFullYear(),
12438                         month = this.viewDate.getUTCMonth();
12439
12440                     if (className.indexOf('old') !== -1) {
12441                         if(month === 0 ){
12442                             month = 11;
12443                             year -= 1;
12444                         }else{
12445                             month -= 1;
12446                         }
12447                     } else if (className.indexOf('new') !== -1) {
12448                         if (month == 11) {
12449                             month = 0;
12450                             year += 1;
12451                         } else {
12452                             month += 1;
12453                         }
12454                     }
12455                     this.date = this.UTCDate(year, month, day,0,0,0,0);
12456                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12457                     this.fill();
12458                     this.setValue(this.formatDate(this.date));
12459                     this.hide();
12460                 }
12461                 break;
12462         }
12463     },
12464     
12465     setStartDate: function(startDate){
12466         this.startDate = startDate || -Infinity;
12467         if (this.startDate !== -Infinity) {
12468             this.startDate = this.parseDate(this.startDate);
12469         }
12470         this.update();
12471         this.updateNavArrows();
12472     },
12473
12474     setEndDate: function(endDate){
12475         this.endDate = endDate || Infinity;
12476         if (this.endDate !== Infinity) {
12477             this.endDate = this.parseDate(this.endDate);
12478         }
12479         this.update();
12480         this.updateNavArrows();
12481     },
12482     
12483     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12484         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12485         if (typeof(this.daysOfWeekDisabled) !== 'object') {
12486             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12487         }
12488         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12489             return parseInt(d, 10);
12490         });
12491         this.update();
12492         this.updateNavArrows();
12493     },
12494     
12495     updateNavArrows: function() {
12496         var d = new Date(this.viewDate),
12497         year = d.getUTCFullYear(),
12498         month = d.getUTCMonth();
12499         
12500         Roo.each(this.picker().select('.prev', true).elements, function(v){
12501             v.show();
12502             switch (this.viewMode) {
12503                 case 0:
12504
12505                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12506                         v.hide();
12507                     }
12508                     break;
12509                 case 1:
12510                 case 2:
12511                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12512                         v.hide();
12513                     }
12514                     break;
12515             }
12516         });
12517         
12518         Roo.each(this.picker().select('.next', true).elements, function(v){
12519             v.show();
12520             switch (this.viewMode) {
12521                 case 0:
12522
12523                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12524                         v.hide();
12525                     }
12526                     break;
12527                 case 1:
12528                 case 2:
12529                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12530                         v.hide();
12531                     }
12532                     break;
12533             }
12534         })
12535     },
12536     
12537     moveMonth: function(date, dir){
12538         if (!dir) return date;
12539         var new_date = new Date(date.valueOf()),
12540         day = new_date.getUTCDate(),
12541         month = new_date.getUTCMonth(),
12542         mag = Math.abs(dir),
12543         new_month, test;
12544         dir = dir > 0 ? 1 : -1;
12545         if (mag == 1){
12546             test = dir == -1
12547             // If going back one month, make sure month is not current month
12548             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12549             ? function(){
12550                 return new_date.getUTCMonth() == month;
12551             }
12552             // If going forward one month, make sure month is as expected
12553             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12554             : function(){
12555                 return new_date.getUTCMonth() != new_month;
12556             };
12557             new_month = month + dir;
12558             new_date.setUTCMonth(new_month);
12559             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12560             if (new_month < 0 || new_month > 11)
12561                 new_month = (new_month + 12) % 12;
12562         } else {
12563             // For magnitudes >1, move one month at a time...
12564             for (var i=0; i<mag; i++)
12565                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12566                 new_date = this.moveMonth(new_date, dir);
12567             // ...then reset the day, keeping it in the new month
12568             new_month = new_date.getUTCMonth();
12569             new_date.setUTCDate(day);
12570             test = function(){
12571                 return new_month != new_date.getUTCMonth();
12572             };
12573         }
12574         // Common date-resetting loop -- if date is beyond end of month, make it
12575         // end of month
12576         while (test()){
12577             new_date.setUTCDate(--day);
12578             new_date.setUTCMonth(new_month);
12579         }
12580         return new_date;
12581     },
12582
12583     moveYear: function(date, dir){
12584         return this.moveMonth(date, dir*12);
12585     },
12586
12587     dateWithinRange: function(date){
12588         return date >= this.startDate && date <= this.endDate;
12589     },
12590
12591     
12592     remove: function() {
12593         this.picker().remove();
12594     }
12595    
12596 });
12597
12598 Roo.apply(Roo.bootstrap.DateField,  {
12599     
12600     head : {
12601         tag: 'thead',
12602         cn: [
12603         {
12604             tag: 'tr',
12605             cn: [
12606             {
12607                 tag: 'th',
12608                 cls: 'prev',
12609                 html: '<i class="icon-arrow-left"/>'
12610             },
12611             {
12612                 tag: 'th',
12613                 cls: 'switch',
12614                 colspan: '5'
12615             },
12616             {
12617                 tag: 'th',
12618                 cls: 'next',
12619                 html: '<i class="icon-arrow-right"/>'
12620             }
12621
12622             ]
12623         }
12624         ]
12625     },
12626     
12627     content : {
12628         tag: 'tbody',
12629         cn: [
12630         {
12631             tag: 'tr',
12632             cn: [
12633             {
12634                 tag: 'td',
12635                 colspan: '7'
12636             }
12637             ]
12638         }
12639         ]
12640     },
12641     
12642     footer : {
12643         tag: 'tfoot',
12644         cn: [
12645         {
12646             tag: 'tr',
12647             cn: [
12648             {
12649                 tag: 'th',
12650                 colspan: '7',
12651                 cls: 'today'
12652             }
12653                     
12654             ]
12655         }
12656         ]
12657     },
12658     
12659     dates:{
12660         en: {
12661             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12662             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12663             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12664             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12665             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12666             today: "Today"
12667         }
12668     },
12669     
12670     modes: [
12671     {
12672         clsName: 'days',
12673         navFnc: 'Month',
12674         navStep: 1
12675     },
12676     {
12677         clsName: 'months',
12678         navFnc: 'FullYear',
12679         navStep: 1
12680     },
12681     {
12682         clsName: 'years',
12683         navFnc: 'FullYear',
12684         navStep: 10
12685     }]
12686 });
12687
12688 Roo.apply(Roo.bootstrap.DateField,  {
12689   
12690     template : {
12691         tag: 'div',
12692         cls: 'datepicker dropdown-menu',
12693         cn: [
12694         {
12695             tag: 'div',
12696             cls: 'datepicker-days',
12697             cn: [
12698             {
12699                 tag: 'table',
12700                 cls: 'table-condensed',
12701                 cn:[
12702                 Roo.bootstrap.DateField.head,
12703                 {
12704                     tag: 'tbody'
12705                 },
12706                 Roo.bootstrap.DateField.footer
12707                 ]
12708             }
12709             ]
12710         },
12711         {
12712             tag: 'div',
12713             cls: 'datepicker-months',
12714             cn: [
12715             {
12716                 tag: 'table',
12717                 cls: 'table-condensed',
12718                 cn:[
12719                 Roo.bootstrap.DateField.head,
12720                 Roo.bootstrap.DateField.content,
12721                 Roo.bootstrap.DateField.footer
12722                 ]
12723             }
12724             ]
12725         },
12726         {
12727             tag: 'div',
12728             cls: 'datepicker-years',
12729             cn: [
12730             {
12731                 tag: 'table',
12732                 cls: 'table-condensed',
12733                 cn:[
12734                 Roo.bootstrap.DateField.head,
12735                 Roo.bootstrap.DateField.content,
12736                 Roo.bootstrap.DateField.footer
12737                 ]
12738             }
12739             ]
12740         }
12741         ]
12742     }
12743 });
12744
12745  
12746
12747  /*
12748  * - LGPL
12749  *
12750  * TimeField
12751  * 
12752  */
12753
12754 /**
12755  * @class Roo.bootstrap.TimeField
12756  * @extends Roo.bootstrap.Input
12757  * Bootstrap DateField class
12758  * 
12759  * 
12760  * @constructor
12761  * Create a new TimeField
12762  * @param {Object} config The config object
12763  */
12764
12765 Roo.bootstrap.TimeField = function(config){
12766     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12767     this.addEvents({
12768             /**
12769              * @event show
12770              * Fires when this field show.
12771              * @param {Roo.bootstrap.DateField} this
12772              * @param {Mixed} date The date value
12773              */
12774             show : true,
12775             /**
12776              * @event show
12777              * Fires when this field hide.
12778              * @param {Roo.bootstrap.DateField} this
12779              * @param {Mixed} date The date value
12780              */
12781             hide : true,
12782             /**
12783              * @event select
12784              * Fires when select a date.
12785              * @param {Roo.bootstrap.DateField} this
12786              * @param {Mixed} date The date value
12787              */
12788             select : true
12789         });
12790 };
12791
12792 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
12793     
12794     /**
12795      * @cfg {String} format
12796      * The default time format string which can be overriden for localization support.  The format must be
12797      * valid according to {@link Date#parseDate} (defaults to 'H:i').
12798      */
12799     format : "H:i",
12800        
12801     onRender: function(ct, position)
12802     {
12803         
12804         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12805                 
12806         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12807         
12808         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12809         
12810         this.pop = this.picker().select('>.datepicker-time',true).first();
12811         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
12812         
12813         this.picker().on('mousedown', this.onMousedown, this);
12814         this.picker().on('click', this.onClick, this);
12815         
12816         this.picker().addClass('datepicker-dropdown');
12817     
12818         this.fillTime();
12819         this.update();
12820             
12821         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12822         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12823         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12824         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12825         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12826         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12827
12828     },
12829     
12830     fireKey: function(e){
12831         if (!this.picker().isVisible()){
12832             if (e.keyCode == 27) // allow escape to hide and re-show picker
12833                 this.show();
12834             return;
12835         }
12836
12837         e.preventDefault();
12838         
12839         switch(e.keyCode){
12840             case 27: // escape
12841                 this.hide();
12842                 break;
12843             case 37: // left
12844             case 39: // right
12845                 this.onTogglePeriod();
12846                 break;
12847             case 38: // up
12848                 this.onIncrementMinutes();
12849                 break;
12850             case 40: // down
12851                 this.onDecrementMinutes();
12852                 break;
12853             case 13: // enter
12854             case 9: // tab
12855                 this.setTime();
12856                 break;
12857         }
12858     },
12859     
12860     onClick: function(e) {
12861         e.stopPropagation();
12862         e.preventDefault();
12863     },
12864     
12865     picker : function()
12866     {
12867         return this.el.select('.datepicker', true).first();
12868     },
12869     
12870     fillTime: function()
12871     {    
12872         var time = this.pop.select('tbody', true).first();
12873         
12874         time.dom.innerHTML = '';
12875         
12876         time.createChild({
12877             tag: 'tr',
12878             cn: [
12879                 {
12880                     tag: 'td',
12881                     cn: [
12882                         {
12883                             tag: 'a',
12884                             href: '#',
12885                             cls: 'btn',
12886                             cn: [
12887                                 {
12888                                     tag: 'span',
12889                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12890                                 }
12891                             ]
12892                         } 
12893                     ]
12894                 },
12895                 {
12896                     tag: 'td',
12897                     cls: 'separator'
12898                 },
12899                 {
12900                     tag: 'td',
12901                     cn: [
12902                         {
12903                             tag: 'a',
12904                             href: '#',
12905                             cls: 'btn',
12906                             cn: [
12907                                 {
12908                                     tag: 'span',
12909                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12910                                 }
12911                             ]
12912                         }
12913                     ]
12914                 },
12915                 {
12916                     tag: 'td',
12917                     cls: 'separator'
12918                 }
12919             ]
12920         });
12921         
12922         time.createChild({
12923             tag: 'tr',
12924             cn: [
12925                 {
12926                     tag: 'td',
12927                     cn: [
12928                         {
12929                             tag: 'span',
12930                             cls: 'timepicker-hour',
12931                             html: '00'
12932                         }  
12933                     ]
12934                 },
12935                 {
12936                     tag: 'td',
12937                     cls: 'separator',
12938                     html: ':'
12939                 },
12940                 {
12941                     tag: 'td',
12942                     cn: [
12943                         {
12944                             tag: 'span',
12945                             cls: 'timepicker-minute',
12946                             html: '00'
12947                         }  
12948                     ]
12949                 },
12950                 {
12951                     tag: 'td',
12952                     cls: 'separator'
12953                 },
12954                 {
12955                     tag: 'td',
12956                     cn: [
12957                         {
12958                             tag: 'button',
12959                             type: 'button',
12960                             cls: 'btn btn-primary period',
12961                             html: 'AM'
12962                             
12963                         }
12964                     ]
12965                 }
12966             ]
12967         });
12968         
12969         time.createChild({
12970             tag: 'tr',
12971             cn: [
12972                 {
12973                     tag: 'td',
12974                     cn: [
12975                         {
12976                             tag: 'a',
12977                             href: '#',
12978                             cls: 'btn',
12979                             cn: [
12980                                 {
12981                                     tag: 'span',
12982                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12983                                 }
12984                             ]
12985                         }
12986                     ]
12987                 },
12988                 {
12989                     tag: 'td',
12990                     cls: 'separator'
12991                 },
12992                 {
12993                     tag: 'td',
12994                     cn: [
12995                         {
12996                             tag: 'a',
12997                             href: '#',
12998                             cls: 'btn',
12999                             cn: [
13000                                 {
13001                                     tag: 'span',
13002                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13003                                 }
13004                             ]
13005                         }
13006                     ]
13007                 },
13008                 {
13009                     tag: 'td',
13010                     cls: 'separator'
13011                 }
13012             ]
13013         });
13014         
13015     },
13016     
13017     update: function()
13018     {
13019         
13020         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13021         
13022         this.fill();
13023     },
13024     
13025     fill: function() 
13026     {
13027         var hours = this.time.getHours();
13028         var minutes = this.time.getMinutes();
13029         var period = 'AM';
13030         
13031         if(hours > 11){
13032             period = 'PM';
13033         }
13034         
13035         if(hours == 0){
13036             hours = 12;
13037         }
13038         
13039         
13040         if(hours > 12){
13041             hours = hours - 12;
13042         }
13043         
13044         if(hours < 10){
13045             hours = '0' + hours;
13046         }
13047         
13048         if(minutes < 10){
13049             minutes = '0' + minutes;
13050         }
13051         
13052         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13053         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13054         this.pop.select('button', true).first().dom.innerHTML = period;
13055         
13056     },
13057     
13058     place: function()
13059     {   
13060         this.picker().removeClass(['bottom', 'top']);
13061         
13062         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13063             /*
13064              * place to the top of element!
13065              *
13066              */
13067             
13068             this.picker().addClass('top');
13069             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13070             
13071             return;
13072         }
13073         
13074         this.picker().addClass('bottom');
13075         
13076         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13077     },
13078   
13079     onFocus : function()
13080     {
13081         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13082         this.show();
13083     },
13084     
13085     onBlur : function()
13086     {
13087         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13088         this.hide();
13089     },
13090     
13091     show : function()
13092     {
13093         this.picker().show();
13094         this.pop.show();
13095         this.update();
13096         this.place();
13097         
13098         this.fireEvent('show', this, this.date);
13099     },
13100     
13101     hide : function()
13102     {
13103         this.picker().hide();
13104         this.pop.hide();
13105         
13106         this.fireEvent('hide', this, this.date);
13107     },
13108     
13109     setTime : function()
13110     {
13111         this.hide();
13112         this.setValue(this.time.format(this.format));
13113         
13114         this.fireEvent('select', this, this.date);
13115         
13116         
13117     },
13118     
13119     onMousedown: function(e){
13120         e.stopPropagation();
13121         e.preventDefault();
13122     },
13123     
13124     onIncrementHours: function()
13125     {
13126         Roo.log('onIncrementHours');
13127         this.time = this.time.add(Date.HOUR, 1);
13128         this.update();
13129         
13130     },
13131     
13132     onDecrementHours: function()
13133     {
13134         Roo.log('onDecrementHours');
13135         this.time = this.time.add(Date.HOUR, -1);
13136         this.update();
13137     },
13138     
13139     onIncrementMinutes: function()
13140     {
13141         Roo.log('onIncrementMinutes');
13142         this.time = this.time.add(Date.MINUTE, 1);
13143         this.update();
13144     },
13145     
13146     onDecrementMinutes: function()
13147     {
13148         Roo.log('onDecrementMinutes');
13149         this.time = this.time.add(Date.MINUTE, -1);
13150         this.update();
13151     },
13152     
13153     onTogglePeriod: function()
13154     {
13155         Roo.log('onTogglePeriod');
13156         this.time = this.time.add(Date.HOUR, 12);
13157         this.update();
13158     }
13159     
13160    
13161 });
13162
13163 Roo.apply(Roo.bootstrap.TimeField,  {
13164     
13165     content : {
13166         tag: 'tbody',
13167         cn: [
13168             {
13169                 tag: 'tr',
13170                 cn: [
13171                 {
13172                     tag: 'td',
13173                     colspan: '7'
13174                 }
13175                 ]
13176             }
13177         ]
13178     },
13179     
13180     footer : {
13181         tag: 'tfoot',
13182         cn: [
13183             {
13184                 tag: 'tr',
13185                 cn: [
13186                 {
13187                     tag: 'th',
13188                     colspan: '7',
13189                     cls: '',
13190                     cn: [
13191                         {
13192                             tag: 'button',
13193                             cls: 'btn btn-info ok',
13194                             html: 'OK'
13195                         }
13196                     ]
13197                 }
13198
13199                 ]
13200             }
13201         ]
13202     }
13203 });
13204
13205 Roo.apply(Roo.bootstrap.TimeField,  {
13206   
13207     template : {
13208         tag: 'div',
13209         cls: 'datepicker dropdown-menu',
13210         cn: [
13211             {
13212                 tag: 'div',
13213                 cls: 'datepicker-time',
13214                 cn: [
13215                 {
13216                     tag: 'table',
13217                     cls: 'table-condensed',
13218                     cn:[
13219                     Roo.bootstrap.TimeField.content,
13220                     Roo.bootstrap.TimeField.footer
13221                     ]
13222                 }
13223                 ]
13224             }
13225         ]
13226     }
13227 });
13228
13229  
13230
13231  /*
13232  * - LGPL
13233  *
13234  * CheckBox
13235  * 
13236  */
13237
13238 /**
13239  * @class Roo.bootstrap.CheckBox
13240  * @extends Roo.bootstrap.Input
13241  * Bootstrap CheckBox class
13242  * 
13243  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13244  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13245  * @cfg {String} boxLabel The text that appears beside the checkbox
13246  * @cfg {Boolean} checked initnal the element
13247  * 
13248  * @constructor
13249  * Create a new CheckBox
13250  * @param {Object} config The config object
13251  */
13252
13253 Roo.bootstrap.CheckBox = function(config){
13254     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13255    
13256         this.addEvents({
13257             /**
13258             * @event check
13259             * Fires when the element is checked or unchecked.
13260             * @param {Roo.bootstrap.CheckBox} this This input
13261             * @param {Boolean} checked The new checked value
13262             */
13263            check : true
13264         });
13265 };
13266
13267 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13268     
13269     inputType: 'checkbox',
13270     inputValue: 1,
13271     valueOff: 0,
13272     boxLabel: false,
13273     checked: false,
13274     
13275     getAutoCreate : function()
13276     {
13277         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13278         
13279         var id = Roo.id();
13280         
13281         var cfg = {};
13282         
13283         cfg.cls = 'form-group' //input-group
13284         
13285         var input =  {
13286             tag: 'input',
13287             id : id,
13288             type : this.inputType,
13289             value : (!this.checked) ? this.valueOff : this.inputValue,
13290             cls : 'form-box',
13291             placeholder : this.placeholder || ''
13292             
13293         };
13294         
13295         if (this.disabled) {
13296             input.disabled=true;
13297         }
13298         
13299         if(this.checked){
13300             input.checked = this.checked;
13301         }
13302         
13303         if (this.name) {
13304             input.name = this.name;
13305         }
13306         
13307         if (this.size) {
13308             input.cls += ' input-' + this.size;
13309         }
13310         
13311         var settings=this;
13312         ['xs','sm','md','lg'].map(function(size){
13313             if (settings[size]) {
13314                 cfg.cls += ' col-' + size + '-' + settings[size];
13315             }
13316         });
13317         
13318         var inputblock = input;
13319         
13320         if (this.before || this.after) {
13321             
13322             inputblock = {
13323                 cls : 'input-group',
13324                 cn :  [] 
13325             };
13326             if (this.before) {
13327                 inputblock.cn.push({
13328                     tag :'span',
13329                     cls : 'input-group-addon',
13330                     html : this.before
13331                 });
13332             }
13333             inputblock.cn.push(input);
13334             if (this.after) {
13335                 inputblock.cn.push({
13336                     tag :'span',
13337                     cls : 'input-group-addon',
13338                     html : this.after
13339                 });
13340             }
13341             
13342         };
13343         
13344         if (align ==='left' && this.fieldLabel.length) {
13345                 Roo.log("left and has label");
13346                 cfg.cn = [
13347                     
13348                     {
13349                         tag: 'label',
13350                         'for' :  id,
13351                         cls : 'control-label col-md-' + this.labelWidth,
13352                         html : this.fieldLabel
13353                         
13354                     },
13355                     {
13356                         cls : "col-md-" + (12 - this.labelWidth), 
13357                         cn: [
13358                             inputblock
13359                         ]
13360                     }
13361                     
13362                 ];
13363         } else if ( this.fieldLabel.length) {
13364                 Roo.log(" label");
13365                 cfg.cn = [
13366                    
13367                     {
13368                         tag: this.boxLabel ? 'span' : 'label',
13369                         'for': id,
13370                         cls: 'control-label box-input-label',
13371                         //cls : 'input-group-addon',
13372                         html : this.fieldLabel
13373                         
13374                     },
13375                     
13376                     inputblock
13377                     
13378                 ];
13379
13380         } else {
13381             
13382                    Roo.log(" no label && no align");
13383                 cfg.cn = [
13384                     
13385                         inputblock
13386                     
13387                 ];
13388                 
13389                 
13390         };
13391         
13392         if(this.boxLabel){
13393             cfg.cn.push({
13394                 tag: 'label',
13395                 'for': id,
13396                 cls: 'box-label',
13397                 html: this.boxLabel
13398             })
13399         }
13400         
13401         return cfg;
13402         
13403     },
13404     
13405     /**
13406      * return the real input element.
13407      */
13408     inputEl: function ()
13409     {
13410         return this.el.select('input.form-box',true).first();
13411     },
13412     
13413     label: function()
13414     {
13415         return this.el.select('label.control-label',true).first();
13416     },
13417     
13418     initEvents : function()
13419     {
13420 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13421         
13422         this.inputEl().on('click', this.onClick,  this);
13423         
13424     },
13425     
13426     onClick : function()
13427     {   
13428         this.setChecked(!this.checked);
13429     },
13430     
13431     setChecked : function(state,suppressEvent)
13432     {
13433         this.checked = state;
13434         
13435         this.inputEl().dom.checked = state;
13436         
13437         if(suppressEvent !== true){
13438             this.fireEvent('check', this, state);
13439         }
13440         
13441         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13442         
13443     },
13444     
13445     setValue : function(v,suppressEvent)
13446     {
13447         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13448     }
13449     
13450 });
13451
13452  
13453 /*
13454  * - LGPL
13455  *
13456  * Radio
13457  * 
13458  */
13459
13460 /**
13461  * @class Roo.bootstrap.Radio
13462  * @extends Roo.bootstrap.CheckBox
13463  * Bootstrap Radio class
13464
13465  * @constructor
13466  * Create a new Radio
13467  * @param {Object} config The config object
13468  */
13469
13470 Roo.bootstrap.Radio = function(config){
13471     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13472    
13473 };
13474
13475 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
13476     
13477     inputType: 'radio',
13478     inputValue: '',
13479     valueOff: '',
13480     
13481     getAutoCreate : function()
13482     {
13483         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13484         
13485         var id = Roo.id();
13486         
13487         var cfg = {};
13488         
13489         cfg.cls = 'form-group' //input-group
13490         
13491         var input =  {
13492             tag: 'input',
13493             id : id,
13494             type : this.inputType,
13495             value : (!this.checked) ? this.valueOff : this.inputValue,
13496             cls : 'form-box',
13497             placeholder : this.placeholder || ''
13498             
13499         };
13500         
13501         if (this.disabled) {
13502             input.disabled=true;
13503         }
13504         
13505         if(this.checked){
13506             input.checked = this.checked;
13507         }
13508         
13509         if (this.name) {
13510             input.name = this.name;
13511         }
13512         
13513         if (this.size) {
13514             input.cls += ' input-' + this.size;
13515         }
13516         
13517         var settings=this;
13518         ['xs','sm','md','lg'].map(function(size){
13519             if (settings[size]) {
13520                 cfg.cls += ' col-' + size + '-' + settings[size];
13521             }
13522         });
13523         
13524         var inputblock = input;
13525         
13526         if (this.before || this.after) {
13527             
13528             inputblock = {
13529                 cls : 'input-group',
13530                 cn :  [] 
13531             };
13532             if (this.before) {
13533                 inputblock.cn.push({
13534                     tag :'span',
13535                     cls : 'input-group-addon',
13536                     html : this.before
13537                 });
13538             }
13539             inputblock.cn.push(input);
13540             if (this.after) {
13541                 inputblock.cn.push({
13542                     tag :'span',
13543                     cls : 'input-group-addon',
13544                     html : this.after
13545                 });
13546             }
13547             
13548         };
13549         
13550         if (align ==='left' && this.fieldLabel.length) {
13551                 Roo.log("left and has label");
13552                 cfg.cn = [
13553                     
13554                     {
13555                         tag: 'label',
13556                         'for' :  id,
13557                         cls : 'control-label col-md-' + this.labelWidth,
13558                         html : this.fieldLabel
13559                         
13560                     },
13561                     {
13562                         cls : "col-md-" + (12 - this.labelWidth), 
13563                         cn: [
13564                             inputblock
13565                         ]
13566                     }
13567                     
13568                 ];
13569         } else if ( this.fieldLabel.length) {
13570                 Roo.log(" label");
13571                  cfg.cn = [
13572                    
13573                     {
13574                         tag: 'label',
13575                         'for': id,
13576                         cls: 'control-label box-input-label',
13577                         //cls : 'input-group-addon',
13578                         html : this.fieldLabel
13579                         
13580                     },
13581                     
13582                     inputblock
13583                     
13584                 ];
13585
13586         } else {
13587             
13588                    Roo.log(" no label && no align");
13589                 cfg.cn = [
13590                     
13591                         inputblock
13592                     
13593                 ];
13594                 
13595                 
13596         };
13597         
13598         if(this.boxLabel){
13599             cfg.cn.push({
13600                 tag: 'label',
13601                 'for': id,
13602                 cls: 'box-label',
13603                 html: this.boxLabel
13604             })
13605         }
13606         
13607         return cfg;
13608         
13609     },
13610    
13611     onClick : function()
13612     {   
13613         this.setChecked(true);
13614     },
13615     
13616     setChecked : function(state,suppressEvent)
13617     {
13618         if(state){
13619             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13620                 v.dom.checked = false;
13621             });
13622         }
13623         
13624         this.checked = state;
13625         this.inputEl().dom.checked = state;
13626         
13627         if(suppressEvent !== true){
13628             this.fireEvent('check', this, state);
13629         }
13630         
13631         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13632         
13633     },
13634     
13635     getGroupValue : function()
13636     {
13637         var value = ''
13638         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13639             if(v.dom.checked == true){
13640                 value = v.dom.value;
13641             }
13642         });
13643         
13644         return value;
13645     },
13646     
13647     /**
13648      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13649      * @return {Mixed} value The field value
13650      */
13651     getValue : function(){
13652         return this.getGroupValue();
13653     }
13654     
13655 });
13656
13657  
13658 //<script type="text/javascript">
13659
13660 /*
13661  * Based  Ext JS Library 1.1.1
13662  * Copyright(c) 2006-2007, Ext JS, LLC.
13663  * LGPL
13664  *
13665  */
13666  
13667 /**
13668  * @class Roo.HtmlEditorCore
13669  * @extends Roo.Component
13670  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13671  *
13672  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13673  */
13674
13675 Roo.HtmlEditorCore = function(config){
13676     
13677     
13678     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13679     this.addEvents({
13680         /**
13681          * @event initialize
13682          * Fires when the editor is fully initialized (including the iframe)
13683          * @param {Roo.HtmlEditorCore} this
13684          */
13685         initialize: true,
13686         /**
13687          * @event activate
13688          * Fires when the editor is first receives the focus. Any insertion must wait
13689          * until after this event.
13690          * @param {Roo.HtmlEditorCore} this
13691          */
13692         activate: true,
13693          /**
13694          * @event beforesync
13695          * Fires before the textarea is updated with content from the editor iframe. Return false
13696          * to cancel the sync.
13697          * @param {Roo.HtmlEditorCore} this
13698          * @param {String} html
13699          */
13700         beforesync: true,
13701          /**
13702          * @event beforepush
13703          * Fires before the iframe editor is updated with content from the textarea. Return false
13704          * to cancel the push.
13705          * @param {Roo.HtmlEditorCore} this
13706          * @param {String} html
13707          */
13708         beforepush: true,
13709          /**
13710          * @event sync
13711          * Fires when the textarea is updated with content from the editor iframe.
13712          * @param {Roo.HtmlEditorCore} this
13713          * @param {String} html
13714          */
13715         sync: true,
13716          /**
13717          * @event push
13718          * Fires when the iframe editor is updated with content from the textarea.
13719          * @param {Roo.HtmlEditorCore} this
13720          * @param {String} html
13721          */
13722         push: true,
13723         
13724         /**
13725          * @event editorevent
13726          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13727          * @param {Roo.HtmlEditorCore} this
13728          */
13729         editorevent: true
13730     });
13731      
13732 };
13733
13734
13735 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
13736
13737
13738      /**
13739      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
13740      */
13741     
13742     owner : false,
13743     
13744      /**
13745      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13746      *                        Roo.resizable.
13747      */
13748     resizable : false,
13749      /**
13750      * @cfg {Number} height (in pixels)
13751      */   
13752     height: 300,
13753    /**
13754      * @cfg {Number} width (in pixels)
13755      */   
13756     width: 500,
13757     
13758     /**
13759      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13760      * 
13761      */
13762     stylesheets: false,
13763     
13764     // id of frame..
13765     frameId: false,
13766     
13767     // private properties
13768     validationEvent : false,
13769     deferHeight: true,
13770     initialized : false,
13771     activated : false,
13772     sourceEditMode : false,
13773     onFocus : Roo.emptyFn,
13774     iframePad:3,
13775     hideMode:'offsets',
13776     
13777     clearUp: true,
13778     
13779      
13780     
13781
13782     /**
13783      * Protected method that will not generally be called directly. It
13784      * is called when the editor initializes the iframe with HTML contents. Override this method if you
13785      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13786      */
13787     getDocMarkup : function(){
13788         // body styles..
13789         var st = '';
13790         Roo.log(this.stylesheets);
13791         
13792         // inherit styels from page...?? 
13793         if (this.stylesheets === false) {
13794             
13795             Roo.get(document.head).select('style').each(function(node) {
13796                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13797             });
13798             
13799             Roo.get(document.head).select('link').each(function(node) { 
13800                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13801             });
13802             
13803         } else if (!this.stylesheets.length) {
13804                 // simple..
13805                 st = '<style type="text/css">' +
13806                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13807                    '</style>';
13808         } else {
13809             Roo.each(this.stylesheets, function(s) {
13810                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13811             });
13812             
13813         }
13814         
13815         st +=  '<style type="text/css">' +
13816             'IMG { cursor: pointer } ' +
13817         '</style>';
13818
13819         
13820         return '<html><head>' + st  +
13821             //<style type="text/css">' +
13822             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13823             //'</style>' +
13824             ' </head><body class="roo-htmleditor-body"></body></html>';
13825     },
13826
13827     // private
13828     onRender : function(ct, position)
13829     {
13830         var _t = this;
13831         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13832         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13833         
13834         
13835         this.el.dom.style.border = '0 none';
13836         this.el.dom.setAttribute('tabIndex', -1);
13837         this.el.addClass('x-hidden hide');
13838         
13839         
13840         
13841         if(Roo.isIE){ // fix IE 1px bogus margin
13842             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13843         }
13844        
13845         
13846         this.frameId = Roo.id();
13847         
13848          
13849         
13850         var iframe = this.owner.wrap.createChild({
13851             tag: 'iframe',
13852             cls: 'form-control', // bootstrap..
13853             id: this.frameId,
13854             name: this.frameId,
13855             frameBorder : 'no',
13856             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13857         }, this.el
13858         );
13859         
13860         
13861         this.iframe = iframe.dom;
13862
13863          this.assignDocWin();
13864         
13865         this.doc.designMode = 'on';
13866        
13867         this.doc.open();
13868         this.doc.write(this.getDocMarkup());
13869         this.doc.close();
13870
13871         
13872         var task = { // must defer to wait for browser to be ready
13873             run : function(){
13874                 //console.log("run task?" + this.doc.readyState);
13875                 this.assignDocWin();
13876                 if(this.doc.body || this.doc.readyState == 'complete'){
13877                     try {
13878                         this.doc.designMode="on";
13879                     } catch (e) {
13880                         return;
13881                     }
13882                     Roo.TaskMgr.stop(task);
13883                     this.initEditor.defer(10, this);
13884                 }
13885             },
13886             interval : 10,
13887             duration: 10000,
13888             scope: this
13889         };
13890         Roo.TaskMgr.start(task);
13891
13892         
13893          
13894     },
13895
13896     // private
13897     onResize : function(w, h)
13898     {
13899          Roo.log('resize: ' +w + ',' + h );
13900         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13901         if(!this.iframe){
13902             return;
13903         }
13904         if(typeof w == 'number'){
13905             
13906             this.iframe.style.width = w + 'px';
13907         }
13908         if(typeof h == 'number'){
13909             
13910             this.iframe.style.height = h + 'px';
13911             if(this.doc){
13912                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13913             }
13914         }
13915         
13916     },
13917
13918     /**
13919      * Toggles the editor between standard and source edit mode.
13920      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13921      */
13922     toggleSourceEdit : function(sourceEditMode){
13923         
13924         this.sourceEditMode = sourceEditMode === true;
13925         
13926         if(this.sourceEditMode){
13927  
13928             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13929             
13930         }else{
13931             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13932             //this.iframe.className = '';
13933             this.deferFocus();
13934         }
13935         //this.setSize(this.owner.wrap.getSize());
13936         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13937     },
13938
13939     
13940   
13941
13942     /**
13943      * Protected method that will not generally be called directly. If you need/want
13944      * custom HTML cleanup, this is the method you should override.
13945      * @param {String} html The HTML to be cleaned
13946      * return {String} The cleaned HTML
13947      */
13948     cleanHtml : function(html){
13949         html = String(html);
13950         if(html.length > 5){
13951             if(Roo.isSafari){ // strip safari nonsense
13952                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13953             }
13954         }
13955         if(html == '&nbsp;'){
13956             html = '';
13957         }
13958         return html;
13959     },
13960
13961     /**
13962      * HTML Editor -> Textarea
13963      * Protected method that will not generally be called directly. Syncs the contents
13964      * of the editor iframe with the textarea.
13965      */
13966     syncValue : function(){
13967         if(this.initialized){
13968             var bd = (this.doc.body || this.doc.documentElement);
13969             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13970             var html = bd.innerHTML;
13971             if(Roo.isSafari){
13972                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13973                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13974                 if(m && m[1]){
13975                     html = '<div style="'+m[0]+'">' + html + '</div>';
13976                 }
13977             }
13978             html = this.cleanHtml(html);
13979             // fix up the special chars.. normaly like back quotes in word...
13980             // however we do not want to do this with chinese..
13981             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13982                 var cc = b.charCodeAt();
13983                 if (
13984                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13985                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13986                     (cc >= 0xf900 && cc < 0xfb00 )
13987                 ) {
13988                         return b;
13989                 }
13990                 return "&#"+cc+";" 
13991             });
13992             if(this.owner.fireEvent('beforesync', this, html) !== false){
13993                 this.el.dom.value = html;
13994                 this.owner.fireEvent('sync', this, html);
13995             }
13996         }
13997     },
13998
13999     /**
14000      * Protected method that will not generally be called directly. Pushes the value of the textarea
14001      * into the iframe editor.
14002      */
14003     pushValue : function(){
14004         if(this.initialized){
14005             var v = this.el.dom.value.trim();
14006             
14007 //            if(v.length < 1){
14008 //                v = '&#160;';
14009 //            }
14010             
14011             if(this.owner.fireEvent('beforepush', this, v) !== false){
14012                 var d = (this.doc.body || this.doc.documentElement);
14013                 d.innerHTML = v;
14014                 this.cleanUpPaste();
14015                 this.el.dom.value = d.innerHTML;
14016                 this.owner.fireEvent('push', this, v);
14017             }
14018         }
14019     },
14020
14021     // private
14022     deferFocus : function(){
14023         this.focus.defer(10, this);
14024     },
14025
14026     // doc'ed in Field
14027     focus : function(){
14028         if(this.win && !this.sourceEditMode){
14029             this.win.focus();
14030         }else{
14031             this.el.focus();
14032         }
14033     },
14034     
14035     assignDocWin: function()
14036     {
14037         var iframe = this.iframe;
14038         
14039          if(Roo.isIE){
14040             this.doc = iframe.contentWindow.document;
14041             this.win = iframe.contentWindow;
14042         } else {
14043             if (!Roo.get(this.frameId)) {
14044                 return;
14045             }
14046             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14047             this.win = Roo.get(this.frameId).dom.contentWindow;
14048         }
14049     },
14050     
14051     // private
14052     initEditor : function(){
14053         //console.log("INIT EDITOR");
14054         this.assignDocWin();
14055         
14056         
14057         
14058         this.doc.designMode="on";
14059         this.doc.open();
14060         this.doc.write(this.getDocMarkup());
14061         this.doc.close();
14062         
14063         var dbody = (this.doc.body || this.doc.documentElement);
14064         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14065         // this copies styles from the containing element into thsi one..
14066         // not sure why we need all of this..
14067         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14068         ss['background-attachment'] = 'fixed'; // w3c
14069         dbody.bgProperties = 'fixed'; // ie
14070         Roo.DomHelper.applyStyles(dbody, ss);
14071         Roo.EventManager.on(this.doc, {
14072             //'mousedown': this.onEditorEvent,
14073             'mouseup': this.onEditorEvent,
14074             'dblclick': this.onEditorEvent,
14075             'click': this.onEditorEvent,
14076             'keyup': this.onEditorEvent,
14077             buffer:100,
14078             scope: this
14079         });
14080         if(Roo.isGecko){
14081             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14082         }
14083         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14084             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14085         }
14086         this.initialized = true;
14087
14088         this.owner.fireEvent('initialize', this);
14089         this.pushValue();
14090     },
14091
14092     // private
14093     onDestroy : function(){
14094         
14095         
14096         
14097         if(this.rendered){
14098             
14099             //for (var i =0; i < this.toolbars.length;i++) {
14100             //    // fixme - ask toolbars for heights?
14101             //    this.toolbars[i].onDestroy();
14102            // }
14103             
14104             //this.wrap.dom.innerHTML = '';
14105             //this.wrap.remove();
14106         }
14107     },
14108
14109     // private
14110     onFirstFocus : function(){
14111         
14112         this.assignDocWin();
14113         
14114         
14115         this.activated = true;
14116          
14117     
14118         if(Roo.isGecko){ // prevent silly gecko errors
14119             this.win.focus();
14120             var s = this.win.getSelection();
14121             if(!s.focusNode || s.focusNode.nodeType != 3){
14122                 var r = s.getRangeAt(0);
14123                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14124                 r.collapse(true);
14125                 this.deferFocus();
14126             }
14127             try{
14128                 this.execCmd('useCSS', true);
14129                 this.execCmd('styleWithCSS', false);
14130             }catch(e){}
14131         }
14132         this.owner.fireEvent('activate', this);
14133     },
14134
14135     // private
14136     adjustFont: function(btn){
14137         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14138         //if(Roo.isSafari){ // safari
14139         //    adjust *= 2;
14140        // }
14141         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14142         if(Roo.isSafari){ // safari
14143             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14144             v =  (v < 10) ? 10 : v;
14145             v =  (v > 48) ? 48 : v;
14146             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14147             
14148         }
14149         
14150         
14151         v = Math.max(1, v+adjust);
14152         
14153         this.execCmd('FontSize', v  );
14154     },
14155
14156     onEditorEvent : function(e){
14157         this.owner.fireEvent('editorevent', this, e);
14158       //  this.updateToolbar();
14159         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14160     },
14161
14162     insertTag : function(tg)
14163     {
14164         // could be a bit smarter... -> wrap the current selected tRoo..
14165         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14166             
14167             range = this.createRange(this.getSelection());
14168             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14169             wrappingNode.appendChild(range.extractContents());
14170             range.insertNode(wrappingNode);
14171
14172             return;
14173             
14174             
14175             
14176         }
14177         this.execCmd("formatblock",   tg);
14178         
14179     },
14180     
14181     insertText : function(txt)
14182     {
14183         
14184         
14185         var range = this.createRange();
14186         range.deleteContents();
14187                //alert(Sender.getAttribute('label'));
14188                
14189         range.insertNode(this.doc.createTextNode(txt));
14190     } ,
14191     
14192      
14193
14194     /**
14195      * Executes a Midas editor command on the editor document and performs necessary focus and
14196      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14197      * @param {String} cmd The Midas command
14198      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14199      */
14200     relayCmd : function(cmd, value){
14201         this.win.focus();
14202         this.execCmd(cmd, value);
14203         this.owner.fireEvent('editorevent', this);
14204         //this.updateToolbar();
14205         this.owner.deferFocus();
14206     },
14207
14208     /**
14209      * Executes a Midas editor command directly on the editor document.
14210      * For visual commands, you should use {@link #relayCmd} instead.
14211      * <b>This should only be called after the editor is initialized.</b>
14212      * @param {String} cmd The Midas command
14213      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14214      */
14215     execCmd : function(cmd, value){
14216         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14217         this.syncValue();
14218     },
14219  
14220  
14221    
14222     /**
14223      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14224      * to insert tRoo.
14225      * @param {String} text | dom node.. 
14226      */
14227     insertAtCursor : function(text)
14228     {
14229         
14230         
14231         
14232         if(!this.activated){
14233             return;
14234         }
14235         /*
14236         if(Roo.isIE){
14237             this.win.focus();
14238             var r = this.doc.selection.createRange();
14239             if(r){
14240                 r.collapse(true);
14241                 r.pasteHTML(text);
14242                 this.syncValue();
14243                 this.deferFocus();
14244             
14245             }
14246             return;
14247         }
14248         */
14249         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14250             this.win.focus();
14251             
14252             
14253             // from jquery ui (MIT licenced)
14254             var range, node;
14255             var win = this.win;
14256             
14257             if (win.getSelection && win.getSelection().getRangeAt) {
14258                 range = win.getSelection().getRangeAt(0);
14259                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14260                 range.insertNode(node);
14261             } else if (win.document.selection && win.document.selection.createRange) {
14262                 // no firefox support
14263                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14264                 win.document.selection.createRange().pasteHTML(txt);
14265             } else {
14266                 // no firefox support
14267                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14268                 this.execCmd('InsertHTML', txt);
14269             } 
14270             
14271             this.syncValue();
14272             
14273             this.deferFocus();
14274         }
14275     },
14276  // private
14277     mozKeyPress : function(e){
14278         if(e.ctrlKey){
14279             var c = e.getCharCode(), cmd;
14280           
14281             if(c > 0){
14282                 c = String.fromCharCode(c).toLowerCase();
14283                 switch(c){
14284                     case 'b':
14285                         cmd = 'bold';
14286                         break;
14287                     case 'i':
14288                         cmd = 'italic';
14289                         break;
14290                     
14291                     case 'u':
14292                         cmd = 'underline';
14293                         break;
14294                     
14295                     case 'v':
14296                         this.cleanUpPaste.defer(100, this);
14297                         return;
14298                         
14299                 }
14300                 if(cmd){
14301                     this.win.focus();
14302                     this.execCmd(cmd);
14303                     this.deferFocus();
14304                     e.preventDefault();
14305                 }
14306                 
14307             }
14308         }
14309     },
14310
14311     // private
14312     fixKeys : function(){ // load time branching for fastest keydown performance
14313         if(Roo.isIE){
14314             return function(e){
14315                 var k = e.getKey(), r;
14316                 if(k == e.TAB){
14317                     e.stopEvent();
14318                     r = this.doc.selection.createRange();
14319                     if(r){
14320                         r.collapse(true);
14321                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14322                         this.deferFocus();
14323                     }
14324                     return;
14325                 }
14326                 
14327                 if(k == e.ENTER){
14328                     r = this.doc.selection.createRange();
14329                     if(r){
14330                         var target = r.parentElement();
14331                         if(!target || target.tagName.toLowerCase() != 'li'){
14332                             e.stopEvent();
14333                             r.pasteHTML('<br />');
14334                             r.collapse(false);
14335                             r.select();
14336                         }
14337                     }
14338                 }
14339                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14340                     this.cleanUpPaste.defer(100, this);
14341                     return;
14342                 }
14343                 
14344                 
14345             };
14346         }else if(Roo.isOpera){
14347             return function(e){
14348                 var k = e.getKey();
14349                 if(k == e.TAB){
14350                     e.stopEvent();
14351                     this.win.focus();
14352                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
14353                     this.deferFocus();
14354                 }
14355                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14356                     this.cleanUpPaste.defer(100, this);
14357                     return;
14358                 }
14359                 
14360             };
14361         }else if(Roo.isSafari){
14362             return function(e){
14363                 var k = e.getKey();
14364                 
14365                 if(k == e.TAB){
14366                     e.stopEvent();
14367                     this.execCmd('InsertText','\t');
14368                     this.deferFocus();
14369                     return;
14370                 }
14371                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14372                     this.cleanUpPaste.defer(100, this);
14373                     return;
14374                 }
14375                 
14376              };
14377         }
14378     }(),
14379     
14380     getAllAncestors: function()
14381     {
14382         var p = this.getSelectedNode();
14383         var a = [];
14384         if (!p) {
14385             a.push(p); // push blank onto stack..
14386             p = this.getParentElement();
14387         }
14388         
14389         
14390         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14391             a.push(p);
14392             p = p.parentNode;
14393         }
14394         a.push(this.doc.body);
14395         return a;
14396     },
14397     lastSel : false,
14398     lastSelNode : false,
14399     
14400     
14401     getSelection : function() 
14402     {
14403         this.assignDocWin();
14404         return Roo.isIE ? this.doc.selection : this.win.getSelection();
14405     },
14406     
14407     getSelectedNode: function() 
14408     {
14409         // this may only work on Gecko!!!
14410         
14411         // should we cache this!!!!
14412         
14413         
14414         
14415          
14416         var range = this.createRange(this.getSelection()).cloneRange();
14417         
14418         if (Roo.isIE) {
14419             var parent = range.parentElement();
14420             while (true) {
14421                 var testRange = range.duplicate();
14422                 testRange.moveToElementText(parent);
14423                 if (testRange.inRange(range)) {
14424                     break;
14425                 }
14426                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14427                     break;
14428                 }
14429                 parent = parent.parentElement;
14430             }
14431             return parent;
14432         }
14433         
14434         // is ancestor a text element.
14435         var ac =  range.commonAncestorContainer;
14436         if (ac.nodeType == 3) {
14437             ac = ac.parentNode;
14438         }
14439         
14440         var ar = ac.childNodes;
14441          
14442         var nodes = [];
14443         var other_nodes = [];
14444         var has_other_nodes = false;
14445         for (var i=0;i<ar.length;i++) {
14446             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
14447                 continue;
14448             }
14449             // fullly contained node.
14450             
14451             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14452                 nodes.push(ar[i]);
14453                 continue;
14454             }
14455             
14456             // probably selected..
14457             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14458                 other_nodes.push(ar[i]);
14459                 continue;
14460             }
14461             // outer..
14462             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
14463                 continue;
14464             }
14465             
14466             
14467             has_other_nodes = true;
14468         }
14469         if (!nodes.length && other_nodes.length) {
14470             nodes= other_nodes;
14471         }
14472         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14473             return false;
14474         }
14475         
14476         return nodes[0];
14477     },
14478     createRange: function(sel)
14479     {
14480         // this has strange effects when using with 
14481         // top toolbar - not sure if it's a great idea.
14482         //this.editor.contentWindow.focus();
14483         if (typeof sel != "undefined") {
14484             try {
14485                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14486             } catch(e) {
14487                 return this.doc.createRange();
14488             }
14489         } else {
14490             return this.doc.createRange();
14491         }
14492     },
14493     getParentElement: function()
14494     {
14495         
14496         this.assignDocWin();
14497         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14498         
14499         var range = this.createRange(sel);
14500          
14501         try {
14502             var p = range.commonAncestorContainer;
14503             while (p.nodeType == 3) { // text node
14504                 p = p.parentNode;
14505             }
14506             return p;
14507         } catch (e) {
14508             return null;
14509         }
14510     
14511     },
14512     /***
14513      *
14514      * Range intersection.. the hard stuff...
14515      *  '-1' = before
14516      *  '0' = hits..
14517      *  '1' = after.
14518      *         [ -- selected range --- ]
14519      *   [fail]                        [fail]
14520      *
14521      *    basically..
14522      *      if end is before start or  hits it. fail.
14523      *      if start is after end or hits it fail.
14524      *
14525      *   if either hits (but other is outside. - then it's not 
14526      *   
14527      *    
14528      **/
14529     
14530     
14531     // @see http://www.thismuchiknow.co.uk/?p=64.
14532     rangeIntersectsNode : function(range, node)
14533     {
14534         var nodeRange = node.ownerDocument.createRange();
14535         try {
14536             nodeRange.selectNode(node);
14537         } catch (e) {
14538             nodeRange.selectNodeContents(node);
14539         }
14540     
14541         var rangeStartRange = range.cloneRange();
14542         rangeStartRange.collapse(true);
14543     
14544         var rangeEndRange = range.cloneRange();
14545         rangeEndRange.collapse(false);
14546     
14547         var nodeStartRange = nodeRange.cloneRange();
14548         nodeStartRange.collapse(true);
14549     
14550         var nodeEndRange = nodeRange.cloneRange();
14551         nodeEndRange.collapse(false);
14552     
14553         return rangeStartRange.compareBoundaryPoints(
14554                  Range.START_TO_START, nodeEndRange) == -1 &&
14555                rangeEndRange.compareBoundaryPoints(
14556                  Range.START_TO_START, nodeStartRange) == 1;
14557         
14558          
14559     },
14560     rangeCompareNode : function(range, node)
14561     {
14562         var nodeRange = node.ownerDocument.createRange();
14563         try {
14564             nodeRange.selectNode(node);
14565         } catch (e) {
14566             nodeRange.selectNodeContents(node);
14567         }
14568         
14569         
14570         range.collapse(true);
14571     
14572         nodeRange.collapse(true);
14573      
14574         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14575         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
14576          
14577         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14578         
14579         var nodeIsBefore   =  ss == 1;
14580         var nodeIsAfter    = ee == -1;
14581         
14582         if (nodeIsBefore && nodeIsAfter)
14583             return 0; // outer
14584         if (!nodeIsBefore && nodeIsAfter)
14585             return 1; //right trailed.
14586         
14587         if (nodeIsBefore && !nodeIsAfter)
14588             return 2;  // left trailed.
14589         // fully contined.
14590         return 3;
14591     },
14592
14593     // private? - in a new class?
14594     cleanUpPaste :  function()
14595     {
14596         // cleans up the whole document..
14597         Roo.log('cleanuppaste');
14598         
14599         this.cleanUpChildren(this.doc.body);
14600         var clean = this.cleanWordChars(this.doc.body.innerHTML);
14601         if (clean != this.doc.body.innerHTML) {
14602             this.doc.body.innerHTML = clean;
14603         }
14604         
14605     },
14606     
14607     cleanWordChars : function(input) {// change the chars to hex code
14608         var he = Roo.HtmlEditorCore;
14609         
14610         var output = input;
14611         Roo.each(he.swapCodes, function(sw) { 
14612             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14613             
14614             output = output.replace(swapper, sw[1]);
14615         });
14616         
14617         return output;
14618     },
14619     
14620     
14621     cleanUpChildren : function (n)
14622     {
14623         if (!n.childNodes.length) {
14624             return;
14625         }
14626         for (var i = n.childNodes.length-1; i > -1 ; i--) {
14627            this.cleanUpChild(n.childNodes[i]);
14628         }
14629     },
14630     
14631     
14632         
14633     
14634     cleanUpChild : function (node)
14635     {
14636         var ed = this;
14637         //console.log(node);
14638         if (node.nodeName == "#text") {
14639             // clean up silly Windows -- stuff?
14640             return; 
14641         }
14642         if (node.nodeName == "#comment") {
14643             node.parentNode.removeChild(node);
14644             // clean up silly Windows -- stuff?
14645             return; 
14646         }
14647         
14648         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14649             // remove node.
14650             node.parentNode.removeChild(node);
14651             return;
14652             
14653         }
14654         
14655         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14656         
14657         // remove <a name=....> as rendering on yahoo mailer is borked with this.
14658         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14659         
14660         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14661         //    remove_keep_children = true;
14662         //}
14663         
14664         if (remove_keep_children) {
14665             this.cleanUpChildren(node);
14666             // inserts everything just before this node...
14667             while (node.childNodes.length) {
14668                 var cn = node.childNodes[0];
14669                 node.removeChild(cn);
14670                 node.parentNode.insertBefore(cn, node);
14671             }
14672             node.parentNode.removeChild(node);
14673             return;
14674         }
14675         
14676         if (!node.attributes || !node.attributes.length) {
14677             this.cleanUpChildren(node);
14678             return;
14679         }
14680         
14681         function cleanAttr(n,v)
14682         {
14683             
14684             if (v.match(/^\./) || v.match(/^\//)) {
14685                 return;
14686             }
14687             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14688                 return;
14689             }
14690             if (v.match(/^#/)) {
14691                 return;
14692             }
14693 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14694             node.removeAttribute(n);
14695             
14696         }
14697         
14698         function cleanStyle(n,v)
14699         {
14700             if (v.match(/expression/)) { //XSS?? should we even bother..
14701                 node.removeAttribute(n);
14702                 return;
14703             }
14704             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14705             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14706             
14707             
14708             var parts = v.split(/;/);
14709             var clean = [];
14710             
14711             Roo.each(parts, function(p) {
14712                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14713                 if (!p.length) {
14714                     return true;
14715                 }
14716                 var l = p.split(':').shift().replace(/\s+/g,'');
14717                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14718                 
14719                 if ( cblack.indexOf(l) > -1) {
14720 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14721                     //node.removeAttribute(n);
14722                     return true;
14723                 }
14724                 //Roo.log()
14725                 // only allow 'c whitelisted system attributes'
14726                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
14727 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14728                     //node.removeAttribute(n);
14729                     return true;
14730                 }
14731                 
14732                 
14733                  
14734                 
14735                 clean.push(p);
14736                 return true;
14737             });
14738             if (clean.length) { 
14739                 node.setAttribute(n, clean.join(';'));
14740             } else {
14741                 node.removeAttribute(n);
14742             }
14743             
14744         }
14745         
14746         
14747         for (var i = node.attributes.length-1; i > -1 ; i--) {
14748             var a = node.attributes[i];
14749             //console.log(a);
14750             
14751             if (a.name.toLowerCase().substr(0,2)=='on')  {
14752                 node.removeAttribute(a.name);
14753                 continue;
14754             }
14755             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14756                 node.removeAttribute(a.name);
14757                 continue;
14758             }
14759             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14760                 cleanAttr(a.name,a.value); // fixme..
14761                 continue;
14762             }
14763             if (a.name == 'style') {
14764                 cleanStyle(a.name,a.value);
14765                 continue;
14766             }
14767             /// clean up MS crap..
14768             // tecnically this should be a list of valid class'es..
14769             
14770             
14771             if (a.name == 'class') {
14772                 if (a.value.match(/^Mso/)) {
14773                     node.className = '';
14774                 }
14775                 
14776                 if (a.value.match(/body/)) {
14777                     node.className = '';
14778                 }
14779                 continue;
14780             }
14781             
14782             // style cleanup!?
14783             // class cleanup?
14784             
14785         }
14786         
14787         
14788         this.cleanUpChildren(node);
14789         
14790         
14791     }
14792     
14793     
14794     // hide stuff that is not compatible
14795     /**
14796      * @event blur
14797      * @hide
14798      */
14799     /**
14800      * @event change
14801      * @hide
14802      */
14803     /**
14804      * @event focus
14805      * @hide
14806      */
14807     /**
14808      * @event specialkey
14809      * @hide
14810      */
14811     /**
14812      * @cfg {String} fieldClass @hide
14813      */
14814     /**
14815      * @cfg {String} focusClass @hide
14816      */
14817     /**
14818      * @cfg {String} autoCreate @hide
14819      */
14820     /**
14821      * @cfg {String} inputType @hide
14822      */
14823     /**
14824      * @cfg {String} invalidClass @hide
14825      */
14826     /**
14827      * @cfg {String} invalidText @hide
14828      */
14829     /**
14830      * @cfg {String} msgFx @hide
14831      */
14832     /**
14833      * @cfg {String} validateOnBlur @hide
14834      */
14835 });
14836
14837 Roo.HtmlEditorCore.white = [
14838         'area', 'br', 'img', 'input', 'hr', 'wbr',
14839         
14840        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14841        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14842        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14843        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14844        'table',   'ul',         'xmp', 
14845        
14846        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14847       'thead',   'tr', 
14848      
14849       'dir', 'menu', 'ol', 'ul', 'dl',
14850        
14851       'embed',  'object'
14852 ];
14853
14854
14855 Roo.HtmlEditorCore.black = [
14856     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14857         'applet', // 
14858         'base',   'basefont', 'bgsound', 'blink',  'body', 
14859         'frame',  'frameset', 'head',    'html',   'ilayer', 
14860         'iframe', 'layer',  'link',     'meta',    'object',   
14861         'script', 'style' ,'title',  'xml' // clean later..
14862 ];
14863 Roo.HtmlEditorCore.clean = [
14864     'script', 'style', 'title', 'xml'
14865 ];
14866 Roo.HtmlEditorCore.remove = [
14867     'font'
14868 ];
14869 // attributes..
14870
14871 Roo.HtmlEditorCore.ablack = [
14872     'on'
14873 ];
14874     
14875 Roo.HtmlEditorCore.aclean = [ 
14876     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14877 ];
14878
14879 // protocols..
14880 Roo.HtmlEditorCore.pwhite= [
14881         'http',  'https',  'mailto'
14882 ];
14883
14884 // white listed style attributes.
14885 Roo.HtmlEditorCore.cwhite= [
14886       //  'text-align', /// default is to allow most things..
14887       
14888          
14889 //        'font-size'//??
14890 ];
14891
14892 // black listed style attributes.
14893 Roo.HtmlEditorCore.cblack= [
14894       //  'font-size' -- this can be set by the project 
14895 ];
14896
14897
14898 Roo.HtmlEditorCore.swapCodes   =[ 
14899     [    8211, "--" ], 
14900     [    8212, "--" ], 
14901     [    8216,  "'" ],  
14902     [    8217, "'" ],  
14903     [    8220, '"' ],  
14904     [    8221, '"' ],  
14905     [    8226, "*" ],  
14906     [    8230, "..." ]
14907 ]; 
14908
14909     /*
14910  * - LGPL
14911  *
14912  * HtmlEditor
14913  * 
14914  */
14915
14916 /**
14917  * @class Roo.bootstrap.HtmlEditor
14918  * @extends Roo.bootstrap.TextArea
14919  * Bootstrap HtmlEditor class
14920
14921  * @constructor
14922  * Create a new HtmlEditor
14923  * @param {Object} config The config object
14924  */
14925
14926 Roo.bootstrap.HtmlEditor = function(config){
14927     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14928     if (!this.toolbars) {
14929         this.toolbars = [];
14930     }
14931     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14932     this.addEvents({
14933             /**
14934              * @event initialize
14935              * Fires when the editor is fully initialized (including the iframe)
14936              * @param {HtmlEditor} this
14937              */
14938             initialize: true,
14939             /**
14940              * @event activate
14941              * Fires when the editor is first receives the focus. Any insertion must wait
14942              * until after this event.
14943              * @param {HtmlEditor} this
14944              */
14945             activate: true,
14946              /**
14947              * @event beforesync
14948              * Fires before the textarea is updated with content from the editor iframe. Return false
14949              * to cancel the sync.
14950              * @param {HtmlEditor} this
14951              * @param {String} html
14952              */
14953             beforesync: true,
14954              /**
14955              * @event beforepush
14956              * Fires before the iframe editor is updated with content from the textarea. Return false
14957              * to cancel the push.
14958              * @param {HtmlEditor} this
14959              * @param {String} html
14960              */
14961             beforepush: true,
14962              /**
14963              * @event sync
14964              * Fires when the textarea is updated with content from the editor iframe.
14965              * @param {HtmlEditor} this
14966              * @param {String} html
14967              */
14968             sync: true,
14969              /**
14970              * @event push
14971              * Fires when the iframe editor is updated with content from the textarea.
14972              * @param {HtmlEditor} this
14973              * @param {String} html
14974              */
14975             push: true,
14976              /**
14977              * @event editmodechange
14978              * Fires when the editor switches edit modes
14979              * @param {HtmlEditor} this
14980              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14981              */
14982             editmodechange: true,
14983             /**
14984              * @event editorevent
14985              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14986              * @param {HtmlEditor} this
14987              */
14988             editorevent: true,
14989             /**
14990              * @event firstfocus
14991              * Fires when on first focus - needed by toolbars..
14992              * @param {HtmlEditor} this
14993              */
14994             firstfocus: true,
14995             /**
14996              * @event autosave
14997              * Auto save the htmlEditor value as a file into Events
14998              * @param {HtmlEditor} this
14999              */
15000             autosave: true,
15001             /**
15002              * @event savedpreview
15003              * preview the saved version of htmlEditor
15004              * @param {HtmlEditor} this
15005              */
15006             savedpreview: true
15007         });
15008 };
15009
15010
15011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15012     
15013     
15014       /**
15015      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15016      */
15017     toolbars : false,
15018    
15019      /**
15020      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15021      *                        Roo.resizable.
15022      */
15023     resizable : false,
15024      /**
15025      * @cfg {Number} height (in pixels)
15026      */   
15027     height: 300,
15028    /**
15029      * @cfg {Number} width (in pixels)
15030      */   
15031     width: false,
15032     
15033     /**
15034      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15035      * 
15036      */
15037     stylesheets: false,
15038     
15039     // id of frame..
15040     frameId: false,
15041     
15042     // private properties
15043     validationEvent : false,
15044     deferHeight: true,
15045     initialized : false,
15046     activated : false,
15047     
15048     onFocus : Roo.emptyFn,
15049     iframePad:3,
15050     hideMode:'offsets',
15051     
15052     
15053     tbContainer : false,
15054     
15055     toolbarContainer :function() {
15056         return this.wrap.select('.x-html-editor-tb',true).first();
15057     },
15058
15059     /**
15060      * Protected method that will not generally be called directly. It
15061      * is called when the editor creates its toolbar. Override this method if you need to
15062      * add custom toolbar buttons.
15063      * @param {HtmlEditor} editor
15064      */
15065     createToolbar : function(){
15066         
15067         Roo.log("create toolbars");
15068         
15069         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15070         this.toolbars[0].render(this.toolbarContainer());
15071         
15072         return;
15073         
15074 //        if (!editor.toolbars || !editor.toolbars.length) {
15075 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15076 //        }
15077 //        
15078 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15079 //            editor.toolbars[i] = Roo.factory(
15080 //                    typeof(editor.toolbars[i]) == 'string' ?
15081 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15082 //                Roo.bootstrap.HtmlEditor);
15083 //            editor.toolbars[i].init(editor);
15084 //        }
15085     },
15086
15087      
15088     // private
15089     onRender : function(ct, position)
15090     {
15091        // Roo.log("Call onRender: " + this.xtype);
15092         var _t = this;
15093         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15094       
15095         this.wrap = this.inputEl().wrap({
15096             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15097         });
15098         
15099         this.editorcore.onRender(ct, position);
15100          
15101         if (this.resizable) {
15102             this.resizeEl = new Roo.Resizable(this.wrap, {
15103                 pinned : true,
15104                 wrap: true,
15105                 dynamic : true,
15106                 minHeight : this.height,
15107                 height: this.height,
15108                 handles : this.resizable,
15109                 width: this.width,
15110                 listeners : {
15111                     resize : function(r, w, h) {
15112                         _t.onResize(w,h); // -something
15113                     }
15114                 }
15115             });
15116             
15117         }
15118         this.createToolbar(this);
15119        
15120         
15121         if(!this.width && this.resizable){
15122             this.setSize(this.wrap.getSize());
15123         }
15124         if (this.resizeEl) {
15125             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15126             // should trigger onReize..
15127         }
15128         
15129     },
15130
15131     // private
15132     onResize : function(w, h)
15133     {
15134         Roo.log('resize: ' +w + ',' + h );
15135         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15136         var ew = false;
15137         var eh = false;
15138         
15139         if(this.inputEl() ){
15140             if(typeof w == 'number'){
15141                 var aw = w - this.wrap.getFrameWidth('lr');
15142                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15143                 ew = aw;
15144             }
15145             if(typeof h == 'number'){
15146                  var tbh = -11;  // fixme it needs to tool bar size!
15147                 for (var i =0; i < this.toolbars.length;i++) {
15148                     // fixme - ask toolbars for heights?
15149                     tbh += this.toolbars[i].el.getHeight();
15150                     //if (this.toolbars[i].footer) {
15151                     //    tbh += this.toolbars[i].footer.el.getHeight();
15152                     //}
15153                 }
15154               
15155                 
15156                 
15157                 
15158                 
15159                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15160                 ah -= 5; // knock a few pixes off for look..
15161                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15162                 var eh = ah;
15163             }
15164         }
15165         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15166         this.editorcore.onResize(ew,eh);
15167         
15168     },
15169
15170     /**
15171      * Toggles the editor between standard and source edit mode.
15172      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15173      */
15174     toggleSourceEdit : function(sourceEditMode)
15175     {
15176         this.editorcore.toggleSourceEdit(sourceEditMode);
15177         
15178         if(this.editorcore.sourceEditMode){
15179             Roo.log('editor - showing textarea');
15180             
15181 //            Roo.log('in');
15182 //            Roo.log(this.syncValue());
15183             this.syncValue();
15184             this.inputEl().removeClass('hide');
15185             this.inputEl().dom.removeAttribute('tabIndex');
15186             this.inputEl().focus();
15187         }else{
15188             Roo.log('editor - hiding textarea');
15189 //            Roo.log('out')
15190 //            Roo.log(this.pushValue()); 
15191             this.pushValue();
15192             
15193             this.inputEl().addClass('hide');
15194             this.inputEl().dom.setAttribute('tabIndex', -1);
15195             //this.deferFocus();
15196         }
15197          
15198         if(this.resizable){
15199             this.setSize(this.wrap.getSize());
15200         }
15201         
15202         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15203     },
15204  
15205     // private (for BoxComponent)
15206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
15207
15208     // private (for BoxComponent)
15209     getResizeEl : function(){
15210         return this.wrap;
15211     },
15212
15213     // private (for BoxComponent)
15214     getPositionEl : function(){
15215         return this.wrap;
15216     },
15217
15218     // private
15219     initEvents : function(){
15220         this.originalValue = this.getValue();
15221     },
15222
15223 //    /**
15224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15225 //     * @method
15226 //     */
15227 //    markInvalid : Roo.emptyFn,
15228 //    /**
15229 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15230 //     * @method
15231 //     */
15232 //    clearInvalid : Roo.emptyFn,
15233
15234     setValue : function(v){
15235         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15236         this.editorcore.pushValue();
15237     },
15238
15239      
15240     // private
15241     deferFocus : function(){
15242         this.focus.defer(10, this);
15243     },
15244
15245     // doc'ed in Field
15246     focus : function(){
15247         this.editorcore.focus();
15248         
15249     },
15250       
15251
15252     // private
15253     onDestroy : function(){
15254         
15255         
15256         
15257         if(this.rendered){
15258             
15259             for (var i =0; i < this.toolbars.length;i++) {
15260                 // fixme - ask toolbars for heights?
15261                 this.toolbars[i].onDestroy();
15262             }
15263             
15264             this.wrap.dom.innerHTML = '';
15265             this.wrap.remove();
15266         }
15267     },
15268
15269     // private
15270     onFirstFocus : function(){
15271         //Roo.log("onFirstFocus");
15272         this.editorcore.onFirstFocus();
15273          for (var i =0; i < this.toolbars.length;i++) {
15274             this.toolbars[i].onFirstFocus();
15275         }
15276         
15277     },
15278     
15279     // private
15280     syncValue : function()
15281     {   
15282         this.editorcore.syncValue();
15283     },
15284     
15285     pushValue : function()
15286     {   
15287         this.editorcore.pushValue();
15288     }
15289      
15290     
15291     // hide stuff that is not compatible
15292     /**
15293      * @event blur
15294      * @hide
15295      */
15296     /**
15297      * @event change
15298      * @hide
15299      */
15300     /**
15301      * @event focus
15302      * @hide
15303      */
15304     /**
15305      * @event specialkey
15306      * @hide
15307      */
15308     /**
15309      * @cfg {String} fieldClass @hide
15310      */
15311     /**
15312      * @cfg {String} focusClass @hide
15313      */
15314     /**
15315      * @cfg {String} autoCreate @hide
15316      */
15317     /**
15318      * @cfg {String} inputType @hide
15319      */
15320     /**
15321      * @cfg {String} invalidClass @hide
15322      */
15323     /**
15324      * @cfg {String} invalidText @hide
15325      */
15326     /**
15327      * @cfg {String} msgFx @hide
15328      */
15329     /**
15330      * @cfg {String} validateOnBlur @hide
15331      */
15332 });
15333  
15334     
15335    
15336    
15337    
15338       
15339
15340 /**
15341  * @class Roo.bootstrap.HtmlEditorToolbar1
15342  * Basic Toolbar
15343  * 
15344  * Usage:
15345  *
15346  new Roo.bootstrap.HtmlEditor({
15347     ....
15348     toolbars : [
15349         new Roo.bootstrap.HtmlEditorToolbar1({
15350             disable : { fonts: 1 , format: 1, ..., ... , ...],
15351             btns : [ .... ]
15352         })
15353     }
15354      
15355  * 
15356  * @cfg {Object} disable List of elements to disable..
15357  * @cfg {Array} btns List of additional buttons.
15358  * 
15359  * 
15360  * NEEDS Extra CSS? 
15361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15362  */
15363  
15364 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15365 {
15366     
15367     Roo.apply(this, config);
15368     
15369     // default disabled, based on 'good practice'..
15370     this.disable = this.disable || {};
15371     Roo.applyIf(this.disable, {
15372         fontSize : true,
15373         colors : true,
15374         specialElements : true
15375     });
15376     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15377     
15378     this.editor = config.editor;
15379     this.editorcore = config.editor.editorcore;
15380     
15381     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15382     
15383     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15384     // dont call parent... till later.
15385 }
15386 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
15387     
15388     
15389     bar : true,
15390     
15391     editor : false,
15392     editorcore : false,
15393     
15394     
15395     formats : [
15396         "p" ,  
15397         "h1","h2","h3","h4","h5","h6", 
15398         "pre", "code", 
15399         "abbr", "acronym", "address", "cite", "samp", "var",
15400         'div','span'
15401     ],
15402     
15403     onRender : function(ct, position)
15404     {
15405        // Roo.log("Call onRender: " + this.xtype);
15406         
15407        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15408        Roo.log(this.el);
15409        this.el.dom.style.marginBottom = '0';
15410        var _this = this;
15411        var editorcore = this.editorcore;
15412        var editor= this.editor;
15413        
15414        var children = [];
15415        var btn = function(id,cmd , toggle, handler){
15416        
15417             var  event = toggle ? 'toggle' : 'click';
15418        
15419             var a = {
15420                 size : 'sm',
15421                 xtype: 'Button',
15422                 xns: Roo.bootstrap,
15423                 glyphicon : id,
15424                 cmd : id || cmd,
15425                 enableToggle:toggle !== false,
15426                 //html : 'submit'
15427                 pressed : toggle ? false : null,
15428                 listeners : {}
15429             }
15430             a.listeners[toggle ? 'toggle' : 'click'] = function() {
15431                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
15432             }
15433             children.push(a);
15434             return a;
15435        }
15436         
15437         var style = {
15438                 xtype: 'Button',
15439                 size : 'sm',
15440                 xns: Roo.bootstrap,
15441                 glyphicon : 'font',
15442                 //html : 'submit'
15443                 menu : {
15444                     xtype: 'Menu',
15445                     xns: Roo.bootstrap,
15446                     items:  []
15447                 }
15448         };
15449         Roo.each(this.formats, function(f) {
15450             style.menu.items.push({
15451                 xtype :'MenuItem',
15452                 xns: Roo.bootstrap,
15453                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15454                 tagname : f,
15455                 listeners : {
15456                     click : function()
15457                     {
15458                         editorcore.insertTag(this.tagname);
15459                         editor.focus();
15460                     }
15461                 }
15462                 
15463             });
15464         });
15465          children.push(style);   
15466             
15467             
15468         btn('bold',false,true);
15469         btn('italic',false,true);
15470         btn('align-left', 'justifyleft',true);
15471         btn('align-center', 'justifycenter',true);
15472         btn('align-right' , 'justifyright',true);
15473         btn('link', false, false, function(btn) {
15474             //Roo.log("create link?");
15475             var url = prompt(this.createLinkText, this.defaultLinkValue);
15476             if(url && url != 'http:/'+'/'){
15477                 this.editorcore.relayCmd('createlink', url);
15478             }
15479         }),
15480         btn('list','insertunorderedlist',true);
15481         btn('pencil', false,true, function(btn){
15482                 Roo.log(this);
15483                 
15484                 this.toggleSourceEdit(btn.pressed);
15485         });
15486         /*
15487         var cog = {
15488                 xtype: 'Button',
15489                 size : 'sm',
15490                 xns: Roo.bootstrap,
15491                 glyphicon : 'cog',
15492                 //html : 'submit'
15493                 menu : {
15494                     xtype: 'Menu',
15495                     xns: Roo.bootstrap,
15496                     items:  []
15497                 }
15498         };
15499         
15500         cog.menu.items.push({
15501             xtype :'MenuItem',
15502             xns: Roo.bootstrap,
15503             html : Clean styles,
15504             tagname : f,
15505             listeners : {
15506                 click : function()
15507                 {
15508                     editorcore.insertTag(this.tagname);
15509                     editor.focus();
15510                 }
15511             }
15512             
15513         });
15514        */
15515         
15516          
15517        this.xtype = 'Navbar';
15518         
15519         for(var i=0;i< children.length;i++) {
15520             
15521             this.buttons.add(this.addxtypeChild(children[i]));
15522             
15523         }
15524         
15525         editor.on('editorevent', this.updateToolbar, this);
15526     },
15527     onBtnClick : function(id)
15528     {
15529        this.editorcore.relayCmd(id);
15530        this.editorcore.focus();
15531     },
15532     
15533     /**
15534      * Protected method that will not generally be called directly. It triggers
15535      * a toolbar update by reading the markup state of the current selection in the editor.
15536      */
15537     updateToolbar: function(){
15538
15539         if(!this.editorcore.activated){
15540             this.editor.onFirstFocus(); // is this neeed?
15541             return;
15542         }
15543
15544         var btns = this.buttons; 
15545         var doc = this.editorcore.doc;
15546         btns.get('bold').setActive(doc.queryCommandState('bold'));
15547         btns.get('italic').setActive(doc.queryCommandState('italic'));
15548         //btns.get('underline').setActive(doc.queryCommandState('underline'));
15549         
15550         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15551         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15552         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15553         
15554         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15555         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15556          /*
15557         
15558         var ans = this.editorcore.getAllAncestors();
15559         if (this.formatCombo) {
15560             
15561             
15562             var store = this.formatCombo.store;
15563             this.formatCombo.setValue("");
15564             for (var i =0; i < ans.length;i++) {
15565                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15566                     // select it..
15567                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15568                     break;
15569                 }
15570             }
15571         }
15572         
15573         
15574         
15575         // hides menus... - so this cant be on a menu...
15576         Roo.bootstrap.MenuMgr.hideAll();
15577         */
15578         Roo.bootstrap.MenuMgr.hideAll();
15579         //this.editorsyncValue();
15580     },
15581     onFirstFocus: function() {
15582         this.buttons.each(function(item){
15583            item.enable();
15584         });
15585     },
15586     toggleSourceEdit : function(sourceEditMode){
15587         
15588           
15589         if(sourceEditMode){
15590             Roo.log("disabling buttons");
15591            this.buttons.each( function(item){
15592                 if(item.cmd != 'pencil'){
15593                     item.disable();
15594                 }
15595             });
15596           
15597         }else{
15598             Roo.log("enabling buttons");
15599             if(this.editorcore.initialized){
15600                 this.buttons.each( function(item){
15601                     item.enable();
15602                 });
15603             }
15604             
15605         }
15606         Roo.log("calling toggole on editor");
15607         // tell the editor that it's been pressed..
15608         this.editor.toggleSourceEdit(sourceEditMode);
15609        
15610     }
15611 });
15612
15613
15614
15615
15616
15617 /**
15618  * @class Roo.bootstrap.Table.AbstractSelectionModel
15619  * @extends Roo.util.Observable
15620  * Abstract base class for grid SelectionModels.  It provides the interface that should be
15621  * implemented by descendant classes.  This class should not be directly instantiated.
15622  * @constructor
15623  */
15624 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15625     this.locked = false;
15626     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15627 };
15628
15629
15630 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
15631     /** @ignore Called by the grid automatically. Do not call directly. */
15632     init : function(grid){
15633         this.grid = grid;
15634         this.initEvents();
15635     },
15636
15637     /**
15638      * Locks the selections.
15639      */
15640     lock : function(){
15641         this.locked = true;
15642     },
15643
15644     /**
15645      * Unlocks the selections.
15646      */
15647     unlock : function(){
15648         this.locked = false;
15649     },
15650
15651     /**
15652      * Returns true if the selections are locked.
15653      * @return {Boolean}
15654      */
15655     isLocked : function(){
15656         return this.locked;
15657     }
15658 });
15659 /**
15660  * @class Roo.bootstrap.Table.ColumnModel
15661  * @extends Roo.util.Observable
15662  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15663  * the columns in the table.
15664  
15665  * @constructor
15666  * @param {Object} config An Array of column config objects. See this class's
15667  * config objects for details.
15668 */
15669 Roo.bootstrap.Table.ColumnModel = function(config){
15670         /**
15671      * The config passed into the constructor
15672      */
15673     this.config = config;
15674     this.lookup = {};
15675
15676     // if no id, create one
15677     // if the column does not have a dataIndex mapping,
15678     // map it to the order it is in the config
15679     for(var i = 0, len = config.length; i < len; i++){
15680         var c = config[i];
15681         if(typeof c.dataIndex == "undefined"){
15682             c.dataIndex = i;
15683         }
15684         if(typeof c.renderer == "string"){
15685             c.renderer = Roo.util.Format[c.renderer];
15686         }
15687         if(typeof c.id == "undefined"){
15688             c.id = Roo.id();
15689         }
15690 //        if(c.editor && c.editor.xtype){
15691 //            c.editor  = Roo.factory(c.editor, Roo.grid);
15692 //        }
15693 //        if(c.editor && c.editor.isFormField){
15694 //            c.editor = new Roo.grid.GridEditor(c.editor);
15695 //        }
15696
15697         this.lookup[c.id] = c;
15698     }
15699
15700     /**
15701      * The width of columns which have no width specified (defaults to 100)
15702      * @type Number
15703      */
15704     this.defaultWidth = 100;
15705
15706     /**
15707      * Default sortable of columns which have no sortable specified (defaults to false)
15708      * @type Boolean
15709      */
15710     this.defaultSortable = false;
15711
15712     this.addEvents({
15713         /**
15714              * @event widthchange
15715              * Fires when the width of a column changes.
15716              * @param {ColumnModel} this
15717              * @param {Number} columnIndex The column index
15718              * @param {Number} newWidth The new width
15719              */
15720             "widthchange": true,
15721         /**
15722              * @event headerchange
15723              * Fires when the text of a header changes.
15724              * @param {ColumnModel} this
15725              * @param {Number} columnIndex The column index
15726              * @param {Number} newText The new header text
15727              */
15728             "headerchange": true,
15729         /**
15730              * @event hiddenchange
15731              * Fires when a column is hidden or "unhidden".
15732              * @param {ColumnModel} this
15733              * @param {Number} columnIndex The column index
15734              * @param {Boolean} hidden true if hidden, false otherwise
15735              */
15736             "hiddenchange": true,
15737             /**
15738          * @event columnmoved
15739          * Fires when a column is moved.
15740          * @param {ColumnModel} this
15741          * @param {Number} oldIndex
15742          * @param {Number} newIndex
15743          */
15744         "columnmoved" : true,
15745         /**
15746          * @event columlockchange
15747          * Fires when a column's locked state is changed
15748          * @param {ColumnModel} this
15749          * @param {Number} colIndex
15750          * @param {Boolean} locked true if locked
15751          */
15752         "columnlockchange" : true
15753     });
15754     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15755 };
15756 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15757     /**
15758      * @cfg {String} header The header text to display in the Grid view.
15759      */
15760     /**
15761      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15762      * {@link Roo.data.Record} definition from which to draw the column's value. If not
15763      * specified, the column's index is used as an index into the Record's data Array.
15764      */
15765     /**
15766      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15767      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15768      */
15769     /**
15770      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15771      * Defaults to the value of the {@link #defaultSortable} property.
15772      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15773      */
15774     /**
15775      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
15776      */
15777     /**
15778      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
15779      */
15780     /**
15781      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15782      */
15783     /**
15784      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15785      */
15786     /**
15787      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15788      * given the cell's data value. See {@link #setRenderer}. If not specified, the
15789      * default renderer uses the raw data value.
15790      */
15791     /**
15792      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
15793      */
15794
15795     /**
15796      * Returns the id of the column at the specified index.
15797      * @param {Number} index The column index
15798      * @return {String} the id
15799      */
15800     getColumnId : function(index){
15801         return this.config[index].id;
15802     },
15803
15804     /**
15805      * Returns the column for a specified id.
15806      * @param {String} id The column id
15807      * @return {Object} the column
15808      */
15809     getColumnById : function(id){
15810         return this.lookup[id];
15811     },
15812
15813     
15814     /**
15815      * Returns the column for a specified dataIndex.
15816      * @param {String} dataIndex The column dataIndex
15817      * @return {Object|Boolean} the column or false if not found
15818      */
15819     getColumnByDataIndex: function(dataIndex){
15820         var index = this.findColumnIndex(dataIndex);
15821         return index > -1 ? this.config[index] : false;
15822     },
15823     
15824     /**
15825      * Returns the index for a specified column id.
15826      * @param {String} id The column id
15827      * @return {Number} the index, or -1 if not found
15828      */
15829     getIndexById : function(id){
15830         for(var i = 0, len = this.config.length; i < len; i++){
15831             if(this.config[i].id == id){
15832                 return i;
15833             }
15834         }
15835         return -1;
15836     },
15837     
15838     /**
15839      * Returns the index for a specified column dataIndex.
15840      * @param {String} dataIndex The column dataIndex
15841      * @return {Number} the index, or -1 if not found
15842      */
15843     
15844     findColumnIndex : function(dataIndex){
15845         for(var i = 0, len = this.config.length; i < len; i++){
15846             if(this.config[i].dataIndex == dataIndex){
15847                 return i;
15848             }
15849         }
15850         return -1;
15851     },
15852     
15853     
15854     moveColumn : function(oldIndex, newIndex){
15855         var c = this.config[oldIndex];
15856         this.config.splice(oldIndex, 1);
15857         this.config.splice(newIndex, 0, c);
15858         this.dataMap = null;
15859         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15860     },
15861
15862     isLocked : function(colIndex){
15863         return this.config[colIndex].locked === true;
15864     },
15865
15866     setLocked : function(colIndex, value, suppressEvent){
15867         if(this.isLocked(colIndex) == value){
15868             return;
15869         }
15870         this.config[colIndex].locked = value;
15871         if(!suppressEvent){
15872             this.fireEvent("columnlockchange", this, colIndex, value);
15873         }
15874     },
15875
15876     getTotalLockedWidth : function(){
15877         var totalWidth = 0;
15878         for(var i = 0; i < this.config.length; i++){
15879             if(this.isLocked(i) && !this.isHidden(i)){
15880                 this.totalWidth += this.getColumnWidth(i);
15881             }
15882         }
15883         return totalWidth;
15884     },
15885
15886     getLockedCount : function(){
15887         for(var i = 0, len = this.config.length; i < len; i++){
15888             if(!this.isLocked(i)){
15889                 return i;
15890             }
15891         }
15892     },
15893
15894     /**
15895      * Returns the number of columns.
15896      * @return {Number}
15897      */
15898     getColumnCount : function(visibleOnly){
15899         if(visibleOnly === true){
15900             var c = 0;
15901             for(var i = 0, len = this.config.length; i < len; i++){
15902                 if(!this.isHidden(i)){
15903                     c++;
15904                 }
15905             }
15906             return c;
15907         }
15908         return this.config.length;
15909     },
15910
15911     /**
15912      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15913      * @param {Function} fn
15914      * @param {Object} scope (optional)
15915      * @return {Array} result
15916      */
15917     getColumnsBy : function(fn, scope){
15918         var r = [];
15919         for(var i = 0, len = this.config.length; i < len; i++){
15920             var c = this.config[i];
15921             if(fn.call(scope||this, c, i) === true){
15922                 r[r.length] = c;
15923             }
15924         }
15925         return r;
15926     },
15927
15928     /**
15929      * Returns true if the specified column is sortable.
15930      * @param {Number} col The column index
15931      * @return {Boolean}
15932      */
15933     isSortable : function(col){
15934         if(typeof this.config[col].sortable == "undefined"){
15935             return this.defaultSortable;
15936         }
15937         return this.config[col].sortable;
15938     },
15939
15940     /**
15941      * Returns the rendering (formatting) function defined for the column.
15942      * @param {Number} col The column index.
15943      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15944      */
15945     getRenderer : function(col){
15946         if(!this.config[col].renderer){
15947             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15948         }
15949         return this.config[col].renderer;
15950     },
15951
15952     /**
15953      * Sets the rendering (formatting) function for a column.
15954      * @param {Number} col The column index
15955      * @param {Function} fn The function to use to process the cell's raw data
15956      * to return HTML markup for the grid view. The render function is called with
15957      * the following parameters:<ul>
15958      * <li>Data value.</li>
15959      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15960      * <li>css A CSS style string to apply to the table cell.</li>
15961      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15962      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15963      * <li>Row index</li>
15964      * <li>Column index</li>
15965      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15966      */
15967     setRenderer : function(col, fn){
15968         this.config[col].renderer = fn;
15969     },
15970
15971     /**
15972      * Returns the width for the specified column.
15973      * @param {Number} col The column index
15974      * @return {Number}
15975      */
15976     getColumnWidth : function(col){
15977         return this.config[col].width * 1 || this.defaultWidth;
15978     },
15979
15980     /**
15981      * Sets the width for a column.
15982      * @param {Number} col The column index
15983      * @param {Number} width The new width
15984      */
15985     setColumnWidth : function(col, width, suppressEvent){
15986         this.config[col].width = width;
15987         this.totalWidth = null;
15988         if(!suppressEvent){
15989              this.fireEvent("widthchange", this, col, width);
15990         }
15991     },
15992
15993     /**
15994      * Returns the total width of all columns.
15995      * @param {Boolean} includeHidden True to include hidden column widths
15996      * @return {Number}
15997      */
15998     getTotalWidth : function(includeHidden){
15999         if(!this.totalWidth){
16000             this.totalWidth = 0;
16001             for(var i = 0, len = this.config.length; i < len; i++){
16002                 if(includeHidden || !this.isHidden(i)){
16003                     this.totalWidth += this.getColumnWidth(i);
16004                 }
16005             }
16006         }
16007         return this.totalWidth;
16008     },
16009
16010     /**
16011      * Returns the header for the specified column.
16012      * @param {Number} col The column index
16013      * @return {String}
16014      */
16015     getColumnHeader : function(col){
16016         return this.config[col].header;
16017     },
16018
16019     /**
16020      * Sets the header for a column.
16021      * @param {Number} col The column index
16022      * @param {String} header The new header
16023      */
16024     setColumnHeader : function(col, header){
16025         this.config[col].header = header;
16026         this.fireEvent("headerchange", this, col, header);
16027     },
16028
16029     /**
16030      * Returns the tooltip for the specified column.
16031      * @param {Number} col The column index
16032      * @return {String}
16033      */
16034     getColumnTooltip : function(col){
16035             return this.config[col].tooltip;
16036     },
16037     /**
16038      * Sets the tooltip for a column.
16039      * @param {Number} col The column index
16040      * @param {String} tooltip The new tooltip
16041      */
16042     setColumnTooltip : function(col, tooltip){
16043             this.config[col].tooltip = tooltip;
16044     },
16045
16046     /**
16047      * Returns the dataIndex for the specified column.
16048      * @param {Number} col The column index
16049      * @return {Number}
16050      */
16051     getDataIndex : function(col){
16052         return this.config[col].dataIndex;
16053     },
16054
16055     /**
16056      * Sets the dataIndex for a column.
16057      * @param {Number} col The column index
16058      * @param {Number} dataIndex The new dataIndex
16059      */
16060     setDataIndex : function(col, dataIndex){
16061         this.config[col].dataIndex = dataIndex;
16062     },
16063
16064     
16065     
16066     /**
16067      * Returns true if the cell is editable.
16068      * @param {Number} colIndex The column index
16069      * @param {Number} rowIndex The row index
16070      * @return {Boolean}
16071      */
16072     isCellEditable : function(colIndex, rowIndex){
16073         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16074     },
16075
16076     /**
16077      * Returns the editor defined for the cell/column.
16078      * return false or null to disable editing.
16079      * @param {Number} colIndex The column index
16080      * @param {Number} rowIndex The row index
16081      * @return {Object}
16082      */
16083     getCellEditor : function(colIndex, rowIndex){
16084         return this.config[colIndex].editor;
16085     },
16086
16087     /**
16088      * Sets if a column is editable.
16089      * @param {Number} col The column index
16090      * @param {Boolean} editable True if the column is editable
16091      */
16092     setEditable : function(col, editable){
16093         this.config[col].editable = editable;
16094     },
16095
16096
16097     /**
16098      * Returns true if the column is hidden.
16099      * @param {Number} colIndex The column index
16100      * @return {Boolean}
16101      */
16102     isHidden : function(colIndex){
16103         return this.config[colIndex].hidden;
16104     },
16105
16106
16107     /**
16108      * Returns true if the column width cannot be changed
16109      */
16110     isFixed : function(colIndex){
16111         return this.config[colIndex].fixed;
16112     },
16113
16114     /**
16115      * Returns true if the column can be resized
16116      * @return {Boolean}
16117      */
16118     isResizable : function(colIndex){
16119         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16120     },
16121     /**
16122      * Sets if a column is hidden.
16123      * @param {Number} colIndex The column index
16124      * @param {Boolean} hidden True if the column is hidden
16125      */
16126     setHidden : function(colIndex, hidden){
16127         this.config[colIndex].hidden = hidden;
16128         this.totalWidth = null;
16129         this.fireEvent("hiddenchange", this, colIndex, hidden);
16130     },
16131
16132     /**
16133      * Sets the editor for a column.
16134      * @param {Number} col The column index
16135      * @param {Object} editor The editor object
16136      */
16137     setEditor : function(col, editor){
16138         this.config[col].editor = editor;
16139     }
16140 });
16141
16142 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16143         if(typeof value == "string" && value.length < 1){
16144             return "&#160;";
16145         }
16146         return value;
16147 };
16148
16149 // Alias for backwards compatibility
16150 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16151
16152 /**
16153  * @extends Roo.bootstrap.Table.AbstractSelectionModel
16154  * @class Roo.bootstrap.Table.RowSelectionModel
16155  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16156  * It supports multiple selections and keyboard selection/navigation. 
16157  * @constructor
16158  * @param {Object} config
16159  */
16160
16161 Roo.bootstrap.Table.RowSelectionModel = function(config){
16162     Roo.apply(this, config);
16163     this.selections = new Roo.util.MixedCollection(false, function(o){
16164         return o.id;
16165     });
16166
16167     this.last = false;
16168     this.lastActive = false;
16169
16170     this.addEvents({
16171         /**
16172              * @event selectionchange
16173              * Fires when the selection changes
16174              * @param {SelectionModel} this
16175              */
16176             "selectionchange" : true,
16177         /**
16178              * @event afterselectionchange
16179              * Fires after the selection changes (eg. by key press or clicking)
16180              * @param {SelectionModel} this
16181              */
16182             "afterselectionchange" : true,
16183         /**
16184              * @event beforerowselect
16185              * Fires when a row is selected being selected, return false to cancel.
16186              * @param {SelectionModel} this
16187              * @param {Number} rowIndex The selected index
16188              * @param {Boolean} keepExisting False if other selections will be cleared
16189              */
16190             "beforerowselect" : true,
16191         /**
16192              * @event rowselect
16193              * Fires when a row is selected.
16194              * @param {SelectionModel} this
16195              * @param {Number} rowIndex The selected index
16196              * @param {Roo.data.Record} r The record
16197              */
16198             "rowselect" : true,
16199         /**
16200              * @event rowdeselect
16201              * Fires when a row is deselected.
16202              * @param {SelectionModel} this
16203              * @param {Number} rowIndex The selected index
16204              */
16205         "rowdeselect" : true
16206     });
16207     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16208     this.locked = false;
16209 };
16210
16211 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
16212     /**
16213      * @cfg {Boolean} singleSelect
16214      * True to allow selection of only one row at a time (defaults to false)
16215      */
16216     singleSelect : false,
16217
16218     // private
16219     initEvents : function(){
16220
16221         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16222             this.grid.on("mousedown", this.handleMouseDown, this);
16223         }else{ // allow click to work like normal
16224             this.grid.on("rowclick", this.handleDragableRowClick, this);
16225         }
16226
16227         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16228             "up" : function(e){
16229                 if(!e.shiftKey){
16230                     this.selectPrevious(e.shiftKey);
16231                 }else if(this.last !== false && this.lastActive !== false){
16232                     var last = this.last;
16233                     this.selectRange(this.last,  this.lastActive-1);
16234                     this.grid.getView().focusRow(this.lastActive);
16235                     if(last !== false){
16236                         this.last = last;
16237                     }
16238                 }else{
16239                     this.selectFirstRow();
16240                 }
16241                 this.fireEvent("afterselectionchange", this);
16242             },
16243             "down" : function(e){
16244                 if(!e.shiftKey){
16245                     this.selectNext(e.shiftKey);
16246                 }else if(this.last !== false && this.lastActive !== false){
16247                     var last = this.last;
16248                     this.selectRange(this.last,  this.lastActive+1);
16249                     this.grid.getView().focusRow(this.lastActive);
16250                     if(last !== false){
16251                         this.last = last;
16252                     }
16253                 }else{
16254                     this.selectFirstRow();
16255                 }
16256                 this.fireEvent("afterselectionchange", this);
16257             },
16258             scope: this
16259         });
16260
16261         var view = this.grid.view;
16262         view.on("refresh", this.onRefresh, this);
16263         view.on("rowupdated", this.onRowUpdated, this);
16264         view.on("rowremoved", this.onRemove, this);
16265     },
16266
16267     // private
16268     onRefresh : function(){
16269         var ds = this.grid.dataSource, i, v = this.grid.view;
16270         var s = this.selections;
16271         s.each(function(r){
16272             if((i = ds.indexOfId(r.id)) != -1){
16273                 v.onRowSelect(i);
16274             }else{
16275                 s.remove(r);
16276             }
16277         });
16278     },
16279
16280     // private
16281     onRemove : function(v, index, r){
16282         this.selections.remove(r);
16283     },
16284
16285     // private
16286     onRowUpdated : function(v, index, r){
16287         if(this.isSelected(r)){
16288             v.onRowSelect(index);
16289         }
16290     },
16291
16292     /**
16293      * Select records.
16294      * @param {Array} records The records to select
16295      * @param {Boolean} keepExisting (optional) True to keep existing selections
16296      */
16297     selectRecords : function(records, keepExisting){
16298         if(!keepExisting){
16299             this.clearSelections();
16300         }
16301         var ds = this.grid.dataSource;
16302         for(var i = 0, len = records.length; i < len; i++){
16303             this.selectRow(ds.indexOf(records[i]), true);
16304         }
16305     },
16306
16307     /**
16308      * Gets the number of selected rows.
16309      * @return {Number}
16310      */
16311     getCount : function(){
16312         return this.selections.length;
16313     },
16314
16315     /**
16316      * Selects the first row in the grid.
16317      */
16318     selectFirstRow : function(){
16319         this.selectRow(0);
16320     },
16321
16322     /**
16323      * Select the last row.
16324      * @param {Boolean} keepExisting (optional) True to keep existing selections
16325      */
16326     selectLastRow : function(keepExisting){
16327         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16328     },
16329
16330     /**
16331      * Selects the row immediately following the last selected row.
16332      * @param {Boolean} keepExisting (optional) True to keep existing selections
16333      */
16334     selectNext : function(keepExisting){
16335         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16336             this.selectRow(this.last+1, keepExisting);
16337             this.grid.getView().focusRow(this.last);
16338         }
16339     },
16340
16341     /**
16342      * Selects the row that precedes the last selected row.
16343      * @param {Boolean} keepExisting (optional) True to keep existing selections
16344      */
16345     selectPrevious : function(keepExisting){
16346         if(this.last){
16347             this.selectRow(this.last-1, keepExisting);
16348             this.grid.getView().focusRow(this.last);
16349         }
16350     },
16351
16352     /**
16353      * Returns the selected records
16354      * @return {Array} Array of selected records
16355      */
16356     getSelections : function(){
16357         return [].concat(this.selections.items);
16358     },
16359
16360     /**
16361      * Returns the first selected record.
16362      * @return {Record}
16363      */
16364     getSelected : function(){
16365         return this.selections.itemAt(0);
16366     },
16367
16368
16369     /**
16370      * Clears all selections.
16371      */
16372     clearSelections : function(fast){
16373         if(this.locked) return;
16374         if(fast !== true){
16375             var ds = this.grid.dataSource;
16376             var s = this.selections;
16377             s.each(function(r){
16378                 this.deselectRow(ds.indexOfId(r.id));
16379             }, this);
16380             s.clear();
16381         }else{
16382             this.selections.clear();
16383         }
16384         this.last = false;
16385     },
16386
16387
16388     /**
16389      * Selects all rows.
16390      */
16391     selectAll : function(){
16392         if(this.locked) return;
16393         this.selections.clear();
16394         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16395             this.selectRow(i, true);
16396         }
16397     },
16398
16399     /**
16400      * Returns True if there is a selection.
16401      * @return {Boolean}
16402      */
16403     hasSelection : function(){
16404         return this.selections.length > 0;
16405     },
16406
16407     /**
16408      * Returns True if the specified row is selected.
16409      * @param {Number/Record} record The record or index of the record to check
16410      * @return {Boolean}
16411      */
16412     isSelected : function(index){
16413         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16414         return (r && this.selections.key(r.id) ? true : false);
16415     },
16416
16417     /**
16418      * Returns True if the specified record id is selected.
16419      * @param {String} id The id of record to check
16420      * @return {Boolean}
16421      */
16422     isIdSelected : function(id){
16423         return (this.selections.key(id) ? true : false);
16424     },
16425
16426     // private
16427     handleMouseDown : function(e, t){
16428         var view = this.grid.getView(), rowIndex;
16429         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16430             return;
16431         };
16432         if(e.shiftKey && this.last !== false){
16433             var last = this.last;
16434             this.selectRange(last, rowIndex, e.ctrlKey);
16435             this.last = last; // reset the last
16436             view.focusRow(rowIndex);
16437         }else{
16438             var isSelected = this.isSelected(rowIndex);
16439             if(e.button !== 0 && isSelected){
16440                 view.focusRow(rowIndex);
16441             }else if(e.ctrlKey && isSelected){
16442                 this.deselectRow(rowIndex);
16443             }else if(!isSelected){
16444                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16445                 view.focusRow(rowIndex);
16446             }
16447         }
16448         this.fireEvent("afterselectionchange", this);
16449     },
16450     // private
16451     handleDragableRowClick :  function(grid, rowIndex, e) 
16452     {
16453         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16454             this.selectRow(rowIndex, false);
16455             grid.view.focusRow(rowIndex);
16456              this.fireEvent("afterselectionchange", this);
16457         }
16458     },
16459     
16460     /**
16461      * Selects multiple rows.
16462      * @param {Array} rows Array of the indexes of the row to select
16463      * @param {Boolean} keepExisting (optional) True to keep existing selections
16464      */
16465     selectRows : function(rows, keepExisting){
16466         if(!keepExisting){
16467             this.clearSelections();
16468         }
16469         for(var i = 0, len = rows.length; i < len; i++){
16470             this.selectRow(rows[i], true);
16471         }
16472     },
16473
16474     /**
16475      * Selects a range of rows. All rows in between startRow and endRow are also selected.
16476      * @param {Number} startRow The index of the first row in the range
16477      * @param {Number} endRow The index of the last row in the range
16478      * @param {Boolean} keepExisting (optional) True to retain existing selections
16479      */
16480     selectRange : function(startRow, endRow, keepExisting){
16481         if(this.locked) return;
16482         if(!keepExisting){
16483             this.clearSelections();
16484         }
16485         if(startRow <= endRow){
16486             for(var i = startRow; i <= endRow; i++){
16487                 this.selectRow(i, true);
16488             }
16489         }else{
16490             for(var i = startRow; i >= endRow; i--){
16491                 this.selectRow(i, true);
16492             }
16493         }
16494     },
16495
16496     /**
16497      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16498      * @param {Number} startRow The index of the first row in the range
16499      * @param {Number} endRow The index of the last row in the range
16500      */
16501     deselectRange : function(startRow, endRow, preventViewNotify){
16502         if(this.locked) return;
16503         for(var i = startRow; i <= endRow; i++){
16504             this.deselectRow(i, preventViewNotify);
16505         }
16506     },
16507
16508     /**
16509      * Selects a row.
16510      * @param {Number} row The index of the row to select
16511      * @param {Boolean} keepExisting (optional) True to keep existing selections
16512      */
16513     selectRow : function(index, keepExisting, preventViewNotify){
16514         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16515         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16516             if(!keepExisting || this.singleSelect){
16517                 this.clearSelections();
16518             }
16519             var r = this.grid.dataSource.getAt(index);
16520             this.selections.add(r);
16521             this.last = this.lastActive = index;
16522             if(!preventViewNotify){
16523                 this.grid.getView().onRowSelect(index);
16524             }
16525             this.fireEvent("rowselect", this, index, r);
16526             this.fireEvent("selectionchange", this);
16527         }
16528     },
16529
16530     /**
16531      * Deselects a row.
16532      * @param {Number} row The index of the row to deselect
16533      */
16534     deselectRow : function(index, preventViewNotify){
16535         if(this.locked) return;
16536         if(this.last == index){
16537             this.last = false;
16538         }
16539         if(this.lastActive == index){
16540             this.lastActive = false;
16541         }
16542         var r = this.grid.dataSource.getAt(index);
16543         this.selections.remove(r);
16544         if(!preventViewNotify){
16545             this.grid.getView().onRowDeselect(index);
16546         }
16547         this.fireEvent("rowdeselect", this, index);
16548         this.fireEvent("selectionchange", this);
16549     },
16550
16551     // private
16552     restoreLast : function(){
16553         if(this._last){
16554             this.last = this._last;
16555         }
16556     },
16557
16558     // private
16559     acceptsNav : function(row, col, cm){
16560         return !cm.isHidden(col) && cm.isCellEditable(col, row);
16561     },
16562
16563     // private
16564     onEditorKey : function(field, e){
16565         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16566         if(k == e.TAB){
16567             e.stopEvent();
16568             ed.completeEdit();
16569             if(e.shiftKey){
16570                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16571             }else{
16572                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16573             }
16574         }else if(k == e.ENTER && !e.ctrlKey){
16575             e.stopEvent();
16576             ed.completeEdit();
16577             if(e.shiftKey){
16578                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16579             }else{
16580                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16581             }
16582         }else if(k == e.ESC){
16583             ed.cancelEdit();
16584         }
16585         if(newCell){
16586             g.startEditing(newCell[0], newCell[1]);
16587         }
16588     }
16589 });/*
16590  * - LGPL
16591  *
16592  * element
16593  * 
16594  */
16595
16596 /**
16597  * @class Roo.bootstrap.MessageBar
16598  * @extends Roo.bootstrap.Component
16599  * Bootstrap MessageBar class
16600  * @cfg {String} html contents of the MessageBar
16601  * @cfg {String} weight (info | success | warning | danger) default info
16602  * @cfg {String} beforeClass insert the bar before the given class
16603  * @cfg {Boolean} closable (true | false) default false
16604  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16605  * 
16606  * @constructor
16607  * Create a new Element
16608  * @param {Object} config The config object
16609  */
16610
16611 Roo.bootstrap.MessageBar = function(config){
16612     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16613 };
16614
16615 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
16616     
16617     html: '',
16618     weight: 'info',
16619     closable: false,
16620     fixed: false,
16621     beforeClass: 'bootstrap-sticky-wrap',
16622     
16623     getAutoCreate : function(){
16624         
16625         var cfg = {
16626             tag: 'div',
16627             cls: 'alert alert-dismissable alert-' + this.weight,
16628             cn: [
16629                 {
16630                     tag: 'span',
16631                     cls: 'message',
16632                     html: this.html || ''
16633                 }
16634             ]
16635         }
16636         
16637         if(this.fixed){
16638             cfg.cls += ' alert-messages-fixed';
16639         }
16640         
16641         if(this.closable){
16642             cfg.cn.push({
16643                 tag: 'button',
16644                 cls: 'close',
16645                 html: 'x'
16646             });
16647         }
16648         
16649         return cfg;
16650     },
16651     
16652     onRender : function(ct, position)
16653     {
16654         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16655         
16656         if(!this.el){
16657             var cfg = Roo.apply({},  this.getAutoCreate());
16658             cfg.id = Roo.id();
16659             
16660             if (this.cls) {
16661                 cfg.cls += ' ' + this.cls;
16662             }
16663             if (this.style) {
16664                 cfg.style = this.style;
16665             }
16666             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16667             
16668             this.el.setVisibilityMode(Roo.Element.DISPLAY);
16669         }
16670         
16671         this.el.select('>button.close').on('click', this.hide, this);
16672         
16673     },
16674     
16675     show : function()
16676     {
16677         if (!this.rendered) {
16678             this.render();
16679         }
16680         
16681         this.el.show();
16682         
16683         this.fireEvent('show', this);
16684         
16685     },
16686     
16687     hide : function()
16688     {
16689         if (!this.rendered) {
16690             this.render();
16691         }
16692         
16693         this.el.hide();
16694         
16695         this.fireEvent('hide', this);
16696     },
16697     
16698     update : function()
16699     {
16700 //        var e = this.el.dom.firstChild;
16701 //        
16702 //        if(this.closable){
16703 //            e = e.nextSibling;
16704 //        }
16705 //        
16706 //        e.data = this.html || '';
16707
16708         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
16709     }
16710    
16711 });
16712
16713  
16714
16715