Roo/bootstrap/Navbar.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         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @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
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216     
2217     
2218    
2219 });
2220
2221  
2222
2223  /*
2224  * - LGPL
2225  *
2226  * nav group
2227  * 
2228  */
2229
2230 /**
2231  * @class Roo.bootstrap.NavGroup
2232  * @extends Roo.bootstrap.Component
2233  * Bootstrap NavGroup class
2234  * @cfg {String} align left | right
2235  * @cfg {Boolean} inverse false | true
2236  * @cfg {String} type (nav|pills|tab) default nav
2237  * 
2238  * @constructor
2239  * Create a new nav group
2240  * @param {Object} config The config object
2241  */
2242
2243 Roo.bootstrap.NavGroup = function(config){
2244     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 };
2246
2247 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2248     
2249     align: '',
2250     inverse: false,
2251     form: false,
2252     type: 'nav',
2253     
2254     getAutoCreate : function(){
2255         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2256         
2257         cfg = {
2258             tag : 'ul',
2259             cls: 'nav' 
2260         }
2261         
2262         if (['tabs','pills'].indexOf(this.type)!==-1) {
2263             cfg.cls += ' nav-' + this.type
2264         } else {
2265             if (this.type!=='nav') {
2266                 Roo.log('nav type must be nav/tabs/pills')
2267             }
2268             cfg.cls += ' navbar-nav'
2269         }
2270         
2271         if (this.parent().sidebar === true) {
2272             cfg = {
2273                 tag: 'ul',
2274                 cls: 'dashboard-menu'
2275             }
2276             
2277             return cfg;
2278         }
2279         
2280         if (this.form === true) {
2281             cfg = {
2282                 tag: 'form',
2283                 cls: 'navbar-form'
2284             }
2285             
2286             if (this.align === 'right') {
2287                 cfg.cls += ' navbar-right';
2288             } else {
2289                 cfg.cls += ' navbar-left';
2290             }
2291         }
2292         
2293         if (this.align === 'right') {
2294             cfg.cls += ' navbar-right';
2295         }
2296         
2297         if (this.inverse) {
2298             cfg.cls += ' navbar-inverse';
2299             
2300         }
2301         
2302         
2303         return cfg;
2304     }
2305    
2306 });
2307
2308  
2309
2310  /*
2311  * - LGPL
2312  *
2313  * row
2314  * 
2315  */
2316
2317 /**
2318  * @class Roo.bootstrap.Navbar.Item
2319  * @extends Roo.bootstrap.Component
2320  * Bootstrap Navbar.Button class
2321  * @cfg {String} href  link to
2322  * @cfg {String} html content of button
2323  * @cfg {String} badge text inside badge
2324  * @cfg {String} glyphicon name of glyphicon
2325  * @cfg {String} icon name of font awesome icon
2326  * @cfg {Boolena} active Is item active
2327  * @cfg {Boolean} preventDefault (true | false) default false
2328   
2329  * @constructor
2330  * Create a new Navbar Button
2331  * @param {Object} config The config object
2332  */
2333 Roo.bootstrap.Navbar.Item = function(config){
2334     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2335     this.addEvents({
2336         // raw events
2337         /**
2338          * @event click
2339          * The raw click event for the entire grid.
2340          * @param {Roo.EventObject} e
2341          */
2342         "click" : true
2343     });
2344 };
2345
2346 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2347     
2348     href: false,
2349     html: '',
2350     badge: '',
2351     icon: false,
2352     glyphicon: false,
2353     icon: false,
2354     active: false,
2355     preventDefault : false,
2356     
2357     getAutoCreate : function(){
2358         
2359         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2360         
2361         if (this.parent().parent().sidebar === true) {
2362             cfg = {
2363                 tag: 'li',
2364                 cls: '',
2365                 cn: [
2366                     {
2367                         tag: 'p',
2368                         cls: ''
2369                     }
2370                 ]
2371             }
2372             
2373             if (this.html) {
2374                 cfg.cn[0].html = this.html;
2375             }
2376             
2377             if (this.active) {
2378                 this.cls += ' active';
2379             }
2380             
2381             if (this.menu) {
2382                 cfg.cn[0].cls += ' dropdown-toggle';
2383                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2384             }
2385             
2386             if (this.href) {
2387                 cfg.cn[0].tag = 'a',
2388                 cfg.cn[0].href = this.href;
2389             }
2390             
2391             if (this.glyphicon) {
2392                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2393             }
2394             
2395             if (this.icon) {
2396                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2397             }
2398             
2399             return cfg;
2400         }
2401         
2402         cfg = {
2403             tag: 'li',
2404             cls: 'nav-item'
2405         }
2406         
2407         if (this.active) {
2408             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2409         }
2410             
2411         cfg.cn = [
2412             {
2413                 tag: 'p',
2414                 html: 'Text'
2415             }
2416         ];
2417         
2418         if (this.glyphicon) {
2419             if(cfg.html){cfg.html = ' ' + this.html};
2420             cfg.cn=[
2421                 {
2422                     tag: 'span',
2423                     cls: 'glyphicon glyphicon-' + this.glyphicon
2424                 }
2425             ];
2426         }
2427         
2428         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2429         
2430         if (this.menu) {
2431             cfg.cn[0].tag='a';
2432             cfg.cn[0].href='#';
2433             cfg.cn[0].html += " <span class='caret'></span>";
2434         //}else if (!this.href) {
2435         //    cfg.cn[0].tag='p';
2436         //    cfg.cn[0].cls='navbar-text';
2437         } else {
2438             cfg.cn[0].tag='a';
2439             cfg.cn[0].href=this.href||'#';
2440             cfg.cn[0].html=this.html;
2441         }
2442         
2443         if (this.badge !== '') {
2444             
2445             cfg.cn[0].cn=[
2446                 cfg.cn[0].html + ' ',
2447                 {
2448                     tag: 'span',
2449                     cls: 'badge',
2450                     html: this.badge
2451                 }
2452             ];
2453             cfg.cn[0].html=''
2454         }
2455          
2456         if (this.icon) {
2457             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2458         }
2459         
2460         return cfg;
2461     },
2462     initEvents: function() {
2463        // Roo.log('init events?');
2464        // Roo.log(this.el.dom);
2465         this.el.select('a',true).on('click', this.onClick, this);
2466     },
2467     
2468     onClick : function(e)
2469     {
2470         if(this.preventDefault){
2471             e.preventDefault();
2472         }
2473         
2474         if(this.fireEvent('click', this, e) === false){
2475             return;
2476         };
2477         
2478         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2479             this.onTabsClick(e);
2480         } 
2481     },
2482     
2483     onTabsClick : function(e)
2484     {
2485         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2486             v.removeClass('active');
2487         })
2488
2489         this.el.addClass('active');
2490
2491         if(this.href && this.href.substring(0,1) == '#'){
2492             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2493
2494             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2495                 v.removeClass('active');
2496             });
2497
2498             tab.addClass('active');
2499         }
2500     }
2501    
2502 });
2503  
2504
2505  /*
2506  * - LGPL
2507  *
2508  * row
2509  * 
2510  */
2511
2512 /**
2513  * @class Roo.bootstrap.Row
2514  * @extends Roo.bootstrap.Component
2515  * Bootstrap Row class (contains columns...)
2516  * 
2517  * @constructor
2518  * Create a new Row
2519  * @param {Object} config The config object
2520  */
2521
2522 Roo.bootstrap.Row = function(config){
2523     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 };
2525
2526 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2527     
2528     getAutoCreate : function(){
2529        return {
2530             cls: 'row clearfix'
2531        };
2532     }
2533     
2534     
2535 });
2536
2537  
2538
2539  /*
2540  * - LGPL
2541  *
2542  * element
2543  * 
2544  */
2545
2546 /**
2547  * @class Roo.bootstrap.Element
2548  * @extends Roo.bootstrap.Component
2549  * Bootstrap Element class
2550  * @cfg {String} html contents of the element
2551  * @cfg {String} tag tag of the element
2552  * @cfg {String} cls class of the element
2553  * 
2554  * @constructor
2555  * Create a new Element
2556  * @param {Object} config The config object
2557  */
2558
2559 Roo.bootstrap.Element = function(config){
2560     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 };
2562
2563 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2564     
2565     tag: 'div',
2566     cls: '',
2567     html: '',
2568      
2569     
2570     getAutoCreate : function(){
2571         
2572         var cfg = {
2573             tag: this.tag,
2574             cls: this.cls,
2575             html: this.html
2576         }
2577         
2578         
2579         
2580         return cfg;
2581     }
2582    
2583 });
2584
2585  
2586
2587  /*
2588  * - LGPL
2589  *
2590  * pagination
2591  * 
2592  */
2593
2594 /**
2595  * @class Roo.bootstrap.Pagination
2596  * @extends Roo.bootstrap.Component
2597  * Bootstrap Pagination class
2598  * @cfg {String} size xs | sm | md | lg
2599  * @cfg {Boolean} inverse false | true
2600  * 
2601  * @constructor
2602  * Create a new Pagination
2603  * @param {Object} config The config object
2604  */
2605
2606 Roo.bootstrap.Pagination = function(config){
2607     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 };
2609
2610 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2611     
2612     cls: false,
2613     size: false,
2614     inverse: false,
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             tag: 'ul',
2619                 cls: 'pagination'
2620         };
2621         if (this.inverse) {
2622             cfg.cls += ' inverse';
2623         }
2624         if (this.html) {
2625             cfg.html=this.html;
2626         }
2627         if (this.cls) {
2628             cfg.cls += " " + this.cls;
2629         }
2630         return cfg;
2631     }
2632    
2633 });
2634
2635  
2636
2637  /*
2638  * - LGPL
2639  *
2640  * Pagination item
2641  * 
2642  */
2643
2644
2645 /**
2646  * @class Roo.bootstrap.PaginationItem
2647  * @extends Roo.bootstrap.Component
2648  * Bootstrap PaginationItem class
2649  * @cfg {String} html text
2650  * @cfg {String} href the link
2651  * @cfg {Boolean} preventDefault (true | false) default true
2652  * @cfg {Boolean} active (true | false) default false
2653  * 
2654  * 
2655  * @constructor
2656  * Create a new PaginationItem
2657  * @param {Object} config The config object
2658  */
2659
2660
2661 Roo.bootstrap.PaginationItem = function(config){
2662     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event click
2667          * The raw click event for the entire grid.
2668          * @param {Roo.EventObject} e
2669          */
2670         "click" : true
2671     });
2672 };
2673
2674 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2675     
2676     href : false,
2677     html : false,
2678     preventDefault: true,
2679     active : false,
2680     cls : false,
2681     
2682     getAutoCreate : function(){
2683         var cfg= {
2684             tag: 'li',
2685             cn: [
2686                 {
2687                     tag : 'a',
2688                     href : this.href ? this.href : '#',
2689                     html : this.html ? this.html : ''
2690                 }
2691             ]
2692         };
2693         
2694         if(this.cls){
2695             cfg.cls = this.cls;
2696         }
2697         
2698         if(this.active){
2699             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2700         }
2701         
2702         return cfg;
2703     },
2704     
2705     initEvents: function() {
2706         
2707         this.el.on('click', this.onClick, this);
2708         
2709     },
2710     onClick : function(e)
2711     {
2712         Roo.log('PaginationItem on click ');
2713         if(this.preventDefault){
2714             e.preventDefault();
2715         }
2716         
2717         this.fireEvent('click', this, e);
2718     }
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * slider
2728  * 
2729  */
2730
2731
2732 /**
2733  * @class Roo.bootstrap.Slider
2734  * @extends Roo.bootstrap.Component
2735  * Bootstrap Slider class
2736  *    
2737  * @constructor
2738  * Create a new Slider
2739  * @param {Object} config The config object
2740  */
2741
2742 Roo.bootstrap.Slider = function(config){
2743     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 };
2745
2746 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2747     
2748     getAutoCreate : function(){
2749         
2750         var cfg = {
2751             tag: 'div',
2752             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2753             cn: [
2754                 {
2755                     tag: 'a',
2756                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2757                 }
2758             ]
2759         }
2760         
2761         return cfg;
2762     }
2763    
2764 });
2765
2766  /*
2767  * - LGPL
2768  *
2769  * table
2770  * 
2771  */
2772
2773 /**
2774  * @class Roo.bootstrap.Table
2775  * @extends Roo.bootstrap.Component
2776  * Bootstrap Table class
2777  * @cfg {String} cls table class
2778  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2779  * @cfg {String} bgcolor Specifies the background color for a table
2780  * @cfg {Number} border Specifies whether the table cells should have borders or not
2781  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2782  * @cfg {Number} cellspacing Specifies the space between cells
2783  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2784  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2785  * @cfg {String} sortable Specifies that the table should be sortable
2786  * @cfg {String} summary Specifies a summary of the content of a table
2787  * @cfg {Number} width Specifies the width of a table
2788  * 
2789  * @cfg {boolean} striped Should the rows be alternative striped
2790  * @cfg {boolean} bordered Add borders to the table
2791  * @cfg {boolean} hover Add hover highlighting
2792  * @cfg {boolean} condensed Format condensed
2793  * @cfg {boolean} responsive Format condensed
2794  *
2795  
2796  
2797  * 
2798  * @constructor
2799  * Create a new Table
2800  * @param {Object} config The config object
2801  */
2802
2803 Roo.bootstrap.Table = function(config){
2804     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805     
2806     if (this.sm) {
2807         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2808         this.sm = this.selModel;
2809         this.sm.xmodule = this.xmodule || false;
2810     }
2811     if (this.cm && typeof(this.cm.config) == 'undefined') {
2812         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2813         this.cm = this.colModel;
2814         this.cm.xmodule = this.xmodule || false;
2815     }
2816     if (this.store) {
2817         this.store= Roo.factory(this.store, Roo.data);
2818         this.ds = this.store;
2819         this.ds.xmodule = this.xmodule || false;
2820          
2821     }
2822 };
2823
2824 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2825     
2826     cls: false,
2827     align: false,
2828     bgcolor: false,
2829     border: false,
2830     cellpadding: false,
2831     cellspacing: false,
2832     frame: false,
2833     rules: false,
2834     sortable: false,
2835     summary: false,
2836     width: false,
2837     striped : false,
2838     bordered: false,
2839     hover:  false,
2840     condensed : false,
2841     responsive : false,
2842     sm : false,
2843     cm : false,
2844     store : false,
2845     
2846     getAutoCreate : function(){
2847         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2848         
2849         cfg = {
2850             tag: 'table',
2851             cls : 'table',
2852             cn : []
2853         }
2854             
2855         if (this.striped) {
2856             cfg.cls += ' table-striped';
2857         }
2858         if (this.hover) {
2859             cfg.cls += ' table-hover';
2860         }
2861         if (this.bordered) {
2862             cfg.cls += ' table-bordered';
2863         }
2864         if (this.condensed) {
2865             cfg.cls += ' table-condensed';
2866         }
2867         if (this.responsive) {
2868             cfg.cls += ' table-responsive';
2869         }
2870         
2871           
2872         
2873         
2874         if (this.cls) {
2875             cfg.cls+=  ' ' +this.cls;
2876         }
2877         
2878         // this lot should be simplifed...
2879         
2880         if (this.align) {
2881             cfg.align=this.align;
2882         }
2883         if (this.bgcolor) {
2884             cfg.bgcolor=this.bgcolor;
2885         }
2886         if (this.border) {
2887             cfg.border=this.border;
2888         }
2889         if (this.cellpadding) {
2890             cfg.cellpadding=this.cellpadding;
2891         }
2892         if (this.cellspacing) {
2893             cfg.cellspacing=this.cellspacing;
2894         }
2895         if (this.frame) {
2896             cfg.frame=this.frame;
2897         }
2898         if (this.rules) {
2899             cfg.rules=this.rules;
2900         }
2901         if (this.sortable) {
2902             cfg.sortable=this.sortable;
2903         }
2904         if (this.summary) {
2905             cfg.summary=this.summary;
2906         }
2907         if (this.width) {
2908             cfg.width=this.width;
2909         }
2910         
2911         if(this.store || this.cm){
2912             cfg.cn.push(this.renderHeader());
2913             cfg.cn.push(this.renderBody());
2914             cfg.cn.push(this.renderFooter());
2915             
2916             cfg.cls+=  ' TableGrid';
2917         }
2918         
2919         return cfg;
2920     },
2921 //    
2922 //    initTableGrid : function()
2923 //    {
2924 //        var cfg = {};
2925 //        
2926 //        var header = {
2927 //            tag: 'thead',
2928 //            cn : []
2929 //        };
2930 //        
2931 //        var cm = this.cm;
2932 //        
2933 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 //            header.cn.push({
2935 //                tag: 'th',
2936 //                html: cm.getColumnHeader(i)
2937 //            })
2938 //        }
2939 //        
2940 //        cfg.push(header);
2941 //        
2942 //        return cfg;
2943 //        
2944 //        
2945 //    },
2946     
2947     initEvents : function()
2948     {   
2949         if(!this.store || !this.cm){
2950             return;
2951         }
2952         
2953         Roo.log('initEvents with ds!!!!');
2954         
2955 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2956 //        this.maskEl.enableDisplayMode("block");
2957 //        this.maskEl.show();
2958         
2959         this.store.on('load', this.onLoad, this);
2960         this.store.on('beforeload', this.onBeforeLoad, this);
2961         
2962         this.store.load();
2963         
2964         
2965         
2966     },
2967     
2968     renderHeader : function()
2969     {
2970         var header = {
2971             tag: 'thead',
2972             cn : []
2973         };
2974         
2975         var cm = this.cm;
2976         
2977         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2978             header.cn.push({
2979                 tag: 'th',
2980                 html: cm.getColumnHeader(i)
2981             })
2982         }
2983         
2984         return header;
2985     },
2986     
2987     renderBody : function()
2988     {
2989         var body = {
2990             tag: 'tbody',
2991             cn : []
2992         };
2993         
2994         return body;
2995     },
2996     
2997     renderFooter : function()
2998     {
2999         var footer = {
3000             tag: 'tfoot',
3001             cn : []
3002         };
3003         
3004         return footer;
3005     },
3006     
3007     onLoad : function()
3008     {
3009         Roo.log('ds onload');
3010         
3011         var cm = this.cm;
3012         
3013         var tbody = this.el.select('tbody', true).first();
3014         
3015         var renders = [];
3016         
3017         if(this.store.getCount() > 0){
3018             this.store.data.each(function(d){
3019                 var row = {
3020                     tag : 'tr',
3021                     cn : []
3022                 };
3023                 
3024                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3025                     var renderer = cm.getRenderer(i);
3026                     var value = '';
3027                     var id = Roo.id();
3028                     
3029                     if(typeof(renderer) !== 'undefined'){
3030                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3031                     }
3032                     
3033                     if(typeof(value) === 'object'){
3034                         renders.push({
3035                             id : id,
3036                             cfg : value 
3037                         })
3038                     }
3039                     
3040                     row.cn.push({
3041                         tag: 'td',
3042                         id: id,
3043                         html: (typeof(value) === 'object') ? '' : value
3044                     })
3045                    
3046                 }
3047                 
3048                 tbody.createChild(row);
3049                 
3050             });
3051         }
3052         
3053         if(renders.length){
3054             Roo.each(renders, function(r){
3055                 r.cfg.render(Roo.get(r.id));
3056             })
3057         }
3058 //        
3059 //        if(this.loadMask){
3060 //            this.maskEl.hide();
3061 //        }
3062     },
3063     
3064     onBeforeLoad : function()
3065     {
3066         Roo.log('ds onBeforeLoad');
3067         
3068         this.clear();
3069         
3070 //        if(this.loadMask){
3071 //            this.maskEl.show();
3072 //        }
3073     },
3074     
3075     clear : function()
3076     {
3077         this.el.select('tbody', true).first().dom.innerHTML = '';
3078     },
3079     
3080     getSelectionModel : function(){
3081         if(!this.selModel){
3082             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3083         }
3084         return this.selModel;
3085     }
3086    
3087 });
3088
3089  
3090
3091  /*
3092  * - LGPL
3093  *
3094  * table cell
3095  * 
3096  */
3097
3098 /**
3099  * @class Roo.bootstrap.TableCell
3100  * @extends Roo.bootstrap.Component
3101  * Bootstrap TableCell class
3102  * @cfg {String} html cell contain text
3103  * @cfg {String} cls cell class
3104  * @cfg {String} tag cell tag (td|th) default td
3105  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3106  * @cfg {String} align Aligns the content in a cell
3107  * @cfg {String} axis Categorizes cells
3108  * @cfg {String} bgcolor Specifies the background color of a cell
3109  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3110  * @cfg {Number} colspan Specifies the number of columns a cell should span
3111  * @cfg {String} headers Specifies one or more header cells a cell is related to
3112  * @cfg {Number} height Sets the height of a cell
3113  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3114  * @cfg {Number} rowspan Sets the number of rows a cell should span
3115  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3116  * @cfg {String} valign Vertical aligns the content in a cell
3117  * @cfg {Number} width Specifies the width of a cell
3118  * 
3119  * @constructor
3120  * Create a new TableCell
3121  * @param {Object} config The config object
3122  */
3123
3124 Roo.bootstrap.TableCell = function(config){
3125     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3126 };
3127
3128 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3129     
3130     html: false,
3131     cls: false,
3132     tag: false,
3133     abbr: false,
3134     align: false,
3135     axis: false,
3136     bgcolor: false,
3137     charoff: false,
3138     colspan: false,
3139     headers: false,
3140     height: false,
3141     nowrap: false,
3142     rowspan: false,
3143     scope: false,
3144     valign: false,
3145     width: false,
3146     
3147     
3148     getAutoCreate : function(){
3149         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3150         
3151         cfg = {
3152             tag: 'td'
3153         }
3154         
3155         if(this.tag){
3156             cfg.tag = this.tag;
3157         }
3158         
3159         if (this.html) {
3160             cfg.html=this.html
3161         }
3162         if (this.cls) {
3163             cfg.cls=this.cls
3164         }
3165         if (this.abbr) {
3166             cfg.abbr=this.abbr
3167         }
3168         if (this.align) {
3169             cfg.align=this.align
3170         }
3171         if (this.axis) {
3172             cfg.axis=this.axis
3173         }
3174         if (this.bgcolor) {
3175             cfg.bgcolor=this.bgcolor
3176         }
3177         if (this.charoff) {
3178             cfg.charoff=this.charoff
3179         }
3180         if (this.colspan) {
3181             cfg.colspan=this.colspan
3182         }
3183         if (this.headers) {
3184             cfg.headers=this.headers
3185         }
3186         if (this.height) {
3187             cfg.height=this.height
3188         }
3189         if (this.nowrap) {
3190             cfg.nowrap=this.nowrap
3191         }
3192         if (this.rowspan) {
3193             cfg.rowspan=this.rowspan
3194         }
3195         if (this.scope) {
3196             cfg.scope=this.scope
3197         }
3198         if (this.valign) {
3199             cfg.valign=this.valign
3200         }
3201         if (this.width) {
3202             cfg.width=this.width
3203         }
3204         
3205         
3206         return cfg;
3207     }
3208    
3209 });
3210
3211  
3212
3213  /*
3214  * - LGPL
3215  *
3216  * table row
3217  * 
3218  */
3219
3220 /**
3221  * @class Roo.bootstrap.TableRow
3222  * @extends Roo.bootstrap.Component
3223  * Bootstrap TableRow class
3224  * @cfg {String} cls row class
3225  * @cfg {String} align Aligns the content in a table row
3226  * @cfg {String} bgcolor Specifies a background color for a table row
3227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3228  * @cfg {String} valign Vertical aligns the content in a table row
3229  * 
3230  * @constructor
3231  * Create a new TableRow
3232  * @param {Object} config The config object
3233  */
3234
3235 Roo.bootstrap.TableRow = function(config){
3236     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3237 };
3238
3239 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3240     
3241     cls: false,
3242     align: false,
3243     bgcolor: false,
3244     charoff: false,
3245     valign: false,
3246     
3247     getAutoCreate : function(){
3248         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3249         
3250         cfg = {
3251             tag: 'tr'
3252         }
3253             
3254         if(this.cls){
3255             cfg.cls = this.cls;
3256         }
3257         if(this.align){
3258             cfg.align = this.align;
3259         }
3260         if(this.bgcolor){
3261             cfg.bgcolor = this.bgcolor;
3262         }
3263         if(this.charoff){
3264             cfg.charoff = this.charoff;
3265         }
3266         if(this.valign){
3267             cfg.valign = this.valign;
3268         }
3269         
3270         return cfg;
3271     }
3272    
3273 });
3274
3275  
3276
3277  /*
3278  * - LGPL
3279  *
3280  * table body
3281  * 
3282  */
3283
3284 /**
3285  * @class Roo.bootstrap.TableBody
3286  * @extends Roo.bootstrap.Component
3287  * Bootstrap TableBody class
3288  * @cfg {String} cls element class
3289  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3290  * @cfg {String} align Aligns the content inside the element
3291  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3292  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3293  * 
3294  * @constructor
3295  * Create a new TableBody
3296  * @param {Object} config The config object
3297  */
3298
3299 Roo.bootstrap.TableBody = function(config){
3300     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3301 };
3302
3303 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3304     
3305     cls: false,
3306     tag: false,
3307     align: false,
3308     charoff: false,
3309     valign: false,
3310     
3311     getAutoCreate : function(){
3312         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3313         
3314         cfg = {
3315             tag: 'tbody'
3316         }
3317             
3318         if (this.cls) {
3319             cfg.cls=this.cls
3320         }
3321         if(this.tag){
3322             cfg.tag = this.tag;
3323         }
3324         
3325         if(this.align){
3326             cfg.align = this.align;
3327         }
3328         if(this.charoff){
3329             cfg.charoff = this.charoff;
3330         }
3331         if(this.valign){
3332             cfg.valign = this.valign;
3333         }
3334         
3335         return cfg;
3336     }
3337     
3338     
3339 //    initEvents : function()
3340 //    {
3341 //        
3342 //        if(!this.store){
3343 //            return;
3344 //        }
3345 //        
3346 //        this.store = Roo.factory(this.store, Roo.data);
3347 //        this.store.on('load', this.onLoad, this);
3348 //        
3349 //        this.store.load();
3350 //        
3351 //    },
3352 //    
3353 //    onLoad: function () 
3354 //    {   
3355 //        this.fireEvent('load', this);
3356 //    }
3357 //    
3358 //   
3359 });
3360
3361  
3362
3363  /*
3364  * Based on:
3365  * Ext JS Library 1.1.1
3366  * Copyright(c) 2006-2007, Ext JS, LLC.
3367  *
3368  * Originally Released Under LGPL - original licence link has changed is not relivant.
3369  *
3370  * Fork - LGPL
3371  * <script type="text/javascript">
3372  */
3373
3374 // as we use this in bootstrap.
3375 Roo.namespace('Roo.form');
3376  /**
3377  * @class Roo.form.Action
3378  * Internal Class used to handle form actions
3379  * @constructor
3380  * @param {Roo.form.BasicForm} el The form element or its id
3381  * @param {Object} config Configuration options
3382  */
3383
3384  
3385  
3386 // define the action interface
3387 Roo.form.Action = function(form, options){
3388     this.form = form;
3389     this.options = options || {};
3390 };
3391 /**
3392  * Client Validation Failed
3393  * @const 
3394  */
3395 Roo.form.Action.CLIENT_INVALID = 'client';
3396 /**
3397  * Server Validation Failed
3398  * @const 
3399  */
3400 Roo.form.Action.SERVER_INVALID = 'server';
3401  /**
3402  * Connect to Server Failed
3403  * @const 
3404  */
3405 Roo.form.Action.CONNECT_FAILURE = 'connect';
3406 /**
3407  * Reading Data from Server Failed
3408  * @const 
3409  */
3410 Roo.form.Action.LOAD_FAILURE = 'load';
3411
3412 Roo.form.Action.prototype = {
3413     type : 'default',
3414     failureType : undefined,
3415     response : undefined,
3416     result : undefined,
3417
3418     // interface method
3419     run : function(options){
3420
3421     },
3422
3423     // interface method
3424     success : function(response){
3425
3426     },
3427
3428     // interface method
3429     handleResponse : function(response){
3430
3431     },
3432
3433     // default connection failure
3434     failure : function(response){
3435         
3436         this.response = response;
3437         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3438         this.form.afterAction(this, false);
3439     },
3440
3441     processResponse : function(response){
3442         this.response = response;
3443         if(!response.responseText){
3444             return true;
3445         }
3446         this.result = this.handleResponse(response);
3447         return this.result;
3448     },
3449
3450     // utility functions used internally
3451     getUrl : function(appendParams){
3452         var url = this.options.url || this.form.url || this.form.el.dom.action;
3453         if(appendParams){
3454             var p = this.getParams();
3455             if(p){
3456                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3457             }
3458         }
3459         return url;
3460     },
3461
3462     getMethod : function(){
3463         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3464     },
3465
3466     getParams : function(){
3467         var bp = this.form.baseParams;
3468         var p = this.options.params;
3469         if(p){
3470             if(typeof p == "object"){
3471                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3472             }else if(typeof p == 'string' && bp){
3473                 p += '&' + Roo.urlEncode(bp);
3474             }
3475         }else if(bp){
3476             p = Roo.urlEncode(bp);
3477         }
3478         return p;
3479     },
3480
3481     createCallback : function(){
3482         return {
3483             success: this.success,
3484             failure: this.failure,
3485             scope: this,
3486             timeout: (this.form.timeout*1000),
3487             upload: this.form.fileUpload ? this.success : undefined
3488         };
3489     }
3490 };
3491
3492 Roo.form.Action.Submit = function(form, options){
3493     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3494 };
3495
3496 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3497     type : 'submit',
3498
3499     haveProgress : false,
3500     uploadComplete : false,
3501     
3502     // uploadProgress indicator.
3503     uploadProgress : function()
3504     {
3505         if (!this.form.progressUrl) {
3506             return;
3507         }
3508         
3509         if (!this.haveProgress) {
3510             Roo.MessageBox.progress("Uploading", "Uploading");
3511         }
3512         if (this.uploadComplete) {
3513            Roo.MessageBox.hide();
3514            return;
3515         }
3516         
3517         this.haveProgress = true;
3518    
3519         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3520         
3521         var c = new Roo.data.Connection();
3522         c.request({
3523             url : this.form.progressUrl,
3524             params: {
3525                 id : uid
3526             },
3527             method: 'GET',
3528             success : function(req){
3529                //console.log(data);
3530                 var rdata = false;
3531                 var edata;
3532                 try  {
3533                    rdata = Roo.decode(req.responseText)
3534                 } catch (e) {
3535                     Roo.log("Invalid data from server..");
3536                     Roo.log(edata);
3537                     return;
3538                 }
3539                 if (!rdata || !rdata.success) {
3540                     Roo.log(rdata);
3541                     Roo.MessageBox.alert(Roo.encode(rdata));
3542                     return;
3543                 }
3544                 var data = rdata.data;
3545                 
3546                 if (this.uploadComplete) {
3547                    Roo.MessageBox.hide();
3548                    return;
3549                 }
3550                    
3551                 if (data){
3552                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3553                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3554                     );
3555                 }
3556                 this.uploadProgress.defer(2000,this);
3557             },
3558        
3559             failure: function(data) {
3560                 Roo.log('progress url failed ');
3561                 Roo.log(data);
3562             },
3563             scope : this
3564         });
3565            
3566     },
3567     
3568     
3569     run : function()
3570     {
3571         // run get Values on the form, so it syncs any secondary forms.
3572         this.form.getValues();
3573         
3574         var o = this.options;
3575         var method = this.getMethod();
3576         var isPost = method == 'POST';
3577         if(o.clientValidation === false || this.form.isValid()){
3578             
3579             if (this.form.progressUrl) {
3580                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3581                     (new Date() * 1) + '' + Math.random());
3582                     
3583             } 
3584             
3585             
3586             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3587                 form:this.form.el.dom,
3588                 url:this.getUrl(!isPost),
3589                 method: method,
3590                 params:isPost ? this.getParams() : null,
3591                 isUpload: this.form.fileUpload
3592             }));
3593             
3594             this.uploadProgress();
3595
3596         }else if (o.clientValidation !== false){ // client validation failed
3597             this.failureType = Roo.form.Action.CLIENT_INVALID;
3598             this.form.afterAction(this, false);
3599         }
3600     },
3601
3602     success : function(response)
3603     {
3604         this.uploadComplete= true;
3605         if (this.haveProgress) {
3606             Roo.MessageBox.hide();
3607         }
3608         
3609         
3610         var result = this.processResponse(response);
3611         if(result === true || result.success){
3612             this.form.afterAction(this, true);
3613             return;
3614         }
3615         if(result.errors){
3616             this.form.markInvalid(result.errors);
3617             this.failureType = Roo.form.Action.SERVER_INVALID;
3618         }
3619         this.form.afterAction(this, false);
3620     },
3621     failure : function(response)
3622     {
3623         this.uploadComplete= true;
3624         if (this.haveProgress) {
3625             Roo.MessageBox.hide();
3626         }
3627         
3628         this.response = response;
3629         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3630         this.form.afterAction(this, false);
3631     },
3632     
3633     handleResponse : function(response){
3634         if(this.form.errorReader){
3635             var rs = this.form.errorReader.read(response);
3636             var errors = [];
3637             if(rs.records){
3638                 for(var i = 0, len = rs.records.length; i < len; i++) {
3639                     var r = rs.records[i];
3640                     errors[i] = r.data;
3641                 }
3642             }
3643             if(errors.length < 1){
3644                 errors = null;
3645             }
3646             return {
3647                 success : rs.success,
3648                 errors : errors
3649             };
3650         }
3651         var ret = false;
3652         try {
3653             ret = Roo.decode(response.responseText);
3654         } catch (e) {
3655             ret = {
3656                 success: false,
3657                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3658                 errors : []
3659             };
3660         }
3661         return ret;
3662         
3663     }
3664 });
3665
3666
3667 Roo.form.Action.Load = function(form, options){
3668     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3669     this.reader = this.form.reader;
3670 };
3671
3672 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3673     type : 'load',
3674
3675     run : function(){
3676         
3677         Roo.Ajax.request(Roo.apply(
3678                 this.createCallback(), {
3679                     method:this.getMethod(),
3680                     url:this.getUrl(false),
3681                     params:this.getParams()
3682         }));
3683     },
3684
3685     success : function(response){
3686         
3687         var result = this.processResponse(response);
3688         if(result === true || !result.success || !result.data){
3689             this.failureType = Roo.form.Action.LOAD_FAILURE;
3690             this.form.afterAction(this, false);
3691             return;
3692         }
3693         this.form.clearInvalid();
3694         this.form.setValues(result.data);
3695         this.form.afterAction(this, true);
3696     },
3697
3698     handleResponse : function(response){
3699         if(this.form.reader){
3700             var rs = this.form.reader.read(response);
3701             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3702             return {
3703                 success : rs.success,
3704                 data : data
3705             };
3706         }
3707         return Roo.decode(response.responseText);
3708     }
3709 });
3710
3711 Roo.form.Action.ACTION_TYPES = {
3712     'load' : Roo.form.Action.Load,
3713     'submit' : Roo.form.Action.Submit
3714 };/*
3715  * - LGPL
3716  *
3717  * form
3718  * 
3719  */
3720
3721 /**
3722  * @class Roo.bootstrap.Form
3723  * @extends Roo.bootstrap.Component
3724  * Bootstrap Form class
3725  * @cfg {String} method  GET | POST (default POST)
3726  * @cfg {String} labelAlign top | left (default top)
3727   * @cfg {String} align left  | right - for navbars
3728
3729  * 
3730  * @constructor
3731  * Create a new Form
3732  * @param {Object} config The config object
3733  */
3734
3735
3736 Roo.bootstrap.Form = function(config){
3737     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3738     this.addEvents({
3739         /**
3740          * @event clientvalidation
3741          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3742          * @param {Form} this
3743          * @param {Boolean} valid true if the form has passed client-side validation
3744          */
3745         clientvalidation: true,
3746         /**
3747          * @event beforeaction
3748          * Fires before any action is performed. Return false to cancel the action.
3749          * @param {Form} this
3750          * @param {Action} action The action to be performed
3751          */
3752         beforeaction: true,
3753         /**
3754          * @event actionfailed
3755          * Fires when an action fails.
3756          * @param {Form} this
3757          * @param {Action} action The action that failed
3758          */
3759         actionfailed : true,
3760         /**
3761          * @event actioncomplete
3762          * Fires when an action is completed.
3763          * @param {Form} this
3764          * @param {Action} action The action that completed
3765          */
3766         actioncomplete : true
3767     });
3768     
3769 };
3770
3771 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3772       
3773      /**
3774      * @cfg {String} method
3775      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3776      */
3777     method : 'POST',
3778     /**
3779      * @cfg {String} url
3780      * The URL to use for form actions if one isn't supplied in the action options.
3781      */
3782     /**
3783      * @cfg {Boolean} fileUpload
3784      * Set to true if this form is a file upload.
3785      */
3786      
3787     /**
3788      * @cfg {Object} baseParams
3789      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3790      */
3791       
3792     /**
3793      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3794      */
3795     timeout: 30,
3796     /**
3797      * @cfg {Sting} align (left|right) for navbar forms
3798      */
3799     align : 'left',
3800
3801     // private
3802     activeAction : null,
3803  
3804     /**
3805      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3806      * element by passing it or its id or mask the form itself by passing in true.
3807      * @type Mixed
3808      */
3809     waitMsgTarget : false,
3810     
3811      
3812     
3813     /**
3814      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3815      * element by passing it or its id or mask the form itself by passing in true.
3816      * @type Mixed
3817      */
3818     
3819     getAutoCreate : function(){
3820         
3821         var cfg = {
3822             tag: 'form',
3823             method : this.method || 'POST',
3824             id : this.id || Roo.id(),
3825             cls : ''
3826         }
3827         if (this.parent().xtype.match(/^Nav/)) {
3828             cfg.cls = 'navbar-form navbar-' + this.align;
3829             
3830         }
3831         
3832         if (this.labelAlign == 'left' ) {
3833             cfg.cls += ' form-horizontal';
3834         }
3835         
3836         
3837         return cfg;
3838     },
3839     initEvents : function()
3840     {
3841         this.el.on('submit', this.onSubmit, this);
3842         
3843         
3844     },
3845     // private
3846     onSubmit : function(e){
3847         e.stopEvent();
3848     },
3849     
3850      /**
3851      * Returns true if client-side validation on the form is successful.
3852      * @return Boolean
3853      */
3854     isValid : function(){
3855         var items = this.getItems();
3856         var valid = true;
3857         items.each(function(f){
3858            if(!f.validate()){
3859                valid = false;
3860                
3861            }
3862         });
3863         return valid;
3864     },
3865     /**
3866      * Returns true if any fields in this form have changed since their original load.
3867      * @return Boolean
3868      */
3869     isDirty : function(){
3870         var dirty = false;
3871         var items = this.getItems();
3872         items.each(function(f){
3873            if(f.isDirty()){
3874                dirty = true;
3875                return false;
3876            }
3877            return true;
3878         });
3879         return dirty;
3880     },
3881      /**
3882      * Performs a predefined action (submit or load) or custom actions you define on this form.
3883      * @param {String} actionName The name of the action type
3884      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3885      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3886      * accept other config options):
3887      * <pre>
3888 Property          Type             Description
3889 ----------------  ---------------  ----------------------------------------------------------------------------------
3890 url               String           The url for the action (defaults to the form's url)
3891 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3892 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3893 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3894                                    validate the form on the client (defaults to false)
3895      * </pre>
3896      * @return {BasicForm} this
3897      */
3898     doAction : function(action, options){
3899         if(typeof action == 'string'){
3900             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3901         }
3902         if(this.fireEvent('beforeaction', this, action) !== false){
3903             this.beforeAction(action);
3904             action.run.defer(100, action);
3905         }
3906         return this;
3907     },
3908     
3909     // private
3910     beforeAction : function(action){
3911         var o = action.options;
3912         
3913         // not really supported yet.. ??
3914         
3915         //if(this.waitMsgTarget === true){
3916             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3917         //}else if(this.waitMsgTarget){
3918         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3919         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3920         //}else {
3921         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3922        // }
3923          
3924     },
3925
3926     // private
3927     afterAction : function(action, success){
3928         this.activeAction = null;
3929         var o = action.options;
3930         
3931         //if(this.waitMsgTarget === true){
3932             this.el.unmask();
3933         //}else if(this.waitMsgTarget){
3934         //    this.waitMsgTarget.unmask();
3935         //}else{
3936         //    Roo.MessageBox.updateProgress(1);
3937         //    Roo.MessageBox.hide();
3938        // }
3939         // 
3940         if(success){
3941             if(o.reset){
3942                 this.reset();
3943             }
3944             Roo.callback(o.success, o.scope, [this, action]);
3945             this.fireEvent('actioncomplete', this, action);
3946             
3947         }else{
3948             
3949             // failure condition..
3950             // we have a scenario where updates need confirming.
3951             // eg. if a locking scenario exists..
3952             // we look for { errors : { needs_confirm : true }} in the response.
3953             if (
3954                 (typeof(action.result) != 'undefined')  &&
3955                 (typeof(action.result.errors) != 'undefined')  &&
3956                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3957            ){
3958                 var _t = this;
3959                 Roo.log("not supported yet");
3960                  /*
3961                 
3962                 Roo.MessageBox.confirm(
3963                     "Change requires confirmation",
3964                     action.result.errorMsg,
3965                     function(r) {
3966                         if (r != 'yes') {
3967                             return;
3968                         }
3969                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3970                     }
3971                     
3972                 );
3973                 */
3974                 
3975                 
3976                 return;
3977             }
3978             
3979             Roo.callback(o.failure, o.scope, [this, action]);
3980             // show an error message if no failed handler is set..
3981             if (!this.hasListener('actionfailed')) {
3982                 Roo.log("need to add dialog support");
3983                 /*
3984                 Roo.MessageBox.alert("Error",
3985                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3986                         action.result.errorMsg :
3987                         "Saving Failed, please check your entries or try again"
3988                 );
3989                 */
3990             }
3991             
3992             this.fireEvent('actionfailed', this, action);
3993         }
3994         
3995     },
3996     /**
3997      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3998      * @param {String} id The value to search for
3999      * @return Field
4000      */
4001     findField : function(id){
4002         var items = this.getItems();
4003         var field = items.get(id);
4004         if(!field){
4005              items.each(function(f){
4006                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4007                     field = f;
4008                     return false;
4009                 }
4010                 return true;
4011             });
4012         }
4013         return field || null;
4014     },
4015      /**
4016      * Mark fields in this form invalid in bulk.
4017      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4018      * @return {BasicForm} this
4019      */
4020     markInvalid : function(errors){
4021         if(errors instanceof Array){
4022             for(var i = 0, len = errors.length; i < len; i++){
4023                 var fieldError = errors[i];
4024                 var f = this.findField(fieldError.id);
4025                 if(f){
4026                     f.markInvalid(fieldError.msg);
4027                 }
4028             }
4029         }else{
4030             var field, id;
4031             for(id in errors){
4032                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4033                     field.markInvalid(errors[id]);
4034                 }
4035             }
4036         }
4037         //Roo.each(this.childForms || [], function (f) {
4038         //    f.markInvalid(errors);
4039         //});
4040         
4041         return this;
4042     },
4043
4044     /**
4045      * Set values for fields in this form in bulk.
4046      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4047      * @return {BasicForm} this
4048      */
4049     setValues : function(values){
4050         if(values instanceof Array){ // array of objects
4051             for(var i = 0, len = values.length; i < len; i++){
4052                 var v = values[i];
4053                 var f = this.findField(v.id);
4054                 if(f){
4055                     f.setValue(v.value);
4056                     if(this.trackResetOnLoad){
4057                         f.originalValue = f.getValue();
4058                     }
4059                 }
4060             }
4061         }else{ // object hash
4062             var field, id;
4063             for(id in values){
4064                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4065                     
4066                     if (field.setFromData && 
4067                         field.valueField && 
4068                         field.displayField &&
4069                         // combos' with local stores can 
4070                         // be queried via setValue()
4071                         // to set their value..
4072                         (field.store && !field.store.isLocal)
4073                         ) {
4074                         // it's a combo
4075                         var sd = { };
4076                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4077                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4078                         field.setFromData(sd);
4079                         
4080                     } else {
4081                         field.setValue(values[id]);
4082                     }
4083                     
4084                     
4085                     if(this.trackResetOnLoad){
4086                         field.originalValue = field.getValue();
4087                     }
4088                 }
4089             }
4090         }
4091          
4092         //Roo.each(this.childForms || [], function (f) {
4093         //    f.setValues(values);
4094         //});
4095                 
4096         return this;
4097     },
4098
4099     /**
4100      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4101      * they are returned as an array.
4102      * @param {Boolean} asString
4103      * @return {Object}
4104      */
4105     getValues : function(asString){
4106         //if (this.childForms) {
4107             // copy values from the child forms
4108         //    Roo.each(this.childForms, function (f) {
4109         //        this.setValues(f.getValues());
4110         //    }, this);
4111         //}
4112         
4113         
4114         
4115         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4116         if(asString === true){
4117             return fs;
4118         }
4119         return Roo.urlDecode(fs);
4120     },
4121     
4122     /**
4123      * Returns the fields in this form as an object with key/value pairs. 
4124      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4125      * @return {Object}
4126      */
4127     getFieldValues : function(with_hidden)
4128     {
4129         var items = this.getItems();
4130         var ret = {};
4131         items.each(function(f){
4132             if (!f.getName()) {
4133                 return;
4134             }
4135             var v = f.getValue();
4136             if (f.inputType =='radio') {
4137                 if (typeof(ret[f.getName()]) == 'undefined') {
4138                     ret[f.getName()] = ''; // empty..
4139                 }
4140                 
4141                 if (!f.el.dom.checked) {
4142                     return;
4143                     
4144                 }
4145                 v = f.el.dom.value;
4146                 
4147             }
4148             
4149             // not sure if this supported any more..
4150             if ((typeof(v) == 'object') && f.getRawValue) {
4151                 v = f.getRawValue() ; // dates..
4152             }
4153             // combo boxes where name != hiddenName...
4154             if (f.name != f.getName()) {
4155                 ret[f.name] = f.getRawValue();
4156             }
4157             ret[f.getName()] = v;
4158         });
4159         
4160         return ret;
4161     },
4162
4163     /**
4164      * Clears all invalid messages in this form.
4165      * @return {BasicForm} this
4166      */
4167     clearInvalid : function(){
4168         var items = this.getItems();
4169         
4170         items.each(function(f){
4171            f.clearInvalid();
4172         });
4173         
4174         
4175         
4176         return this;
4177     },
4178
4179     /**
4180      * Resets this form.
4181      * @return {BasicForm} this
4182      */
4183     reset : function(){
4184         var items = this.getItems();
4185         items.each(function(f){
4186             f.reset();
4187         });
4188         
4189         Roo.each(this.childForms || [], function (f) {
4190             f.reset();
4191         });
4192        
4193         
4194         return this;
4195     },
4196     getItems : function()
4197     {
4198         var r=new Roo.util.MixedCollection(false, function(o){
4199             return o.id || (o.id = Roo.id());
4200         });
4201         var iter = function(el) {
4202             if (el.inputEl) {
4203                 r.add(el);
4204             }
4205             if (!el.items) {
4206                 return;
4207             }
4208             Roo.each(el.items,function(e) {
4209                 iter(e);
4210             });
4211             
4212             
4213         };
4214         iter(this);
4215         return r;
4216         
4217         
4218         
4219         
4220     }
4221     
4222 });
4223
4224  
4225 /*
4226  * Based on:
4227  * Ext JS Library 1.1.1
4228  * Copyright(c) 2006-2007, Ext JS, LLC.
4229  *
4230  * Originally Released Under LGPL - original licence link has changed is not relivant.
4231  *
4232  * Fork - LGPL
4233  * <script type="text/javascript">
4234  */
4235 /**
4236  * @class Roo.form.VTypes
4237  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4238  * @singleton
4239  */
4240 Roo.form.VTypes = function(){
4241     // closure these in so they are only created once.
4242     var alpha = /^[a-zA-Z_]+$/;
4243     var alphanum = /^[a-zA-Z0-9_]+$/;
4244     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4245     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4246
4247     // All these messages and functions are configurable
4248     return {
4249         /**
4250          * The function used to validate email addresses
4251          * @param {String} value The email address
4252          */
4253         'email' : function(v){
4254             return email.test(v);
4255         },
4256         /**
4257          * The error text to display when the email validation function returns false
4258          * @type String
4259          */
4260         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4261         /**
4262          * The keystroke filter mask to be applied on email input
4263          * @type RegExp
4264          */
4265         'emailMask' : /[a-z0-9_\.\-@]/i,
4266
4267         /**
4268          * The function used to validate URLs
4269          * @param {String} value The URL
4270          */
4271         'url' : function(v){
4272             return url.test(v);
4273         },
4274         /**
4275          * The error text to display when the url validation function returns false
4276          * @type String
4277          */
4278         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4279         
4280         /**
4281          * The function used to validate alpha values
4282          * @param {String} value The value
4283          */
4284         'alpha' : function(v){
4285             return alpha.test(v);
4286         },
4287         /**
4288          * The error text to display when the alpha validation function returns false
4289          * @type String
4290          */
4291         'alphaText' : 'This field should only contain letters and _',
4292         /**
4293          * The keystroke filter mask to be applied on alpha input
4294          * @type RegExp
4295          */
4296         'alphaMask' : /[a-z_]/i,
4297
4298         /**
4299          * The function used to validate alphanumeric values
4300          * @param {String} value The value
4301          */
4302         'alphanum' : function(v){
4303             return alphanum.test(v);
4304         },
4305         /**
4306          * The error text to display when the alphanumeric validation function returns false
4307          * @type String
4308          */
4309         'alphanumText' : 'This field should only contain letters, numbers and _',
4310         /**
4311          * The keystroke filter mask to be applied on alphanumeric input
4312          * @type RegExp
4313          */
4314         'alphanumMask' : /[a-z0-9_]/i
4315     };
4316 }();/*
4317  * - LGPL
4318  *
4319  * Input
4320  * 
4321  */
4322
4323 /**
4324  * @class Roo.bootstrap.Input
4325  * @extends Roo.bootstrap.Component
4326  * Bootstrap Input class
4327  * @cfg {Boolean} disabled is it disabled
4328  * @cfg {String} fieldLabel - the label associated
4329  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4330  * @cfg {String} name name of the input
4331  * @cfg {string} fieldLabel - the label associated
4332  * @cfg {string}  inputType - input / file submit ...
4333  * @cfg {string} placeholder - placeholder to put in text.
4334  * @cfg {string}  before - input group add on before
4335  * @cfg {string} after - input group add on after
4336  * @cfg {string} size - (lg|sm) or leave empty..
4337  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4338  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4339  * @cfg {Number} md colspan out of 12 for computer-sized screens
4340  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4341  * @cfg {string} value default value of the input
4342  * @cfg {Number} labelWidth set the width of label (0-12)
4343  * @cfg {String} labelAlign (top|left)
4344  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4345  * 
4346  * 
4347  * @constructor
4348  * Create a new Input
4349  * @param {Object} config The config object
4350  */
4351
4352 Roo.bootstrap.Input = function(config){
4353     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4354    
4355         this.addEvents({
4356             /**
4357              * @event focus
4358              * Fires when this field receives input focus.
4359              * @param {Roo.form.Field} this
4360              */
4361             focus : true,
4362             /**
4363              * @event blur
4364              * Fires when this field loses input focus.
4365              * @param {Roo.form.Field} this
4366              */
4367             blur : true,
4368             /**
4369              * @event specialkey
4370              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4371              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4372              * @param {Roo.form.Field} this
4373              * @param {Roo.EventObject} e The event object
4374              */
4375             specialkey : true,
4376             /**
4377              * @event change
4378              * Fires just before the field blurs if the field value has changed.
4379              * @param {Roo.form.Field} this
4380              * @param {Mixed} newValue The new value
4381              * @param {Mixed} oldValue The original value
4382              */
4383             change : true,
4384             /**
4385              * @event invalid
4386              * Fires after the field has been marked as invalid.
4387              * @param {Roo.form.Field} this
4388              * @param {String} msg The validation message
4389              */
4390             invalid : true,
4391             /**
4392              * @event valid
4393              * Fires after the field has been validated with no errors.
4394              * @param {Roo.form.Field} this
4395              */
4396             valid : true,
4397              /**
4398              * @event keyup
4399              * Fires after the key up
4400              * @param {Roo.form.Field} this
4401              * @param {Roo.EventObject}  e The event Object
4402              */
4403             keyup : true
4404         });
4405 };
4406
4407 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4408      /**
4409      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4410       automatic validation (defaults to "keyup").
4411      */
4412     validationEvent : "keyup",
4413      /**
4414      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4415      */
4416     validateOnBlur : true,
4417     /**
4418      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4419      */
4420     validationDelay : 250,
4421      /**
4422      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4423      */
4424     focusClass : "x-form-focus",  // not needed???
4425     
4426        
4427     /**
4428      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4429      */
4430     invalidClass : "has-error",
4431     
4432     /**
4433      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4434      */
4435     selectOnFocus : false,
4436     
4437      /**
4438      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4439      */
4440     maskRe : null,
4441        /**
4442      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4443      */
4444     vtype : null,
4445     
4446       /**
4447      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4448      */
4449     disableKeyFilter : false,
4450     
4451        /**
4452      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4453      */
4454     disabled : false,
4455      /**
4456      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4457      */
4458     allowBlank : true,
4459     /**
4460      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4461      */
4462     blankText : "This field is required",
4463     
4464      /**
4465      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4466      */
4467     minLength : 0,
4468     /**
4469      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4470      */
4471     maxLength : Number.MAX_VALUE,
4472     /**
4473      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4474      */
4475     minLengthText : "The minimum length for this field is {0}",
4476     /**
4477      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4478      */
4479     maxLengthText : "The maximum length for this field is {0}",
4480   
4481     
4482     /**
4483      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4484      * If available, this function will be called only after the basic validators all return true, and will be passed the
4485      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4486      */
4487     validator : null,
4488     /**
4489      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4490      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4491      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4492      */
4493     regex : null,
4494     /**
4495      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4496      */
4497     regexText : "",
4498     
4499     
4500     
4501     fieldLabel : '',
4502     inputType : 'text',
4503     
4504     name : false,
4505     placeholder: false,
4506     before : false,
4507     after : false,
4508     size : false,
4509     // private
4510     hasFocus : false,
4511     preventMark: false,
4512     isFormField : true,
4513     value : '',
4514     labelWidth : 2,
4515     labelAlign : false,
4516     readOnly : false,
4517     
4518     parentLabelAlign : function()
4519     {
4520         var parent = this;
4521         while (parent.parent()) {
4522             parent = parent.parent();
4523             if (typeof(parent.labelAlign) !='undefined') {
4524                 return parent.labelAlign;
4525             }
4526         }
4527         return 'left';
4528         
4529     },
4530     
4531     getAutoCreate : function(){
4532         
4533         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4534         
4535         var id = Roo.id();
4536         
4537         var cfg = {};
4538         
4539         if(this.inputType != 'hidden'){
4540             cfg.cls = 'form-group' //input-group
4541         }
4542         
4543         var input =  {
4544             tag: 'input',
4545             id : id,
4546             type : this.inputType,
4547             value : this.value,
4548             cls : 'form-control',
4549             placeholder : this.placeholder || ''
4550             
4551         };
4552         
4553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4554             input.maxLength = this.maxLength;
4555         }
4556         
4557         if (this.disabled) {
4558             input.disabled=true;
4559         }
4560         
4561         if (this.readOnly) {
4562             input.readonly=true;
4563         }
4564         
4565         if (this.name) {
4566             input.name = this.name;
4567         }
4568         if (this.size) {
4569             input.cls += ' input-' + this.size;
4570         }
4571         var settings=this;
4572         ['xs','sm','md','lg'].map(function(size){
4573             if (settings[size]) {
4574                 cfg.cls += ' col-' + size + '-' + settings[size];
4575             }
4576         });
4577         
4578         var inputblock = input;
4579         
4580         if (this.before || this.after) {
4581             
4582             inputblock = {
4583                 cls : 'input-group',
4584                 cn :  [] 
4585             };
4586             if (this.before) {
4587                 inputblock.cn.push({
4588                     tag :'span',
4589                     cls : 'input-group-addon',
4590                     html : this.before
4591                 });
4592             }
4593             inputblock.cn.push(input);
4594             if (this.after) {
4595                 inputblock.cn.push({
4596                     tag :'span',
4597                     cls : 'input-group-addon',
4598                     html : this.after
4599                 });
4600             }
4601             
4602         };
4603         
4604         if (align ==='left' && this.fieldLabel.length) {
4605                 Roo.log("left and has label");
4606                 cfg.cn = [
4607                     
4608                     {
4609                         tag: 'label',
4610                         'for' :  id,
4611                         cls : 'control-label col-sm-' + this.labelWidth,
4612                         html : this.fieldLabel
4613                         
4614                     },
4615                     {
4616                         cls : "col-sm-" + (12 - this.labelWidth), 
4617                         cn: [
4618                             inputblock
4619                         ]
4620                     }
4621                     
4622                 ];
4623         } else if ( this.fieldLabel.length) {
4624                 Roo.log(" label");
4625                  cfg.cn = [
4626                    
4627                     {
4628                         tag: 'label',
4629                         //cls : 'input-group-addon',
4630                         html : this.fieldLabel
4631                         
4632                     },
4633                     
4634                     inputblock
4635                     
4636                 ];
4637
4638         } else {
4639             
4640                 Roo.log(" no label && no align");
4641                 cfg.cn = [
4642                     
4643                         inputblock
4644                     
4645                 ];
4646                 
4647                 
4648         };
4649         Roo.log('input-parentType: ' + this.parentType);
4650         
4651         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4652            cfg.cls += ' navbar-form';
4653            Roo.log(cfg);
4654         }
4655         
4656         return cfg;
4657         
4658     },
4659     /**
4660      * return the real input element.
4661      */
4662     inputEl: function ()
4663     {
4664         return this.el.select('input.form-control',true).first();
4665     },
4666     setDisabled : function(v)
4667     {
4668         var i  = this.inputEl().dom;
4669         if (!v) {
4670             i.removeAttribute('disabled');
4671             return;
4672             
4673         }
4674         i.setAttribute('disabled','true');
4675     },
4676     initEvents : function()
4677     {
4678         
4679         this.inputEl().on("keydown" , this.fireKey,  this);
4680         this.inputEl().on("focus", this.onFocus,  this);
4681         this.inputEl().on("blur", this.onBlur,  this);
4682         
4683         this.inputEl().relayEvent('keyup', this);
4684
4685         // reference to original value for reset
4686         this.originalValue = this.getValue();
4687         //Roo.form.TextField.superclass.initEvents.call(this);
4688         if(this.validationEvent == 'keyup'){
4689             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4690             this.inputEl().on('keyup', this.filterValidation, this);
4691         }
4692         else if(this.validationEvent !== false){
4693             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4694         }
4695         
4696         if(this.selectOnFocus){
4697             this.on("focus", this.preFocus, this);
4698             
4699         }
4700         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4701             this.inputEl().on("keypress", this.filterKeys, this);
4702         }
4703        /* if(this.grow){
4704             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4705             this.el.on("click", this.autoSize,  this);
4706         }
4707         */
4708         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4709             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4710         }
4711         
4712     },
4713     filterValidation : function(e){
4714         if(!e.isNavKeyPress()){
4715             this.validationTask.delay(this.validationDelay);
4716         }
4717     },
4718      /**
4719      * Validates the field value
4720      * @return {Boolean} True if the value is valid, else false
4721      */
4722     validate : function(){
4723         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4724         if(this.disabled || this.validateValue(this.getRawValue())){
4725             this.clearInvalid();
4726             return true;
4727         }
4728         return false;
4729     },
4730     
4731     
4732     /**
4733      * Validates a value according to the field's validation rules and marks the field as invalid
4734      * if the validation fails
4735      * @param {Mixed} value The value to validate
4736      * @return {Boolean} True if the value is valid, else false
4737      */
4738     validateValue : function(value){
4739         if(value.length < 1)  { // if it's blank
4740              if(this.allowBlank){
4741                 this.clearInvalid();
4742                 return true;
4743              }else{
4744                 this.markInvalid(this.blankText);
4745                 return false;
4746              }
4747         }
4748         if(value.length < this.minLength){
4749             this.markInvalid(String.format(this.minLengthText, this.minLength));
4750             return false;
4751         }
4752         if(value.length > this.maxLength){
4753             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4754             return false;
4755         }
4756         if(this.vtype){
4757             var vt = Roo.form.VTypes;
4758             if(!vt[this.vtype](value, this)){
4759                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4760                 return false;
4761             }
4762         }
4763         if(typeof this.validator == "function"){
4764             var msg = this.validator(value);
4765             if(msg !== true){
4766                 this.markInvalid(msg);
4767                 return false;
4768             }
4769         }
4770         if(this.regex && !this.regex.test(value)){
4771             this.markInvalid(this.regexText);
4772             return false;
4773         }
4774         return true;
4775     },
4776
4777     
4778     
4779      // private
4780     fireKey : function(e){
4781         //Roo.log('field ' + e.getKey());
4782         if(e.isNavKeyPress()){
4783             this.fireEvent("specialkey", this, e);
4784         }
4785     },
4786     focus : function (selectText){
4787         if(this.rendered){
4788             this.inputEl().focus();
4789             if(selectText === true){
4790                 this.inputEl().dom.select();
4791             }
4792         }
4793         return this;
4794     } ,
4795     
4796     onFocus : function(){
4797         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4798            // this.el.addClass(this.focusClass);
4799         }
4800         if(!this.hasFocus){
4801             this.hasFocus = true;
4802             this.startValue = this.getValue();
4803             this.fireEvent("focus", this);
4804         }
4805     },
4806     
4807     beforeBlur : Roo.emptyFn,
4808
4809     
4810     // private
4811     onBlur : function(){
4812         this.beforeBlur();
4813         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4814             //this.el.removeClass(this.focusClass);
4815         }
4816         this.hasFocus = false;
4817         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4818             this.validate();
4819         }
4820         var v = this.getValue();
4821         if(String(v) !== String(this.startValue)){
4822             this.fireEvent('change', this, v, this.startValue);
4823         }
4824         this.fireEvent("blur", this);
4825     },
4826     
4827     /**
4828      * Resets the current field value to the originally loaded value and clears any validation messages
4829      */
4830     reset : function(){
4831         this.setValue(this.originalValue);
4832         this.clearInvalid();
4833     },
4834      /**
4835      * Returns the name of the field
4836      * @return {Mixed} name The name field
4837      */
4838     getName: function(){
4839         return this.name;
4840     },
4841      /**
4842      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4843      * @return {Mixed} value The field value
4844      */
4845     getValue : function(){
4846         return this.inputEl().getValue();
4847     },
4848     /**
4849      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4850      * @return {Mixed} value The field value
4851      */
4852     getRawValue : function(){
4853         var v = this.inputEl().getValue();
4854         
4855         return v;
4856     },
4857     
4858     /**
4859      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4860      * @param {Mixed} value The value to set
4861      */
4862     setRawValue : function(v){
4863         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4864     },
4865     
4866     selectText : function(start, end){
4867         var v = this.getRawValue();
4868         if(v.length > 0){
4869             start = start === undefined ? 0 : start;
4870             end = end === undefined ? v.length : end;
4871             var d = this.inputEl().dom;
4872             if(d.setSelectionRange){
4873                 d.setSelectionRange(start, end);
4874             }else if(d.createTextRange){
4875                 var range = d.createTextRange();
4876                 range.moveStart("character", start);
4877                 range.moveEnd("character", v.length-end);
4878                 range.select();
4879             }
4880         }
4881     },
4882     
4883     /**
4884      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4885      * @param {Mixed} value The value to set
4886      */
4887     setValue : function(v){
4888         this.value = v;
4889         if(this.rendered){
4890             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4891             this.validate();
4892         }
4893     },
4894     
4895     /*
4896     processValue : function(value){
4897         if(this.stripCharsRe){
4898             var newValue = value.replace(this.stripCharsRe, '');
4899             if(newValue !== value){
4900                 this.setRawValue(newValue);
4901                 return newValue;
4902             }
4903         }
4904         return value;
4905     },
4906   */
4907     preFocus : function(){
4908         
4909         if(this.selectOnFocus){
4910             this.inputEl().dom.select();
4911         }
4912     },
4913     filterKeys : function(e){
4914         var k = e.getKey();
4915         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4916             return;
4917         }
4918         var c = e.getCharCode(), cc = String.fromCharCode(c);
4919         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4920             return;
4921         }
4922         if(!this.maskRe.test(cc)){
4923             e.stopEvent();
4924         }
4925     },
4926      /**
4927      * Clear any invalid styles/messages for this field
4928      */
4929     clearInvalid : function(){
4930         
4931         if(!this.el || this.preventMark){ // not rendered
4932             return;
4933         }
4934         this.el.removeClass(this.invalidClass);
4935         /*
4936         switch(this.msgTarget){
4937             case 'qtip':
4938                 this.el.dom.qtip = '';
4939                 break;
4940             case 'title':
4941                 this.el.dom.title = '';
4942                 break;
4943             case 'under':
4944                 if(this.errorEl){
4945                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4946                 }
4947                 break;
4948             case 'side':
4949                 if(this.errorIcon){
4950                     this.errorIcon.dom.qtip = '';
4951                     this.errorIcon.hide();
4952                     this.un('resize', this.alignErrorIcon, this);
4953                 }
4954                 break;
4955             default:
4956                 var t = Roo.getDom(this.msgTarget);
4957                 t.innerHTML = '';
4958                 t.style.display = 'none';
4959                 break;
4960         }
4961         */
4962         this.fireEvent('valid', this);
4963     },
4964      /**
4965      * Mark this field as invalid
4966      * @param {String} msg The validation message
4967      */
4968     markInvalid : function(msg){
4969         if(!this.el  || this.preventMark){ // not rendered
4970             return;
4971         }
4972         this.el.addClass(this.invalidClass);
4973         /*
4974         msg = msg || this.invalidText;
4975         switch(this.msgTarget){
4976             case 'qtip':
4977                 this.el.dom.qtip = msg;
4978                 this.el.dom.qclass = 'x-form-invalid-tip';
4979                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4980                     Roo.QuickTips.enable();
4981                 }
4982                 break;
4983             case 'title':
4984                 this.el.dom.title = msg;
4985                 break;
4986             case 'under':
4987                 if(!this.errorEl){
4988                     var elp = this.el.findParent('.x-form-element', 5, true);
4989                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4990                     this.errorEl.setWidth(elp.getWidth(true)-20);
4991                 }
4992                 this.errorEl.update(msg);
4993                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4994                 break;
4995             case 'side':
4996                 if(!this.errorIcon){
4997                     var elp = this.el.findParent('.x-form-element', 5, true);
4998                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4999                 }
5000                 this.alignErrorIcon();
5001                 this.errorIcon.dom.qtip = msg;
5002                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5003                 this.errorIcon.show();
5004                 this.on('resize', this.alignErrorIcon, this);
5005                 break;
5006             default:
5007                 var t = Roo.getDom(this.msgTarget);
5008                 t.innerHTML = msg;
5009                 t.style.display = this.msgDisplay;
5010                 break;
5011         }
5012         */
5013         this.fireEvent('invalid', this, msg);
5014     },
5015     // private
5016     SafariOnKeyDown : function(event)
5017     {
5018         // this is a workaround for a password hang bug on chrome/ webkit.
5019         
5020         var isSelectAll = false;
5021         
5022         if(this.inputEl().dom.selectionEnd > 0){
5023             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5024         }
5025         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5026             event.preventDefault();
5027             this.setValue('');
5028             return;
5029         }
5030         
5031         if(isSelectAll){ // backspace and delete key
5032             
5033             event.preventDefault();
5034             // this is very hacky as keydown always get's upper case.
5035             //
5036             var cc = String.fromCharCode(event.getCharCode());
5037             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5038             
5039         }
5040     },
5041     adjustWidth : function(tag, w){
5042         tag = tag.toLowerCase();
5043         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5044             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5045                 if(tag == 'input'){
5046                     return w + 2;
5047                 }
5048                 if(tag == 'textarea'){
5049                     return w-2;
5050                 }
5051             }else if(Roo.isOpera){
5052                 if(tag == 'input'){
5053                     return w + 2;
5054                 }
5055                 if(tag == 'textarea'){
5056                     return w-2;
5057                 }
5058             }
5059         }
5060         return w;
5061     }
5062     
5063 });
5064
5065  
5066 /*
5067  * - LGPL
5068  *
5069  * Input
5070  * 
5071  */
5072
5073 /**
5074  * @class Roo.bootstrap.TextArea
5075  * @extends Roo.bootstrap.Input
5076  * Bootstrap TextArea class
5077  * @cfg {Number} cols Specifies the visible width of a text area
5078  * @cfg {Number} rows Specifies the visible number of lines in a text area
5079  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5080  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5081  * @cfg {string} html text
5082  * 
5083  * @constructor
5084  * Create a new TextArea
5085  * @param {Object} config The config object
5086  */
5087
5088 Roo.bootstrap.TextArea = function(config){
5089     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5090    
5091 };
5092
5093 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5094      
5095     cols : false,
5096     rows : 5,
5097     readOnly : false,
5098     warp : 'soft',
5099     resize : false,
5100     value: false,
5101     html: false,
5102     
5103     getAutoCreate : function(){
5104         
5105         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5106         
5107         var id = Roo.id();
5108         
5109         var cfg = {};
5110         
5111         var input =  {
5112             tag: 'textarea',
5113             id : id,
5114             warp : this.warp,
5115             rows : this.rows,
5116             value : this.value || '',
5117             html: this.html || '',
5118             cls : 'form-control',
5119             placeholder : this.placeholder || '' 
5120             
5121         };
5122         
5123         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5124             input.maxLength = this.maxLength;
5125         }
5126         
5127         if(this.resize){
5128             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5129         }
5130         
5131         if(this.cols){
5132             input.cols = this.cols;
5133         }
5134         
5135         if (this.readOnly) {
5136             input.readonly = true;
5137         }
5138         
5139         if (this.name) {
5140             input.name = this.name;
5141         }
5142         
5143         if (this.size) {
5144             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5145         }
5146         
5147         var settings=this;
5148         ['xs','sm','md','lg'].map(function(size){
5149             if (settings[size]) {
5150                 cfg.cls += ' col-' + size + '-' + settings[size];
5151             }
5152         });
5153         
5154         var inputblock = input;
5155         
5156         if (this.before || this.after) {
5157             
5158             inputblock = {
5159                 cls : 'input-group',
5160                 cn :  [] 
5161             };
5162             if (this.before) {
5163                 inputblock.cn.push({
5164                     tag :'span',
5165                     cls : 'input-group-addon',
5166                     html : this.before
5167                 });
5168             }
5169             inputblock.cn.push(input);
5170             if (this.after) {
5171                 inputblock.cn.push({
5172                     tag :'span',
5173                     cls : 'input-group-addon',
5174                     html : this.after
5175                 });
5176             }
5177             
5178         }
5179         
5180         if (align ==='left' && this.fieldLabel.length) {
5181                 Roo.log("left and has label");
5182                 cfg.cn = [
5183                     
5184                     {
5185                         tag: 'label',
5186                         'for' :  id,
5187                         cls : 'control-label col-sm-' + this.labelWidth,
5188                         html : this.fieldLabel
5189                         
5190                     },
5191                     {
5192                         cls : "col-sm-" + (12 - this.labelWidth), 
5193                         cn: [
5194                             inputblock
5195                         ]
5196                     }
5197                     
5198                 ];
5199         } else if ( this.fieldLabel.length) {
5200                 Roo.log(" label");
5201                  cfg.cn = [
5202                    
5203                     {
5204                         tag: 'label',
5205                         //cls : 'input-group-addon',
5206                         html : this.fieldLabel
5207                         
5208                     },
5209                     
5210                     inputblock
5211                     
5212                 ];
5213
5214         } else {
5215             
5216                    Roo.log(" no label && no align");
5217                 cfg.cn = [
5218                     
5219                         inputblock
5220                     
5221                 ];
5222                 
5223                 
5224         }
5225         
5226         if (this.disabled) {
5227             input.disabled=true;
5228         }
5229         
5230         return cfg;
5231         
5232     },
5233     /**
5234      * return the real textarea element.
5235      */
5236     inputEl: function ()
5237     {
5238         return this.el.select('textarea.form-control',true).first();
5239     }
5240 });
5241
5242  
5243 /*
5244  * - LGPL
5245  *
5246  * trigger field - base class for combo..
5247  * 
5248  */
5249  
5250 /**
5251  * @class Roo.bootstrap.TriggerField
5252  * @extends Roo.bootstrap.Input
5253  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5254  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5255  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5256  * for which you can provide a custom implementation.  For example:
5257  * <pre><code>
5258 var trigger = new Roo.bootstrap.TriggerField();
5259 trigger.onTriggerClick = myTriggerFn;
5260 trigger.applyTo('my-field');
5261 </code></pre>
5262  *
5263  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5264  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5265  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5266  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5267  * @constructor
5268  * Create a new TriggerField.
5269  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5270  * to the base TextField)
5271  */
5272 Roo.bootstrap.TriggerField = function(config){
5273     this.mimicing = false;
5274     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5275 };
5276
5277 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5278     /**
5279      * @cfg {String} triggerClass A CSS class to apply to the trigger
5280      */
5281      /**
5282      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5283      */
5284     hideTrigger:false,
5285
5286     /** @cfg {Boolean} grow @hide */
5287     /** @cfg {Number} growMin @hide */
5288     /** @cfg {Number} growMax @hide */
5289
5290     /**
5291      * @hide 
5292      * @method
5293      */
5294     autoSize: Roo.emptyFn,
5295     // private
5296     monitorTab : true,
5297     // private
5298     deferHeight : true,
5299
5300     
5301     actionMode : 'wrap',
5302     
5303     
5304     
5305     getAutoCreate : function(){
5306        
5307         var parent = this.parent();
5308         
5309         var align = this.parentLabelAlign();
5310         
5311         var id = Roo.id();
5312         
5313         var cfg = {
5314             cls: 'form-group' //input-group
5315         };
5316         
5317         
5318         var input =  {
5319             tag: 'input',
5320             id : id,
5321             type : this.inputType,
5322             cls : 'form-control',
5323             autocomplete: 'off',
5324             placeholder : this.placeholder || '' 
5325             
5326         };
5327         if (this.name) {
5328             input.name = this.name;
5329         }
5330         if (this.size) {
5331             input.cls += ' input-' + this.size;
5332         }
5333         
5334         if (this.disabled) {
5335             input.disabled=true;
5336         }
5337         
5338         var inputblock = input;
5339         
5340         if (this.before || this.after) {
5341             
5342             inputblock = {
5343                 cls : 'input-group',
5344                 cn :  [] 
5345             };
5346             if (this.before) {
5347                 inputblock.cn.push({
5348                     tag :'span',
5349                     cls : 'input-group-addon',
5350                     html : this.before
5351                 });
5352             }
5353             inputblock.cn.push(input);
5354             if (this.after) {
5355                 inputblock.cn.push({
5356                     tag :'span',
5357                     cls : 'input-group-addon',
5358                     html : this.after
5359                 });
5360             }
5361             
5362         };
5363         
5364         var box = {
5365             tag: 'div',
5366             cn: [
5367                 {
5368                     tag: 'input',
5369                     type : 'hidden',
5370                     cls: 'form-hidden-field'
5371                 },
5372                 inputblock
5373             ]
5374             
5375         };
5376         
5377         if(this.multiple){
5378             Roo.log('multiple');
5379             
5380             box = {
5381                 tag: 'div',
5382                 cn: [
5383                     {
5384                         tag: 'input',
5385                         type : 'hidden',
5386                         cls: 'form-hidden-field'
5387                     },
5388                     {
5389                         tag: 'ul',
5390                         cls: 'select2-choices',
5391                         cn:[
5392                             {
5393                                 tag: 'li',
5394                                 cls: 'select2-search-field',
5395                                 cn: [
5396
5397                                     inputblock
5398                                 ]
5399                             }
5400                         ]
5401                     }
5402                 ]
5403             }
5404         };
5405         
5406         var combobox = {
5407             cls: 'select2-container input-group',
5408             cn: [
5409                 box,
5410                 {
5411                     tag: 'ul',
5412                     cls: 'typeahead typeahead-long dropdown-menu',
5413                     style: 'display:none'
5414                 }
5415             ]
5416         };
5417         
5418         if(!this.multiple){
5419             combobox.cn.push({
5420                 tag :'span',
5421                 cls : 'input-group-addon btn dropdown-toggle',
5422                 cn : [
5423                     {
5424                         tag: 'span',
5425                         cls: 'caret'
5426                     },
5427                     {
5428                         tag: 'span',
5429                         cls: 'combobox-clear',
5430                         cn  : [
5431                             {
5432                                 tag : 'i',
5433                                 cls: 'icon-remove'
5434                             }
5435                         ]
5436                     }
5437                 ]
5438
5439             })
5440         }
5441         
5442         if(this.multiple){
5443             combobox.cls += ' select2-container-multi';
5444         }
5445         
5446         if (align ==='left' && this.fieldLabel.length) {
5447             
5448                 Roo.log("left and has label");
5449                 cfg.cn = [
5450                     
5451                     {
5452                         tag: 'label',
5453                         'for' :  id,
5454                         cls : 'control-label col-sm-' + this.labelWidth,
5455                         html : this.fieldLabel
5456                         
5457                     },
5458                     {
5459                         cls : "col-sm-" + (12 - this.labelWidth), 
5460                         cn: [
5461                             combobox
5462                         ]
5463                     }
5464                     
5465                 ];
5466         } else if ( this.fieldLabel.length) {
5467                 Roo.log(" label");
5468                  cfg.cn = [
5469                    
5470                     {
5471                         tag: 'label',
5472                         //cls : 'input-group-addon',
5473                         html : this.fieldLabel
5474                         
5475                     },
5476                     
5477                     combobox
5478                     
5479                 ];
5480
5481         } else {
5482             
5483                 Roo.log(" no label && no align");
5484                 cfg = combobox
5485                      
5486                 
5487         }
5488          
5489         var settings=this;
5490         ['xs','sm','md','lg'].map(function(size){
5491             if (settings[size]) {
5492                 cfg.cls += ' col-' + size + '-' + settings[size];
5493             }
5494         });
5495         
5496         return cfg;
5497         
5498     },
5499     
5500     
5501     
5502     // private
5503     onResize : function(w, h){
5504 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5505 //        if(typeof w == 'number'){
5506 //            var x = w - this.trigger.getWidth();
5507 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5508 //            this.trigger.setStyle('left', x+'px');
5509 //        }
5510     },
5511
5512     // private
5513     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5514
5515     // private
5516     getResizeEl : function(){
5517         return this.inputEl();
5518     },
5519
5520     // private
5521     getPositionEl : function(){
5522         return this.inputEl();
5523     },
5524
5525     // private
5526     alignErrorIcon : function(){
5527         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5528     },
5529
5530     // private
5531     initEvents : function(){
5532         
5533         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5534         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5535         if(!this.multiple){
5536             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5537             if(this.hideTrigger){
5538                 this.trigger.setDisplayed(false);
5539             }
5540             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5541         }
5542         
5543         if(this.multiple){
5544             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5545         }
5546         
5547         //this.trigger.addClassOnOver('x-form-trigger-over');
5548         //this.trigger.addClassOnClick('x-form-trigger-click');
5549         
5550         //if(!this.width){
5551         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5552         //}
5553     },
5554
5555     // private
5556     initTrigger : function(){
5557        
5558     },
5559
5560     // private
5561     onDestroy : function(){
5562         if(this.trigger){
5563             this.trigger.removeAllListeners();
5564           //  this.trigger.remove();
5565         }
5566         //if(this.wrap){
5567         //    this.wrap.remove();
5568         //}
5569         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5570     },
5571
5572     // private
5573     onFocus : function(){
5574         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5575         /*
5576         if(!this.mimicing){
5577             this.wrap.addClass('x-trigger-wrap-focus');
5578             this.mimicing = true;
5579             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5580             if(this.monitorTab){
5581                 this.el.on("keydown", this.checkTab, this);
5582             }
5583         }
5584         */
5585     },
5586
5587     // private
5588     checkTab : function(e){
5589         if(e.getKey() == e.TAB){
5590             this.triggerBlur();
5591         }
5592     },
5593
5594     // private
5595     onBlur : function(){
5596         // do nothing
5597     },
5598
5599     // private
5600     mimicBlur : function(e, t){
5601         /*
5602         if(!this.wrap.contains(t) && this.validateBlur()){
5603             this.triggerBlur();
5604         }
5605         */
5606     },
5607
5608     // private
5609     triggerBlur : function(){
5610         this.mimicing = false;
5611         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5612         if(this.monitorTab){
5613             this.el.un("keydown", this.checkTab, this);
5614         }
5615         //this.wrap.removeClass('x-trigger-wrap-focus');
5616         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5617     },
5618
5619     // private
5620     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5621     validateBlur : function(e, t){
5622         return true;
5623     },
5624
5625     // private
5626     onDisable : function(){
5627         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5628         //if(this.wrap){
5629         //    this.wrap.addClass('x-item-disabled');
5630         //}
5631     },
5632
5633     // private
5634     onEnable : function(){
5635         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5636         //if(this.wrap){
5637         //    this.el.removeClass('x-item-disabled');
5638         //}
5639     },
5640
5641     // private
5642     onShow : function(){
5643         var ae = this.getActionEl();
5644         
5645         if(ae){
5646             ae.dom.style.display = '';
5647             ae.dom.style.visibility = 'visible';
5648         }
5649     },
5650
5651     // private
5652     
5653     onHide : function(){
5654         var ae = this.getActionEl();
5655         ae.dom.style.display = 'none';
5656     },
5657
5658     /**
5659      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5660      * by an implementing function.
5661      * @method
5662      * @param {EventObject} e
5663      */
5664     onTriggerClick : Roo.emptyFn
5665 });
5666  /*
5667  * Based on:
5668  * Ext JS Library 1.1.1
5669  * Copyright(c) 2006-2007, Ext JS, LLC.
5670  *
5671  * Originally Released Under LGPL - original licence link has changed is not relivant.
5672  *
5673  * Fork - LGPL
5674  * <script type="text/javascript">
5675  */
5676
5677
5678 /**
5679  * @class Roo.data.SortTypes
5680  * @singleton
5681  * Defines the default sorting (casting?) comparison functions used when sorting data.
5682  */
5683 Roo.data.SortTypes = {
5684     /**
5685      * Default sort that does nothing
5686      * @param {Mixed} s The value being converted
5687      * @return {Mixed} The comparison value
5688      */
5689     none : function(s){
5690         return s;
5691     },
5692     
5693     /**
5694      * The regular expression used to strip tags
5695      * @type {RegExp}
5696      * @property
5697      */
5698     stripTagsRE : /<\/?[^>]+>/gi,
5699     
5700     /**
5701      * Strips all HTML tags to sort on text only
5702      * @param {Mixed} s The value being converted
5703      * @return {String} The comparison value
5704      */
5705     asText : function(s){
5706         return String(s).replace(this.stripTagsRE, "");
5707     },
5708     
5709     /**
5710      * Strips all HTML tags to sort on text only - Case insensitive
5711      * @param {Mixed} s The value being converted
5712      * @return {String} The comparison value
5713      */
5714     asUCText : function(s){
5715         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5716     },
5717     
5718     /**
5719      * Case insensitive string
5720      * @param {Mixed} s The value being converted
5721      * @return {String} The comparison value
5722      */
5723     asUCString : function(s) {
5724         return String(s).toUpperCase();
5725     },
5726     
5727     /**
5728      * Date sorting
5729      * @param {Mixed} s The value being converted
5730      * @return {Number} The comparison value
5731      */
5732     asDate : function(s) {
5733         if(!s){
5734             return 0;
5735         }
5736         if(s instanceof Date){
5737             return s.getTime();
5738         }
5739         return Date.parse(String(s));
5740     },
5741     
5742     /**
5743      * Float sorting
5744      * @param {Mixed} s The value being converted
5745      * @return {Float} The comparison value
5746      */
5747     asFloat : function(s) {
5748         var val = parseFloat(String(s).replace(/,/g, ""));
5749         if(isNaN(val)) val = 0;
5750         return val;
5751     },
5752     
5753     /**
5754      * Integer sorting
5755      * @param {Mixed} s The value being converted
5756      * @return {Number} The comparison value
5757      */
5758     asInt : function(s) {
5759         var val = parseInt(String(s).replace(/,/g, ""));
5760         if(isNaN(val)) val = 0;
5761         return val;
5762     }
5763 };/*
5764  * Based on:
5765  * Ext JS Library 1.1.1
5766  * Copyright(c) 2006-2007, Ext JS, LLC.
5767  *
5768  * Originally Released Under LGPL - original licence link has changed is not relivant.
5769  *
5770  * Fork - LGPL
5771  * <script type="text/javascript">
5772  */
5773
5774 /**
5775 * @class Roo.data.Record
5776  * Instances of this class encapsulate both record <em>definition</em> information, and record
5777  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5778  * to access Records cached in an {@link Roo.data.Store} object.<br>
5779  * <p>
5780  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5781  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5782  * objects.<br>
5783  * <p>
5784  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5785  * @constructor
5786  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5787  * {@link #create}. The parameters are the same.
5788  * @param {Array} data An associative Array of data values keyed by the field name.
5789  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5790  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5791  * not specified an integer id is generated.
5792  */
5793 Roo.data.Record = function(data, id){
5794     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5795     this.data = data;
5796 };
5797
5798 /**
5799  * Generate a constructor for a specific record layout.
5800  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5801  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5802  * Each field definition object may contain the following properties: <ul>
5803  * <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,
5804  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5805  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5806  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5807  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5808  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5809  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5810  * this may be omitted.</p></li>
5811  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5812  * <ul><li>auto (Default, implies no conversion)</li>
5813  * <li>string</li>
5814  * <li>int</li>
5815  * <li>float</li>
5816  * <li>boolean</li>
5817  * <li>date</li></ul></p></li>
5818  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5819  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5820  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5821  * by the Reader into an object that will be stored in the Record. It is passed the
5822  * following parameters:<ul>
5823  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5824  * </ul></p></li>
5825  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5826  * </ul>
5827  * <br>usage:<br><pre><code>
5828 var TopicRecord = Roo.data.Record.create(
5829     {name: 'title', mapping: 'topic_title'},
5830     {name: 'author', mapping: 'username'},
5831     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5832     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5833     {name: 'lastPoster', mapping: 'user2'},
5834     {name: 'excerpt', mapping: 'post_text'}
5835 );
5836
5837 var myNewRecord = new TopicRecord({
5838     title: 'Do my job please',
5839     author: 'noobie',
5840     totalPosts: 1,
5841     lastPost: new Date(),
5842     lastPoster: 'Animal',
5843     excerpt: 'No way dude!'
5844 });
5845 myStore.add(myNewRecord);
5846 </code></pre>
5847  * @method create
5848  * @static
5849  */
5850 Roo.data.Record.create = function(o){
5851     var f = function(){
5852         f.superclass.constructor.apply(this, arguments);
5853     };
5854     Roo.extend(f, Roo.data.Record);
5855     var p = f.prototype;
5856     p.fields = new Roo.util.MixedCollection(false, function(field){
5857         return field.name;
5858     });
5859     for(var i = 0, len = o.length; i < len; i++){
5860         p.fields.add(new Roo.data.Field(o[i]));
5861     }
5862     f.getField = function(name){
5863         return p.fields.get(name);  
5864     };
5865     return f;
5866 };
5867
5868 Roo.data.Record.AUTO_ID = 1000;
5869 Roo.data.Record.EDIT = 'edit';
5870 Roo.data.Record.REJECT = 'reject';
5871 Roo.data.Record.COMMIT = 'commit';
5872
5873 Roo.data.Record.prototype = {
5874     /**
5875      * Readonly flag - true if this record has been modified.
5876      * @type Boolean
5877      */
5878     dirty : false,
5879     editing : false,
5880     error: null,
5881     modified: null,
5882
5883     // private
5884     join : function(store){
5885         this.store = store;
5886     },
5887
5888     /**
5889      * Set the named field to the specified value.
5890      * @param {String} name The name of the field to set.
5891      * @param {Object} value The value to set the field to.
5892      */
5893     set : function(name, value){
5894         if(this.data[name] == value){
5895             return;
5896         }
5897         this.dirty = true;
5898         if(!this.modified){
5899             this.modified = {};
5900         }
5901         if(typeof this.modified[name] == 'undefined'){
5902             this.modified[name] = this.data[name];
5903         }
5904         this.data[name] = value;
5905         if(!this.editing && this.store){
5906             this.store.afterEdit(this);
5907         }       
5908     },
5909
5910     /**
5911      * Get the value of the named field.
5912      * @param {String} name The name of the field to get the value of.
5913      * @return {Object} The value of the field.
5914      */
5915     get : function(name){
5916         return this.data[name]; 
5917     },
5918
5919     // private
5920     beginEdit : function(){
5921         this.editing = true;
5922         this.modified = {}; 
5923     },
5924
5925     // private
5926     cancelEdit : function(){
5927         this.editing = false;
5928         delete this.modified;
5929     },
5930
5931     // private
5932     endEdit : function(){
5933         this.editing = false;
5934         if(this.dirty && this.store){
5935             this.store.afterEdit(this);
5936         }
5937     },
5938
5939     /**
5940      * Usually called by the {@link Roo.data.Store} which owns the Record.
5941      * Rejects all changes made to the Record since either creation, or the last commit operation.
5942      * Modified fields are reverted to their original values.
5943      * <p>
5944      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5945      * of reject operations.
5946      */
5947     reject : function(){
5948         var m = this.modified;
5949         for(var n in m){
5950             if(typeof m[n] != "function"){
5951                 this.data[n] = m[n];
5952             }
5953         }
5954         this.dirty = false;
5955         delete this.modified;
5956         this.editing = false;
5957         if(this.store){
5958             this.store.afterReject(this);
5959         }
5960     },
5961
5962     /**
5963      * Usually called by the {@link Roo.data.Store} which owns the Record.
5964      * Commits all changes made to the Record since either creation, or the last commit operation.
5965      * <p>
5966      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5967      * of commit operations.
5968      */
5969     commit : function(){
5970         this.dirty = false;
5971         delete this.modified;
5972         this.editing = false;
5973         if(this.store){
5974             this.store.afterCommit(this);
5975         }
5976     },
5977
5978     // private
5979     hasError : function(){
5980         return this.error != null;
5981     },
5982
5983     // private
5984     clearError : function(){
5985         this.error = null;
5986     },
5987
5988     /**
5989      * Creates a copy of this record.
5990      * @param {String} id (optional) A new record id if you don't want to use this record's id
5991      * @return {Record}
5992      */
5993     copy : function(newId) {
5994         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5995     }
5996 };/*
5997  * Based on:
5998  * Ext JS Library 1.1.1
5999  * Copyright(c) 2006-2007, Ext JS, LLC.
6000  *
6001  * Originally Released Under LGPL - original licence link has changed is not relivant.
6002  *
6003  * Fork - LGPL
6004  * <script type="text/javascript">
6005  */
6006
6007
6008
6009 /**
6010  * @class Roo.data.Store
6011  * @extends Roo.util.Observable
6012  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6013  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6014  * <p>
6015  * 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
6016  * has no knowledge of the format of the data returned by the Proxy.<br>
6017  * <p>
6018  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6019  * instances from the data object. These records are cached and made available through accessor functions.
6020  * @constructor
6021  * Creates a new Store.
6022  * @param {Object} config A config object containing the objects needed for the Store to access data,
6023  * and read the data into Records.
6024  */
6025 Roo.data.Store = function(config){
6026     this.data = new Roo.util.MixedCollection(false);
6027     this.data.getKey = function(o){
6028         return o.id;
6029     };
6030     this.baseParams = {};
6031     // private
6032     this.paramNames = {
6033         "start" : "start",
6034         "limit" : "limit",
6035         "sort" : "sort",
6036         "dir" : "dir",
6037         "multisort" : "_multisort"
6038     };
6039
6040     if(config && config.data){
6041         this.inlineData = config.data;
6042         delete config.data;
6043     }
6044
6045     Roo.apply(this, config);
6046     
6047     if(this.reader){ // reader passed
6048         this.reader = Roo.factory(this.reader, Roo.data);
6049         this.reader.xmodule = this.xmodule || false;
6050         if(!this.recordType){
6051             this.recordType = this.reader.recordType;
6052         }
6053         if(this.reader.onMetaChange){
6054             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6055         }
6056     }
6057
6058     if(this.recordType){
6059         this.fields = this.recordType.prototype.fields;
6060     }
6061     this.modified = [];
6062
6063     this.addEvents({
6064         /**
6065          * @event datachanged
6066          * Fires when the data cache has changed, and a widget which is using this Store
6067          * as a Record cache should refresh its view.
6068          * @param {Store} this
6069          */
6070         datachanged : true,
6071         /**
6072          * @event metachange
6073          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6074          * @param {Store} this
6075          * @param {Object} meta The JSON metadata
6076          */
6077         metachange : true,
6078         /**
6079          * @event add
6080          * Fires when Records have been added to the Store
6081          * @param {Store} this
6082          * @param {Roo.data.Record[]} records The array of Records added
6083          * @param {Number} index The index at which the record(s) were added
6084          */
6085         add : true,
6086         /**
6087          * @event remove
6088          * Fires when a Record has been removed from the Store
6089          * @param {Store} this
6090          * @param {Roo.data.Record} record The Record that was removed
6091          * @param {Number} index The index at which the record was removed
6092          */
6093         remove : true,
6094         /**
6095          * @event update
6096          * Fires when a Record has been updated
6097          * @param {Store} this
6098          * @param {Roo.data.Record} record The Record that was updated
6099          * @param {String} operation The update operation being performed.  Value may be one of:
6100          * <pre><code>
6101  Roo.data.Record.EDIT
6102  Roo.data.Record.REJECT
6103  Roo.data.Record.COMMIT
6104          * </code></pre>
6105          */
6106         update : true,
6107         /**
6108          * @event clear
6109          * Fires when the data cache has been cleared.
6110          * @param {Store} this
6111          */
6112         clear : true,
6113         /**
6114          * @event beforeload
6115          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6116          * the load action will be canceled.
6117          * @param {Store} this
6118          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6119          */
6120         beforeload : true,
6121         /**
6122          * @event beforeloadadd
6123          * Fires after a new set of Records has been loaded.
6124          * @param {Store} this
6125          * @param {Roo.data.Record[]} records The Records that were loaded
6126          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6127          */
6128         beforeloadadd : true,
6129         /**
6130          * @event load
6131          * Fires after a new set of Records has been loaded, before they are added to the store.
6132          * @param {Store} this
6133          * @param {Roo.data.Record[]} records The Records that were loaded
6134          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6135          * @params {Object} return from reader
6136          */
6137         load : true,
6138         /**
6139          * @event loadexception
6140          * Fires if an exception occurs in the Proxy during loading.
6141          * Called with the signature of the Proxy's "loadexception" event.
6142          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6143          * 
6144          * @param {Proxy} 
6145          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6146          * @param {Object} load options 
6147          * @param {Object} jsonData from your request (normally this contains the Exception)
6148          */
6149         loadexception : true
6150     });
6151     
6152     if(this.proxy){
6153         this.proxy = Roo.factory(this.proxy, Roo.data);
6154         this.proxy.xmodule = this.xmodule || false;
6155         this.relayEvents(this.proxy,  ["loadexception"]);
6156     }
6157     this.sortToggle = {};
6158     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6159
6160     Roo.data.Store.superclass.constructor.call(this);
6161
6162     if(this.inlineData){
6163         this.loadData(this.inlineData);
6164         delete this.inlineData;
6165     }
6166 };
6167
6168 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6169      /**
6170     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6171     * without a remote query - used by combo/forms at present.
6172     */
6173     
6174     /**
6175     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6176     */
6177     /**
6178     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6179     */
6180     /**
6181     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6182     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6183     */
6184     /**
6185     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6186     * on any HTTP request
6187     */
6188     /**
6189     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6190     */
6191     /**
6192     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6193     */
6194     multiSort: false,
6195     /**
6196     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6197     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6198     */
6199     remoteSort : false,
6200
6201     /**
6202     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6203      * loaded or when a record is removed. (defaults to false).
6204     */
6205     pruneModifiedRecords : false,
6206
6207     // private
6208     lastOptions : null,
6209
6210     /**
6211      * Add Records to the Store and fires the add event.
6212      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6213      */
6214     add : function(records){
6215         records = [].concat(records);
6216         for(var i = 0, len = records.length; i < len; i++){
6217             records[i].join(this);
6218         }
6219         var index = this.data.length;
6220         this.data.addAll(records);
6221         this.fireEvent("add", this, records, index);
6222     },
6223
6224     /**
6225      * Remove a Record from the Store and fires the remove event.
6226      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6227      */
6228     remove : function(record){
6229         var index = this.data.indexOf(record);
6230         this.data.removeAt(index);
6231         if(this.pruneModifiedRecords){
6232             this.modified.remove(record);
6233         }
6234         this.fireEvent("remove", this, record, index);
6235     },
6236
6237     /**
6238      * Remove all Records from the Store and fires the clear event.
6239      */
6240     removeAll : function(){
6241         this.data.clear();
6242         if(this.pruneModifiedRecords){
6243             this.modified = [];
6244         }
6245         this.fireEvent("clear", this);
6246     },
6247
6248     /**
6249      * Inserts Records to the Store at the given index and fires the add event.
6250      * @param {Number} index The start index at which to insert the passed Records.
6251      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6252      */
6253     insert : function(index, records){
6254         records = [].concat(records);
6255         for(var i = 0, len = records.length; i < len; i++){
6256             this.data.insert(index, records[i]);
6257             records[i].join(this);
6258         }
6259         this.fireEvent("add", this, records, index);
6260     },
6261
6262     /**
6263      * Get the index within the cache of the passed Record.
6264      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6265      * @return {Number} The index of the passed Record. Returns -1 if not found.
6266      */
6267     indexOf : function(record){
6268         return this.data.indexOf(record);
6269     },
6270
6271     /**
6272      * Get the index within the cache of the Record with the passed id.
6273      * @param {String} id The id of the Record to find.
6274      * @return {Number} The index of the Record. Returns -1 if not found.
6275      */
6276     indexOfId : function(id){
6277         return this.data.indexOfKey(id);
6278     },
6279
6280     /**
6281      * Get the Record with the specified id.
6282      * @param {String} id The id of the Record to find.
6283      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6284      */
6285     getById : function(id){
6286         return this.data.key(id);
6287     },
6288
6289     /**
6290      * Get the Record at the specified index.
6291      * @param {Number} index The index of the Record to find.
6292      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6293      */
6294     getAt : function(index){
6295         return this.data.itemAt(index);
6296     },
6297
6298     /**
6299      * Returns a range of Records between specified indices.
6300      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6301      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6302      * @return {Roo.data.Record[]} An array of Records
6303      */
6304     getRange : function(start, end){
6305         return this.data.getRange(start, end);
6306     },
6307
6308     // private
6309     storeOptions : function(o){
6310         o = Roo.apply({}, o);
6311         delete o.callback;
6312         delete o.scope;
6313         this.lastOptions = o;
6314     },
6315
6316     /**
6317      * Loads the Record cache from the configured Proxy using the configured Reader.
6318      * <p>
6319      * If using remote paging, then the first load call must specify the <em>start</em>
6320      * and <em>limit</em> properties in the options.params property to establish the initial
6321      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6322      * <p>
6323      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6324      * and this call will return before the new data has been loaded. Perform any post-processing
6325      * in a callback function, or in a "load" event handler.</strong>
6326      * <p>
6327      * @param {Object} options An object containing properties which control loading options:<ul>
6328      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6329      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6330      * passed the following arguments:<ul>
6331      * <li>r : Roo.data.Record[]</li>
6332      * <li>options: Options object from the load call</li>
6333      * <li>success: Boolean success indicator</li></ul></li>
6334      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6335      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6336      * </ul>
6337      */
6338     load : function(options){
6339         options = options || {};
6340         if(this.fireEvent("beforeload", this, options) !== false){
6341             this.storeOptions(options);
6342             var p = Roo.apply(options.params || {}, this.baseParams);
6343             // if meta was not loaded from remote source.. try requesting it.
6344             if (!this.reader.metaFromRemote) {
6345                 p._requestMeta = 1;
6346             }
6347             if(this.sortInfo && this.remoteSort){
6348                 var pn = this.paramNames;
6349                 p[pn["sort"]] = this.sortInfo.field;
6350                 p[pn["dir"]] = this.sortInfo.direction;
6351             }
6352             if (this.multiSort) {
6353                 var pn = this.paramNames;
6354                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6355             }
6356             
6357             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6358         }
6359     },
6360
6361     /**
6362      * Reloads the Record cache from the configured Proxy using the configured Reader and
6363      * the options from the last load operation performed.
6364      * @param {Object} options (optional) An object containing properties which may override the options
6365      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6366      * the most recently used options are reused).
6367      */
6368     reload : function(options){
6369         this.load(Roo.applyIf(options||{}, this.lastOptions));
6370     },
6371
6372     // private
6373     // Called as a callback by the Reader during a load operation.
6374     loadRecords : function(o, options, success){
6375         if(!o || success === false){
6376             if(success !== false){
6377                 this.fireEvent("load", this, [], options, o);
6378             }
6379             if(options.callback){
6380                 options.callback.call(options.scope || this, [], options, false);
6381             }
6382             return;
6383         }
6384         // if data returned failure - throw an exception.
6385         if (o.success === false) {
6386             // show a message if no listener is registered.
6387             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6388                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6389             }
6390             // loadmask wil be hooked into this..
6391             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6392             return;
6393         }
6394         var r = o.records, t = o.totalRecords || r.length;
6395         
6396         this.fireEvent("beforeloadadd", this, r, options, o);
6397         
6398         if(!options || options.add !== true){
6399             if(this.pruneModifiedRecords){
6400                 this.modified = [];
6401             }
6402             for(var i = 0, len = r.length; i < len; i++){
6403                 r[i].join(this);
6404             }
6405             if(this.snapshot){
6406                 this.data = this.snapshot;
6407                 delete this.snapshot;
6408             }
6409             this.data.clear();
6410             this.data.addAll(r);
6411             this.totalLength = t;
6412             this.applySort();
6413             this.fireEvent("datachanged", this);
6414         }else{
6415             this.totalLength = Math.max(t, this.data.length+r.length);
6416             this.add(r);
6417         }
6418         this.fireEvent("load", this, r, options, o);
6419         if(options.callback){
6420             options.callback.call(options.scope || this, r, options, true);
6421         }
6422     },
6423
6424
6425     /**
6426      * Loads data from a passed data block. A Reader which understands the format of the data
6427      * must have been configured in the constructor.
6428      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6429      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6430      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6431      */
6432     loadData : function(o, append){
6433         var r = this.reader.readRecords(o);
6434         this.loadRecords(r, {add: append}, true);
6435     },
6436
6437     /**
6438      * Gets the number of cached records.
6439      * <p>
6440      * <em>If using paging, this may not be the total size of the dataset. If the data object
6441      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6442      * the data set size</em>
6443      */
6444     getCount : function(){
6445         return this.data.length || 0;
6446     },
6447
6448     /**
6449      * Gets the total number of records in the dataset as returned by the server.
6450      * <p>
6451      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6452      * the dataset size</em>
6453      */
6454     getTotalCount : function(){
6455         return this.totalLength || 0;
6456     },
6457
6458     /**
6459      * Returns the sort state of the Store as an object with two properties:
6460      * <pre><code>
6461  field {String} The name of the field by which the Records are sorted
6462  direction {String} The sort order, "ASC" or "DESC"
6463      * </code></pre>
6464      */
6465     getSortState : function(){
6466         return this.sortInfo;
6467     },
6468
6469     // private
6470     applySort : function(){
6471         if(this.sortInfo && !this.remoteSort){
6472             var s = this.sortInfo, f = s.field;
6473             var st = this.fields.get(f).sortType;
6474             var fn = function(r1, r2){
6475                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6476                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6477             };
6478             this.data.sort(s.direction, fn);
6479             if(this.snapshot && this.snapshot != this.data){
6480                 this.snapshot.sort(s.direction, fn);
6481             }
6482         }
6483     },
6484
6485     /**
6486      * Sets the default sort column and order to be used by the next load operation.
6487      * @param {String} fieldName The name of the field to sort by.
6488      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6489      */
6490     setDefaultSort : function(field, dir){
6491         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6492     },
6493
6494     /**
6495      * Sort the Records.
6496      * If remote sorting is used, the sort is performed on the server, and the cache is
6497      * reloaded. If local sorting is used, the cache is sorted internally.
6498      * @param {String} fieldName The name of the field to sort by.
6499      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6500      */
6501     sort : function(fieldName, dir){
6502         var f = this.fields.get(fieldName);
6503         if(!dir){
6504             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6505             
6506             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6507                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6508             }else{
6509                 dir = f.sortDir;
6510             }
6511         }
6512         this.sortToggle[f.name] = dir;
6513         this.sortInfo = {field: f.name, direction: dir};
6514         if(!this.remoteSort){
6515             this.applySort();
6516             this.fireEvent("datachanged", this);
6517         }else{
6518             this.load(this.lastOptions);
6519         }
6520     },
6521
6522     /**
6523      * Calls the specified function for each of the Records in the cache.
6524      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6525      * Returning <em>false</em> aborts and exits the iteration.
6526      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6527      */
6528     each : function(fn, scope){
6529         this.data.each(fn, scope);
6530     },
6531
6532     /**
6533      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6534      * (e.g., during paging).
6535      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6536      */
6537     getModifiedRecords : function(){
6538         return this.modified;
6539     },
6540
6541     // private
6542     createFilterFn : function(property, value, anyMatch){
6543         if(!value.exec){ // not a regex
6544             value = String(value);
6545             if(value.length == 0){
6546                 return false;
6547             }
6548             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6549         }
6550         return function(r){
6551             return value.test(r.data[property]);
6552         };
6553     },
6554
6555     /**
6556      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6557      * @param {String} property A field on your records
6558      * @param {Number} start The record index to start at (defaults to 0)
6559      * @param {Number} end The last record index to include (defaults to length - 1)
6560      * @return {Number} The sum
6561      */
6562     sum : function(property, start, end){
6563         var rs = this.data.items, v = 0;
6564         start = start || 0;
6565         end = (end || end === 0) ? end : rs.length-1;
6566
6567         for(var i = start; i <= end; i++){
6568             v += (rs[i].data[property] || 0);
6569         }
6570         return v;
6571     },
6572
6573     /**
6574      * Filter the records by a specified property.
6575      * @param {String} field A field on your records
6576      * @param {String/RegExp} value Either a string that the field
6577      * should start with or a RegExp to test against the field
6578      * @param {Boolean} anyMatch True to match any part not just the beginning
6579      */
6580     filter : function(property, value, anyMatch){
6581         var fn = this.createFilterFn(property, value, anyMatch);
6582         return fn ? this.filterBy(fn) : this.clearFilter();
6583     },
6584
6585     /**
6586      * Filter by a function. The specified function will be called with each
6587      * record in this data source. If the function returns true the record is included,
6588      * otherwise it is filtered.
6589      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6590      * @param {Object} scope (optional) The scope of the function (defaults to this)
6591      */
6592     filterBy : function(fn, scope){
6593         this.snapshot = this.snapshot || this.data;
6594         this.data = this.queryBy(fn, scope||this);
6595         this.fireEvent("datachanged", this);
6596     },
6597
6598     /**
6599      * Query the records by a specified property.
6600      * @param {String} field A field on your records
6601      * @param {String/RegExp} value Either a string that the field
6602      * should start with or a RegExp to test against the field
6603      * @param {Boolean} anyMatch True to match any part not just the beginning
6604      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6605      */
6606     query : function(property, value, anyMatch){
6607         var fn = this.createFilterFn(property, value, anyMatch);
6608         return fn ? this.queryBy(fn) : this.data.clone();
6609     },
6610
6611     /**
6612      * Query by a function. The specified function will be called with each
6613      * record in this data source. If the function returns true the record is included
6614      * in the results.
6615      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6616      * @param {Object} scope (optional) The scope of the function (defaults to this)
6617       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6618      **/
6619     queryBy : function(fn, scope){
6620         var data = this.snapshot || this.data;
6621         return data.filterBy(fn, scope||this);
6622     },
6623
6624     /**
6625      * Collects unique values for a particular dataIndex from this store.
6626      * @param {String} dataIndex The property to collect
6627      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6628      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6629      * @return {Array} An array of the unique values
6630      **/
6631     collect : function(dataIndex, allowNull, bypassFilter){
6632         var d = (bypassFilter === true && this.snapshot) ?
6633                 this.snapshot.items : this.data.items;
6634         var v, sv, r = [], l = {};
6635         for(var i = 0, len = d.length; i < len; i++){
6636             v = d[i].data[dataIndex];
6637             sv = String(v);
6638             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6639                 l[sv] = true;
6640                 r[r.length] = v;
6641             }
6642         }
6643         return r;
6644     },
6645
6646     /**
6647      * Revert to a view of the Record cache with no filtering applied.
6648      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6649      */
6650     clearFilter : function(suppressEvent){
6651         if(this.snapshot && this.snapshot != this.data){
6652             this.data = this.snapshot;
6653             delete this.snapshot;
6654             if(suppressEvent !== true){
6655                 this.fireEvent("datachanged", this);
6656             }
6657         }
6658     },
6659
6660     // private
6661     afterEdit : function(record){
6662         if(this.modified.indexOf(record) == -1){
6663             this.modified.push(record);
6664         }
6665         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6666     },
6667     
6668     // private
6669     afterReject : function(record){
6670         this.modified.remove(record);
6671         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6672     },
6673
6674     // private
6675     afterCommit : function(record){
6676         this.modified.remove(record);
6677         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6678     },
6679
6680     /**
6681      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6682      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6683      */
6684     commitChanges : function(){
6685         var m = this.modified.slice(0);
6686         this.modified = [];
6687         for(var i = 0, len = m.length; i < len; i++){
6688             m[i].commit();
6689         }
6690     },
6691
6692     /**
6693      * Cancel outstanding changes on all changed records.
6694      */
6695     rejectChanges : function(){
6696         var m = this.modified.slice(0);
6697         this.modified = [];
6698         for(var i = 0, len = m.length; i < len; i++){
6699             m[i].reject();
6700         }
6701     },
6702
6703     onMetaChange : function(meta, rtype, o){
6704         this.recordType = rtype;
6705         this.fields = rtype.prototype.fields;
6706         delete this.snapshot;
6707         this.sortInfo = meta.sortInfo || this.sortInfo;
6708         this.modified = [];
6709         this.fireEvent('metachange', this, this.reader.meta);
6710     }
6711 });/*
6712  * Based on:
6713  * Ext JS Library 1.1.1
6714  * Copyright(c) 2006-2007, Ext JS, LLC.
6715  *
6716  * Originally Released Under LGPL - original licence link has changed is not relivant.
6717  *
6718  * Fork - LGPL
6719  * <script type="text/javascript">
6720  */
6721
6722 /**
6723  * @class Roo.data.SimpleStore
6724  * @extends Roo.data.Store
6725  * Small helper class to make creating Stores from Array data easier.
6726  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6727  * @cfg {Array} fields An array of field definition objects, or field name strings.
6728  * @cfg {Array} data The multi-dimensional array of data
6729  * @constructor
6730  * @param {Object} config
6731  */
6732 Roo.data.SimpleStore = function(config){
6733     Roo.data.SimpleStore.superclass.constructor.call(this, {
6734         isLocal : true,
6735         reader: new Roo.data.ArrayReader({
6736                 id: config.id
6737             },
6738             Roo.data.Record.create(config.fields)
6739         ),
6740         proxy : new Roo.data.MemoryProxy(config.data)
6741     });
6742     this.load();
6743 };
6744 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6745  * Based on:
6746  * Ext JS Library 1.1.1
6747  * Copyright(c) 2006-2007, Ext JS, LLC.
6748  *
6749  * Originally Released Under LGPL - original licence link has changed is not relivant.
6750  *
6751  * Fork - LGPL
6752  * <script type="text/javascript">
6753  */
6754
6755 /**
6756 /**
6757  * @extends Roo.data.Store
6758  * @class Roo.data.JsonStore
6759  * Small helper class to make creating Stores for JSON data easier. <br/>
6760 <pre><code>
6761 var store = new Roo.data.JsonStore({
6762     url: 'get-images.php',
6763     root: 'images',
6764     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6765 });
6766 </code></pre>
6767  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6768  * JsonReader and HttpProxy (unless inline data is provided).</b>
6769  * @cfg {Array} fields An array of field definition objects, or field name strings.
6770  * @constructor
6771  * @param {Object} config
6772  */
6773 Roo.data.JsonStore = function(c){
6774     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6775         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6776         reader: new Roo.data.JsonReader(c, c.fields)
6777     }));
6778 };
6779 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6780  * Based on:
6781  * Ext JS Library 1.1.1
6782  * Copyright(c) 2006-2007, Ext JS, LLC.
6783  *
6784  * Originally Released Under LGPL - original licence link has changed is not relivant.
6785  *
6786  * Fork - LGPL
6787  * <script type="text/javascript">
6788  */
6789
6790  
6791 Roo.data.Field = function(config){
6792     if(typeof config == "string"){
6793         config = {name: config};
6794     }
6795     Roo.apply(this, config);
6796     
6797     if(!this.type){
6798         this.type = "auto";
6799     }
6800     
6801     var st = Roo.data.SortTypes;
6802     // named sortTypes are supported, here we look them up
6803     if(typeof this.sortType == "string"){
6804         this.sortType = st[this.sortType];
6805     }
6806     
6807     // set default sortType for strings and dates
6808     if(!this.sortType){
6809         switch(this.type){
6810             case "string":
6811                 this.sortType = st.asUCString;
6812                 break;
6813             case "date":
6814                 this.sortType = st.asDate;
6815                 break;
6816             default:
6817                 this.sortType = st.none;
6818         }
6819     }
6820
6821     // define once
6822     var stripRe = /[\$,%]/g;
6823
6824     // prebuilt conversion function for this field, instead of
6825     // switching every time we're reading a value
6826     if(!this.convert){
6827         var cv, dateFormat = this.dateFormat;
6828         switch(this.type){
6829             case "":
6830             case "auto":
6831             case undefined:
6832                 cv = function(v){ return v; };
6833                 break;
6834             case "string":
6835                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6836                 break;
6837             case "int":
6838                 cv = function(v){
6839                     return v !== undefined && v !== null && v !== '' ?
6840                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6841                     };
6842                 break;
6843             case "float":
6844                 cv = function(v){
6845                     return v !== undefined && v !== null && v !== '' ?
6846                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6847                     };
6848                 break;
6849             case "bool":
6850             case "boolean":
6851                 cv = function(v){ return v === true || v === "true" || v == 1; };
6852                 break;
6853             case "date":
6854                 cv = function(v){
6855                     if(!v){
6856                         return '';
6857                     }
6858                     if(v instanceof Date){
6859                         return v;
6860                     }
6861                     if(dateFormat){
6862                         if(dateFormat == "timestamp"){
6863                             return new Date(v*1000);
6864                         }
6865                         return Date.parseDate(v, dateFormat);
6866                     }
6867                     var parsed = Date.parse(v);
6868                     return parsed ? new Date(parsed) : null;
6869                 };
6870              break;
6871             
6872         }
6873         this.convert = cv;
6874     }
6875 };
6876
6877 Roo.data.Field.prototype = {
6878     dateFormat: null,
6879     defaultValue: "",
6880     mapping: null,
6881     sortType : null,
6882     sortDir : "ASC"
6883 };/*
6884  * Based on:
6885  * Ext JS Library 1.1.1
6886  * Copyright(c) 2006-2007, Ext JS, LLC.
6887  *
6888  * Originally Released Under LGPL - original licence link has changed is not relivant.
6889  *
6890  * Fork - LGPL
6891  * <script type="text/javascript">
6892  */
6893  
6894 // Base class for reading structured data from a data source.  This class is intended to be
6895 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6896
6897 /**
6898  * @class Roo.data.DataReader
6899  * Base class for reading structured data from a data source.  This class is intended to be
6900  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6901  */
6902
6903 Roo.data.DataReader = function(meta, recordType){
6904     
6905     this.meta = meta;
6906     
6907     this.recordType = recordType instanceof Array ? 
6908         Roo.data.Record.create(recordType) : recordType;
6909 };
6910
6911 Roo.data.DataReader.prototype = {
6912      /**
6913      * Create an empty record
6914      * @param {Object} data (optional) - overlay some values
6915      * @return {Roo.data.Record} record created.
6916      */
6917     newRow :  function(d) {
6918         var da =  {};
6919         this.recordType.prototype.fields.each(function(c) {
6920             switch( c.type) {
6921                 case 'int' : da[c.name] = 0; break;
6922                 case 'date' : da[c.name] = new Date(); break;
6923                 case 'float' : da[c.name] = 0.0; break;
6924                 case 'boolean' : da[c.name] = false; break;
6925                 default : da[c.name] = ""; break;
6926             }
6927             
6928         });
6929         return new this.recordType(Roo.apply(da, d));
6930     }
6931     
6932 };/*
6933  * Based on:
6934  * Ext JS Library 1.1.1
6935  * Copyright(c) 2006-2007, Ext JS, LLC.
6936  *
6937  * Originally Released Under LGPL - original licence link has changed is not relivant.
6938  *
6939  * Fork - LGPL
6940  * <script type="text/javascript">
6941  */
6942
6943 /**
6944  * @class Roo.data.DataProxy
6945  * @extends Roo.data.Observable
6946  * This class is an abstract base class for implementations which provide retrieval of
6947  * unformatted data objects.<br>
6948  * <p>
6949  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6950  * (of the appropriate type which knows how to parse the data object) to provide a block of
6951  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6952  * <p>
6953  * Custom implementations must implement the load method as described in
6954  * {@link Roo.data.HttpProxy#load}.
6955  */
6956 Roo.data.DataProxy = function(){
6957     this.addEvents({
6958         /**
6959          * @event beforeload
6960          * Fires before a network request is made to retrieve a data object.
6961          * @param {Object} This DataProxy object.
6962          * @param {Object} params The params parameter to the load function.
6963          */
6964         beforeload : true,
6965         /**
6966          * @event load
6967          * Fires before the load method's callback is called.
6968          * @param {Object} This DataProxy object.
6969          * @param {Object} o The data object.
6970          * @param {Object} arg The callback argument object passed to the load function.
6971          */
6972         load : true,
6973         /**
6974          * @event loadexception
6975          * Fires if an Exception occurs during data retrieval.
6976          * @param {Object} This DataProxy object.
6977          * @param {Object} o The data object.
6978          * @param {Object} arg The callback argument object passed to the load function.
6979          * @param {Object} e The Exception.
6980          */
6981         loadexception : true
6982     });
6983     Roo.data.DataProxy.superclass.constructor.call(this);
6984 };
6985
6986 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6987
6988     /**
6989      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6990      */
6991 /*
6992  * Based on:
6993  * Ext JS Library 1.1.1
6994  * Copyright(c) 2006-2007, Ext JS, LLC.
6995  *
6996  * Originally Released Under LGPL - original licence link has changed is not relivant.
6997  *
6998  * Fork - LGPL
6999  * <script type="text/javascript">
7000  */
7001 /**
7002  * @class Roo.data.MemoryProxy
7003  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7004  * to the Reader when its load method is called.
7005  * @constructor
7006  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7007  */
7008 Roo.data.MemoryProxy = function(data){
7009     if (data.data) {
7010         data = data.data;
7011     }
7012     Roo.data.MemoryProxy.superclass.constructor.call(this);
7013     this.data = data;
7014 };
7015
7016 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7017     /**
7018      * Load data from the requested source (in this case an in-memory
7019      * data object passed to the constructor), read the data object into
7020      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7021      * process that block using the passed callback.
7022      * @param {Object} params This parameter is not used by the MemoryProxy class.
7023      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7024      * object into a block of Roo.data.Records.
7025      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7026      * The function must be passed <ul>
7027      * <li>The Record block object</li>
7028      * <li>The "arg" argument from the load function</li>
7029      * <li>A boolean success indicator</li>
7030      * </ul>
7031      * @param {Object} scope The scope in which to call the callback
7032      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7033      */
7034     load : function(params, reader, callback, scope, arg){
7035         params = params || {};
7036         var result;
7037         try {
7038             result = reader.readRecords(this.data);
7039         }catch(e){
7040             this.fireEvent("loadexception", this, arg, null, e);
7041             callback.call(scope, null, arg, false);
7042             return;
7043         }
7044         callback.call(scope, result, arg, true);
7045     },
7046     
7047     // private
7048     update : function(params, records){
7049         
7050     }
7051 });/*
7052  * Based on:
7053  * Ext JS Library 1.1.1
7054  * Copyright(c) 2006-2007, Ext JS, LLC.
7055  *
7056  * Originally Released Under LGPL - original licence link has changed is not relivant.
7057  *
7058  * Fork - LGPL
7059  * <script type="text/javascript">
7060  */
7061 /**
7062  * @class Roo.data.HttpProxy
7063  * @extends Roo.data.DataProxy
7064  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7065  * configured to reference a certain URL.<br><br>
7066  * <p>
7067  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7068  * from which the running page was served.<br><br>
7069  * <p>
7070  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7071  * <p>
7072  * Be aware that to enable the browser to parse an XML document, the server must set
7073  * the Content-Type header in the HTTP response to "text/xml".
7074  * @constructor
7075  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7076  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7077  * will be used to make the request.
7078  */
7079 Roo.data.HttpProxy = function(conn){
7080     Roo.data.HttpProxy.superclass.constructor.call(this);
7081     // is conn a conn config or a real conn?
7082     this.conn = conn;
7083     this.useAjax = !conn || !conn.events;
7084   
7085 };
7086
7087 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7088     // thse are take from connection...
7089     
7090     /**
7091      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7092      */
7093     /**
7094      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7095      * extra parameters to each request made by this object. (defaults to undefined)
7096      */
7097     /**
7098      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7099      *  to each request made by this object. (defaults to undefined)
7100      */
7101     /**
7102      * @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)
7103      */
7104     /**
7105      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7106      */
7107      /**
7108      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7109      * @type Boolean
7110      */
7111   
7112
7113     /**
7114      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7115      * @type Boolean
7116      */
7117     /**
7118      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7119      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7120      * a finer-grained basis than the DataProxy events.
7121      */
7122     getConnection : function(){
7123         return this.useAjax ? Roo.Ajax : this.conn;
7124     },
7125
7126     /**
7127      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7128      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7129      * process that block using the passed callback.
7130      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7131      * for the request to the remote server.
7132      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7133      * object into a block of Roo.data.Records.
7134      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7135      * The function must be passed <ul>
7136      * <li>The Record block object</li>
7137      * <li>The "arg" argument from the load function</li>
7138      * <li>A boolean success indicator</li>
7139      * </ul>
7140      * @param {Object} scope The scope in which to call the callback
7141      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7142      */
7143     load : function(params, reader, callback, scope, arg){
7144         if(this.fireEvent("beforeload", this, params) !== false){
7145             var  o = {
7146                 params : params || {},
7147                 request: {
7148                     callback : callback,
7149                     scope : scope,
7150                     arg : arg
7151                 },
7152                 reader: reader,
7153                 callback : this.loadResponse,
7154                 scope: this
7155             };
7156             if(this.useAjax){
7157                 Roo.applyIf(o, this.conn);
7158                 if(this.activeRequest){
7159                     Roo.Ajax.abort(this.activeRequest);
7160                 }
7161                 this.activeRequest = Roo.Ajax.request(o);
7162             }else{
7163                 this.conn.request(o);
7164             }
7165         }else{
7166             callback.call(scope||this, null, arg, false);
7167         }
7168     },
7169
7170     // private
7171     loadResponse : function(o, success, response){
7172         delete this.activeRequest;
7173         if(!success){
7174             this.fireEvent("loadexception", this, o, response);
7175             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7176             return;
7177         }
7178         var result;
7179         try {
7180             result = o.reader.read(response);
7181         }catch(e){
7182             this.fireEvent("loadexception", this, o, response, e);
7183             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7184             return;
7185         }
7186         
7187         this.fireEvent("load", this, o, o.request.arg);
7188         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7189     },
7190
7191     // private
7192     update : function(dataSet){
7193
7194     },
7195
7196     // private
7197     updateResponse : function(dataSet){
7198
7199     }
7200 });/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210
7211 /**
7212  * @class Roo.data.ScriptTagProxy
7213  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7214  * other than the originating domain of the running page.<br><br>
7215  * <p>
7216  * <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
7217  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7218  * <p>
7219  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7220  * source code that is used as the source inside a &lt;script> tag.<br><br>
7221  * <p>
7222  * In order for the browser to process the returned data, the server must wrap the data object
7223  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7224  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7225  * depending on whether the callback name was passed:
7226  * <p>
7227  * <pre><code>
7228 boolean scriptTag = false;
7229 String cb = request.getParameter("callback");
7230 if (cb != null) {
7231     scriptTag = true;
7232     response.setContentType("text/javascript");
7233 } else {
7234     response.setContentType("application/x-json");
7235 }
7236 Writer out = response.getWriter();
7237 if (scriptTag) {
7238     out.write(cb + "(");
7239 }
7240 out.print(dataBlock.toJsonString());
7241 if (scriptTag) {
7242     out.write(");");
7243 }
7244 </pre></code>
7245  *
7246  * @constructor
7247  * @param {Object} config A configuration object.
7248  */
7249 Roo.data.ScriptTagProxy = function(config){
7250     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7251     Roo.apply(this, config);
7252     this.head = document.getElementsByTagName("head")[0];
7253 };
7254
7255 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7256
7257 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7258     /**
7259      * @cfg {String} url The URL from which to request the data object.
7260      */
7261     /**
7262      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7263      */
7264     timeout : 30000,
7265     /**
7266      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7267      * the server the name of the callback function set up by the load call to process the returned data object.
7268      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7269      * javascript output which calls this named function passing the data object as its only parameter.
7270      */
7271     callbackParam : "callback",
7272     /**
7273      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7274      * name to the request.
7275      */
7276     nocache : true,
7277
7278     /**
7279      * Load data from the configured URL, read the data object into
7280      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7281      * process that block using the passed callback.
7282      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7283      * for the request to the remote server.
7284      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7285      * object into a block of Roo.data.Records.
7286      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7287      * The function must be passed <ul>
7288      * <li>The Record block object</li>
7289      * <li>The "arg" argument from the load function</li>
7290      * <li>A boolean success indicator</li>
7291      * </ul>
7292      * @param {Object} scope The scope in which to call the callback
7293      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7294      */
7295     load : function(params, reader, callback, scope, arg){
7296         if(this.fireEvent("beforeload", this, params) !== false){
7297
7298             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7299
7300             var url = this.url;
7301             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7302             if(this.nocache){
7303                 url += "&_dc=" + (new Date().getTime());
7304             }
7305             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7306             var trans = {
7307                 id : transId,
7308                 cb : "stcCallback"+transId,
7309                 scriptId : "stcScript"+transId,
7310                 params : params,
7311                 arg : arg,
7312                 url : url,
7313                 callback : callback,
7314                 scope : scope,
7315                 reader : reader
7316             };
7317             var conn = this;
7318
7319             window[trans.cb] = function(o){
7320                 conn.handleResponse(o, trans);
7321             };
7322
7323             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7324
7325             if(this.autoAbort !== false){
7326                 this.abort();
7327             }
7328
7329             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7330
7331             var script = document.createElement("script");
7332             script.setAttribute("src", url);
7333             script.setAttribute("type", "text/javascript");
7334             script.setAttribute("id", trans.scriptId);
7335             this.head.appendChild(script);
7336
7337             this.trans = trans;
7338         }else{
7339             callback.call(scope||this, null, arg, false);
7340         }
7341     },
7342
7343     // private
7344     isLoading : function(){
7345         return this.trans ? true : false;
7346     },
7347
7348     /**
7349      * Abort the current server request.
7350      */
7351     abort : function(){
7352         if(this.isLoading()){
7353             this.destroyTrans(this.trans);
7354         }
7355     },
7356
7357     // private
7358     destroyTrans : function(trans, isLoaded){
7359         this.head.removeChild(document.getElementById(trans.scriptId));
7360         clearTimeout(trans.timeoutId);
7361         if(isLoaded){
7362             window[trans.cb] = undefined;
7363             try{
7364                 delete window[trans.cb];
7365             }catch(e){}
7366         }else{
7367             // if hasn't been loaded, wait for load to remove it to prevent script error
7368             window[trans.cb] = function(){
7369                 window[trans.cb] = undefined;
7370                 try{
7371                     delete window[trans.cb];
7372                 }catch(e){}
7373             };
7374         }
7375     },
7376
7377     // private
7378     handleResponse : function(o, trans){
7379         this.trans = false;
7380         this.destroyTrans(trans, true);
7381         var result;
7382         try {
7383             result = trans.reader.readRecords(o);
7384         }catch(e){
7385             this.fireEvent("loadexception", this, o, trans.arg, e);
7386             trans.callback.call(trans.scope||window, null, trans.arg, false);
7387             return;
7388         }
7389         this.fireEvent("load", this, o, trans.arg);
7390         trans.callback.call(trans.scope||window, result, trans.arg, true);
7391     },
7392
7393     // private
7394     handleFailure : function(trans){
7395         this.trans = false;
7396         this.destroyTrans(trans, false);
7397         this.fireEvent("loadexception", this, null, trans.arg);
7398         trans.callback.call(trans.scope||window, null, trans.arg, false);
7399     }
7400 });/*
7401  * Based on:
7402  * Ext JS Library 1.1.1
7403  * Copyright(c) 2006-2007, Ext JS, LLC.
7404  *
7405  * Originally Released Under LGPL - original licence link has changed is not relivant.
7406  *
7407  * Fork - LGPL
7408  * <script type="text/javascript">
7409  */
7410
7411 /**
7412  * @class Roo.data.JsonReader
7413  * @extends Roo.data.DataReader
7414  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7415  * based on mappings in a provided Roo.data.Record constructor.
7416  * 
7417  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7418  * in the reply previously. 
7419  * 
7420  * <p>
7421  * Example code:
7422  * <pre><code>
7423 var RecordDef = Roo.data.Record.create([
7424     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7425     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7426 ]);
7427 var myReader = new Roo.data.JsonReader({
7428     totalProperty: "results",    // The property which contains the total dataset size (optional)
7429     root: "rows",                // The property which contains an Array of row objects
7430     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7431 }, RecordDef);
7432 </code></pre>
7433  * <p>
7434  * This would consume a JSON file like this:
7435  * <pre><code>
7436 { 'results': 2, 'rows': [
7437     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7438     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7439 }
7440 </code></pre>
7441  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7442  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7443  * paged from the remote server.
7444  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7445  * @cfg {String} root name of the property which contains the Array of row objects.
7446  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7447  * @constructor
7448  * Create a new JsonReader
7449  * @param {Object} meta Metadata configuration options
7450  * @param {Object} recordType Either an Array of field definition objects,
7451  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7452  */
7453 Roo.data.JsonReader = function(meta, recordType){
7454     
7455     meta = meta || {};
7456     // set some defaults:
7457     Roo.applyIf(meta, {
7458         totalProperty: 'total',
7459         successProperty : 'success',
7460         root : 'data',
7461         id : 'id'
7462     });
7463     
7464     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7465 };
7466 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7467     
7468     /**
7469      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7470      * Used by Store query builder to append _requestMeta to params.
7471      * 
7472      */
7473     metaFromRemote : false,
7474     /**
7475      * This method is only used by a DataProxy which has retrieved data from a remote server.
7476      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7477      * @return {Object} data A data block which is used by an Roo.data.Store object as
7478      * a cache of Roo.data.Records.
7479      */
7480     read : function(response){
7481         var json = response.responseText;
7482        
7483         var o = /* eval:var:o */ eval("("+json+")");
7484         if(!o) {
7485             throw {message: "JsonReader.read: Json object not found"};
7486         }
7487         
7488         if(o.metaData){
7489             
7490             delete this.ef;
7491             this.metaFromRemote = true;
7492             this.meta = o.metaData;
7493             this.recordType = Roo.data.Record.create(o.metaData.fields);
7494             this.onMetaChange(this.meta, this.recordType, o);
7495         }
7496         return this.readRecords(o);
7497     },
7498
7499     // private function a store will implement
7500     onMetaChange : function(meta, recordType, o){
7501
7502     },
7503
7504     /**
7505          * @ignore
7506          */
7507     simpleAccess: function(obj, subsc) {
7508         return obj[subsc];
7509     },
7510
7511         /**
7512          * @ignore
7513          */
7514     getJsonAccessor: function(){
7515         var re = /[\[\.]/;
7516         return function(expr) {
7517             try {
7518                 return(re.test(expr))
7519                     ? new Function("obj", "return obj." + expr)
7520                     : function(obj){
7521                         return obj[expr];
7522                     };
7523             } catch(e){}
7524             return Roo.emptyFn;
7525         };
7526     }(),
7527
7528     /**
7529      * Create a data block containing Roo.data.Records from an XML document.
7530      * @param {Object} o An object which contains an Array of row objects in the property specified
7531      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7532      * which contains the total size of the dataset.
7533      * @return {Object} data A data block which is used by an Roo.data.Store object as
7534      * a cache of Roo.data.Records.
7535      */
7536     readRecords : function(o){
7537         /**
7538          * After any data loads, the raw JSON data is available for further custom processing.
7539          * @type Object
7540          */
7541         this.o = o;
7542         var s = this.meta, Record = this.recordType,
7543             f = Record.prototype.fields, fi = f.items, fl = f.length;
7544
7545 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7546         if (!this.ef) {
7547             if(s.totalProperty) {
7548                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7549                 }
7550                 if(s.successProperty) {
7551                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7552                 }
7553                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7554                 if (s.id) {
7555                         var g = this.getJsonAccessor(s.id);
7556                         this.getId = function(rec) {
7557                                 var r = g(rec);
7558                                 return (r === undefined || r === "") ? null : r;
7559                         };
7560                 } else {
7561                         this.getId = function(){return null;};
7562                 }
7563             this.ef = [];
7564             for(var jj = 0; jj < fl; jj++){
7565                 f = fi[jj];
7566                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7567                 this.ef[jj] = this.getJsonAccessor(map);
7568             }
7569         }
7570
7571         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7572         if(s.totalProperty){
7573             var vt = parseInt(this.getTotal(o), 10);
7574             if(!isNaN(vt)){
7575                 totalRecords = vt;
7576             }
7577         }
7578         if(s.successProperty){
7579             var vs = this.getSuccess(o);
7580             if(vs === false || vs === 'false'){
7581                 success = false;
7582             }
7583         }
7584         var records = [];
7585             for(var i = 0; i < c; i++){
7586                     var n = root[i];
7587                 var values = {};
7588                 var id = this.getId(n);
7589                 for(var j = 0; j < fl; j++){
7590                     f = fi[j];
7591                 var v = this.ef[j](n);
7592                 if (!f.convert) {
7593                     Roo.log('missing convert for ' + f.name);
7594                     Roo.log(f);
7595                     continue;
7596                 }
7597                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7598                 }
7599                 var record = new Record(values, id);
7600                 record.json = n;
7601                 records[i] = record;
7602             }
7603             return {
7604             raw : o,
7605                 success : success,
7606                 records : records,
7607                 totalRecords : totalRecords
7608             };
7609     }
7610 });/*
7611  * Based on:
7612  * Ext JS Library 1.1.1
7613  * Copyright(c) 2006-2007, Ext JS, LLC.
7614  *
7615  * Originally Released Under LGPL - original licence link has changed is not relivant.
7616  *
7617  * Fork - LGPL
7618  * <script type="text/javascript">
7619  */
7620
7621 /**
7622  * @class Roo.data.ArrayReader
7623  * @extends Roo.data.DataReader
7624  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7625  * Each element of that Array represents a row of data fields. The
7626  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7627  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7628  * <p>
7629  * Example code:.
7630  * <pre><code>
7631 var RecordDef = Roo.data.Record.create([
7632     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7633     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7634 ]);
7635 var myReader = new Roo.data.ArrayReader({
7636     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7637 }, RecordDef);
7638 </code></pre>
7639  * <p>
7640  * This would consume an Array like this:
7641  * <pre><code>
7642 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7643   </code></pre>
7644  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7645  * @constructor
7646  * Create a new JsonReader
7647  * @param {Object} meta Metadata configuration options.
7648  * @param {Object} recordType Either an Array of field definition objects
7649  * as specified to {@link Roo.data.Record#create},
7650  * or an {@link Roo.data.Record} object
7651  * created using {@link Roo.data.Record#create}.
7652  */
7653 Roo.data.ArrayReader = function(meta, recordType){
7654     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7655 };
7656
7657 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7658     /**
7659      * Create a data block containing Roo.data.Records from an XML document.
7660      * @param {Object} o An Array of row objects which represents the dataset.
7661      * @return {Object} data A data block which is used by an Roo.data.Store object as
7662      * a cache of Roo.data.Records.
7663      */
7664     readRecords : function(o){
7665         var sid = this.meta ? this.meta.id : null;
7666         var recordType = this.recordType, fields = recordType.prototype.fields;
7667         var records = [];
7668         var root = o;
7669             for(var i = 0; i < root.length; i++){
7670                     var n = root[i];
7671                 var values = {};
7672                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7673                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7674                 var f = fields.items[j];
7675                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7676                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7677                 v = f.convert(v);
7678                 values[f.name] = v;
7679             }
7680                 var record = new recordType(values, id);
7681                 record.json = n;
7682                 records[records.length] = record;
7683             }
7684             return {
7685                 records : records,
7686                 totalRecords : records.length
7687             };
7688     }
7689 });/*
7690  * - LGPL
7691  * * 
7692  */
7693
7694 /**
7695  * @class Roo.bootstrap.ComboBox
7696  * @extends Roo.bootstrap.TriggerField
7697  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7698  * @cfg {Boolean} append (true|false) default false
7699  * @constructor
7700  * Create a new ComboBox.
7701  * @param {Object} config Configuration options
7702  */
7703 Roo.bootstrap.ComboBox = function(config){
7704     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7705     this.addEvents({
7706         /**
7707          * @event expand
7708          * Fires when the dropdown list is expanded
7709              * @param {Roo.bootstrap.ComboBox} combo This combo box
7710              */
7711         'expand' : true,
7712         /**
7713          * @event collapse
7714          * Fires when the dropdown list is collapsed
7715              * @param {Roo.bootstrap.ComboBox} combo This combo box
7716              */
7717         'collapse' : true,
7718         /**
7719          * @event beforeselect
7720          * Fires before a list item is selected. Return false to cancel the selection.
7721              * @param {Roo.bootstrap.ComboBox} combo This combo box
7722              * @param {Roo.data.Record} record The data record returned from the underlying store
7723              * @param {Number} index The index of the selected item in the dropdown list
7724              */
7725         'beforeselect' : true,
7726         /**
7727          * @event select
7728          * Fires when a list item is selected
7729              * @param {Roo.bootstrap.ComboBox} combo This combo box
7730              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7731              * @param {Number} index The index of the selected item in the dropdown list
7732              */
7733         'select' : true,
7734         /**
7735          * @event beforequery
7736          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7737          * The event object passed has these properties:
7738              * @param {Roo.bootstrap.ComboBox} combo This combo box
7739              * @param {String} query The query
7740              * @param {Boolean} forceAll true to force "all" query
7741              * @param {Boolean} cancel true to cancel the query
7742              * @param {Object} e The query event object
7743              */
7744         'beforequery': true,
7745          /**
7746          * @event add
7747          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7748              * @param {Roo.bootstrap.ComboBox} combo This combo box
7749              */
7750         'add' : true,
7751         /**
7752          * @event edit
7753          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7754              * @param {Roo.bootstrap.ComboBox} combo This combo box
7755              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7756              */
7757         'edit' : true,
7758         /**
7759          * @event remove
7760          * Fires when the remove value from the combobox array
7761              * @param {Roo.bootstrap.ComboBox} combo This combo box
7762              */
7763         'remove' : true
7764         
7765     });
7766     
7767     
7768     this.selectedIndex = -1;
7769     if(this.mode == 'local'){
7770         if(config.queryDelay === undefined){
7771             this.queryDelay = 10;
7772         }
7773         if(config.minChars === undefined){
7774             this.minChars = 0;
7775         }
7776     }
7777 };
7778
7779 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7780      
7781     /**
7782      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7783      * rendering into an Roo.Editor, defaults to false)
7784      */
7785     /**
7786      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7787      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7788      */
7789     /**
7790      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7791      */
7792     /**
7793      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7794      * the dropdown list (defaults to undefined, with no header element)
7795      */
7796
7797      /**
7798      * @cfg {String/Roo.Template} tpl The template to use to render the output
7799      */
7800      
7801      /**
7802      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7803      */
7804     listWidth: undefined,
7805     /**
7806      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7807      * mode = 'remote' or 'text' if mode = 'local')
7808      */
7809     displayField: undefined,
7810     /**
7811      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7812      * mode = 'remote' or 'value' if mode = 'local'). 
7813      * Note: use of a valueField requires the user make a selection
7814      * in order for a value to be mapped.
7815      */
7816     valueField: undefined,
7817     
7818     
7819     /**
7820      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7821      * field's data value (defaults to the underlying DOM element's name)
7822      */
7823     hiddenName: undefined,
7824     /**
7825      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7826      */
7827     listClass: '',
7828     /**
7829      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7830      */
7831     selectedClass: 'active',
7832     
7833     /**
7834      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7835      */
7836     shadow:'sides',
7837     /**
7838      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7839      * anchor positions (defaults to 'tl-bl')
7840      */
7841     listAlign: 'tl-bl?',
7842     /**
7843      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7844      */
7845     maxHeight: 300,
7846     /**
7847      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7848      * query specified by the allQuery config option (defaults to 'query')
7849      */
7850     triggerAction: 'query',
7851     /**
7852      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7853      * (defaults to 4, does not apply if editable = false)
7854      */
7855     minChars : 4,
7856     /**
7857      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7858      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7859      */
7860     typeAhead: false,
7861     /**
7862      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7863      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7864      */
7865     queryDelay: 500,
7866     /**
7867      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7868      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7869      */
7870     pageSize: 0,
7871     /**
7872      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7873      * when editable = true (defaults to false)
7874      */
7875     selectOnFocus:false,
7876     /**
7877      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7878      */
7879     queryParam: 'query',
7880     /**
7881      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7882      * when mode = 'remote' (defaults to 'Loading...')
7883      */
7884     loadingText: 'Loading...',
7885     /**
7886      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7887      */
7888     resizable: false,
7889     /**
7890      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7891      */
7892     handleHeight : 8,
7893     /**
7894      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7895      * traditional select (defaults to true)
7896      */
7897     editable: true,
7898     /**
7899      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7900      */
7901     allQuery: '',
7902     /**
7903      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7904      */
7905     mode: 'remote',
7906     /**
7907      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7908      * listWidth has a higher value)
7909      */
7910     minListWidth : 70,
7911     /**
7912      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7913      * allow the user to set arbitrary text into the field (defaults to false)
7914      */
7915     forceSelection:false,
7916     /**
7917      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7918      * if typeAhead = true (defaults to 250)
7919      */
7920     typeAheadDelay : 250,
7921     /**
7922      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7923      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7924      */
7925     valueNotFoundText : undefined,
7926     /**
7927      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7928      */
7929     blockFocus : false,
7930     
7931     /**
7932      * @cfg {Boolean} disableClear Disable showing of clear button.
7933      */
7934     disableClear : false,
7935     /**
7936      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7937      */
7938     alwaysQuery : false,
7939     
7940     /**
7941      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7942      */
7943     multiple : false,
7944     
7945     //private
7946     addicon : false,
7947     editicon: false,
7948     
7949     page: 0,
7950     hasQuery: false,
7951     append: false,
7952     loadNext: false,
7953     item: [],
7954     
7955     // element that contains real text value.. (when hidden is used..)
7956      
7957     // private
7958     initEvents: function(){
7959         
7960         if (!this.store) {
7961             throw "can not find store for combo";
7962         }
7963         this.store = Roo.factory(this.store, Roo.data);
7964         
7965         
7966         
7967         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7968         
7969         
7970         if(this.hiddenName){
7971             
7972             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7973             
7974             this.hiddenField.dom.value =
7975                 this.hiddenValue !== undefined ? this.hiddenValue :
7976                 this.value !== undefined ? this.value : '';
7977
7978             // prevent input submission
7979             this.el.dom.removeAttribute('name');
7980             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7981              
7982              
7983         }
7984         //if(Roo.isGecko){
7985         //    this.el.dom.setAttribute('autocomplete', 'off');
7986         //}
7987
7988         var cls = 'x-combo-list';
7989         this.list = this.el.select('ul.dropdown-menu',true).first();
7990
7991         //this.list = new Roo.Layer({
7992         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7993         //});
7994         
7995         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7996         this.list.setWidth(lw);
7997         
7998         this.list.on('mouseover', this.onViewOver, this);
7999         this.list.on('mousemove', this.onViewMove, this);
8000         
8001         this.list.on('scroll', this.onViewScroll, this);
8002         
8003         /*
8004         this.list.swallowEvent('mousewheel');
8005         this.assetHeight = 0;
8006
8007         if(this.title){
8008             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8009             this.assetHeight += this.header.getHeight();
8010         }
8011
8012         this.innerList = this.list.createChild({cls:cls+'-inner'});
8013         this.innerList.on('mouseover', this.onViewOver, this);
8014         this.innerList.on('mousemove', this.onViewMove, this);
8015         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8016         
8017         if(this.allowBlank && !this.pageSize && !this.disableClear){
8018             this.footer = this.list.createChild({cls:cls+'-ft'});
8019             this.pageTb = new Roo.Toolbar(this.footer);
8020            
8021         }
8022         if(this.pageSize){
8023             this.footer = this.list.createChild({cls:cls+'-ft'});
8024             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8025                     {pageSize: this.pageSize});
8026             
8027         }
8028         
8029         if (this.pageTb && this.allowBlank && !this.disableClear) {
8030             var _this = this;
8031             this.pageTb.add(new Roo.Toolbar.Fill(), {
8032                 cls: 'x-btn-icon x-btn-clear',
8033                 text: '&#160;',
8034                 handler: function()
8035                 {
8036                     _this.collapse();
8037                     _this.clearValue();
8038                     _this.onSelect(false, -1);
8039                 }
8040             });
8041         }
8042         if (this.footer) {
8043             this.assetHeight += this.footer.getHeight();
8044         }
8045         */
8046             
8047         if(!this.tpl){
8048             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8049         }
8050
8051         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8052             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8053         });
8054         //this.view.wrapEl.setDisplayed(false);
8055         this.view.on('click', this.onViewClick, this);
8056         
8057         
8058         
8059         this.store.on('beforeload', this.onBeforeLoad, this);
8060         this.store.on('load', this.onLoad, this);
8061         this.store.on('loadexception', this.onLoadException, this);
8062         /*
8063         if(this.resizable){
8064             this.resizer = new Roo.Resizable(this.list,  {
8065                pinned:true, handles:'se'
8066             });
8067             this.resizer.on('resize', function(r, w, h){
8068                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8069                 this.listWidth = w;
8070                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8071                 this.restrictHeight();
8072             }, this);
8073             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8074         }
8075         */
8076         if(!this.editable){
8077             this.editable = true;
8078             this.setEditable(false);
8079         }
8080         
8081         /*
8082         
8083         if (typeof(this.events.add.listeners) != 'undefined') {
8084             
8085             this.addicon = this.wrap.createChild(
8086                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8087        
8088             this.addicon.on('click', function(e) {
8089                 this.fireEvent('add', this);
8090             }, this);
8091         }
8092         if (typeof(this.events.edit.listeners) != 'undefined') {
8093             
8094             this.editicon = this.wrap.createChild(
8095                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8096             if (this.addicon) {
8097                 this.editicon.setStyle('margin-left', '40px');
8098             }
8099             this.editicon.on('click', function(e) {
8100                 
8101                 // we fire even  if inothing is selected..
8102                 this.fireEvent('edit', this, this.lastData );
8103                 
8104             }, this);
8105         }
8106         */
8107         
8108         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8109             "up" : function(e){
8110                 this.inKeyMode = true;
8111                 this.selectPrev();
8112             },
8113
8114             "down" : function(e){
8115                 if(!this.isExpanded()){
8116                     this.onTriggerClick();
8117                 }else{
8118                     this.inKeyMode = true;
8119                     this.selectNext();
8120                 }
8121             },
8122
8123             "enter" : function(e){
8124                 this.onViewClick();
8125                 //return true;
8126             },
8127
8128             "esc" : function(e){
8129                 this.collapse();
8130             },
8131
8132             "tab" : function(e){
8133                 this.collapse();
8134                 
8135                 if(this.fireEvent("specialkey", this, e)){
8136                     this.onViewClick(false);
8137                 }
8138                 
8139                 return true;
8140             },
8141
8142             scope : this,
8143
8144             doRelay : function(foo, bar, hname){
8145                 if(hname == 'down' || this.scope.isExpanded()){
8146                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8147                 }
8148                 return true;
8149             },
8150
8151             forceKeyDown: true
8152         });
8153         
8154         
8155         this.queryDelay = Math.max(this.queryDelay || 10,
8156                 this.mode == 'local' ? 10 : 250);
8157         
8158         
8159         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8160         
8161         if(this.typeAhead){
8162             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8163         }
8164         if(this.editable !== false){
8165             this.inputEl().on("keyup", this.onKeyUp, this);
8166         }
8167         if(this.forceSelection){
8168             this.on('blur', this.doForce, this);
8169         }
8170         
8171         if(this.multiple){
8172             this.choices = this.el.select('ul.select2-choices', true).first();
8173             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8174         }
8175     },
8176
8177     onDestroy : function(){
8178         if(this.view){
8179             this.view.setStore(null);
8180             this.view.el.removeAllListeners();
8181             this.view.el.remove();
8182             this.view.purgeListeners();
8183         }
8184         if(this.list){
8185             this.list.dom.innerHTML  = '';
8186         }
8187         if(this.store){
8188             this.store.un('beforeload', this.onBeforeLoad, this);
8189             this.store.un('load', this.onLoad, this);
8190             this.store.un('loadexception', this.onLoadException, this);
8191         }
8192         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8193     },
8194
8195     // private
8196     fireKey : function(e){
8197         if(e.isNavKeyPress() && !this.list.isVisible()){
8198             this.fireEvent("specialkey", this, e);
8199         }
8200     },
8201
8202     // private
8203     onResize: function(w, h){
8204 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8205 //        
8206 //        if(typeof w != 'number'){
8207 //            // we do not handle it!?!?
8208 //            return;
8209 //        }
8210 //        var tw = this.trigger.getWidth();
8211 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8212 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8213 //        var x = w - tw;
8214 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8215 //            
8216 //        //this.trigger.setStyle('left', x+'px');
8217 //        
8218 //        if(this.list && this.listWidth === undefined){
8219 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8220 //            this.list.setWidth(lw);
8221 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8222 //        }
8223         
8224     
8225         
8226     },
8227
8228     /**
8229      * Allow or prevent the user from directly editing the field text.  If false is passed,
8230      * the user will only be able to select from the items defined in the dropdown list.  This method
8231      * is the runtime equivalent of setting the 'editable' config option at config time.
8232      * @param {Boolean} value True to allow the user to directly edit the field text
8233      */
8234     setEditable : function(value){
8235         if(value == this.editable){
8236             return;
8237         }
8238         this.editable = value;
8239         if(!value){
8240             this.inputEl().dom.setAttribute('readOnly', true);
8241             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8242             this.inputEl().addClass('x-combo-noedit');
8243         }else{
8244             this.inputEl().dom.setAttribute('readOnly', false);
8245             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8246             this.inputEl().removeClass('x-combo-noedit');
8247         }
8248     },
8249
8250     // private
8251     
8252     onBeforeLoad : function(combo,opts){
8253         if(!this.hasFocus){
8254             return;
8255         }
8256          if (!opts.add) {
8257             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8258          }
8259         this.restrictHeight();
8260         this.selectedIndex = -1;
8261     },
8262
8263     // private
8264     onLoad : function(){
8265         
8266         this.hasQuery = false;
8267         
8268         if(!this.hasFocus){
8269             return;
8270         }
8271         
8272         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8273             this.loading.hide();
8274         }
8275         
8276         if(this.store.getCount() > 0){
8277             this.expand();
8278             this.restrictHeight();
8279             if(this.lastQuery == this.allQuery){
8280                 if(this.editable){
8281                     this.inputEl().dom.select();
8282                 }
8283                 if(!this.selectByValue(this.value, true)){
8284                     this.select(0, true);
8285                 }
8286             }else{
8287                 this.selectNext();
8288                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8289                     this.taTask.delay(this.typeAheadDelay);
8290                 }
8291             }
8292         }else{
8293             this.onEmptyResults();
8294         }
8295         
8296         //this.el.focus();
8297     },
8298     // private
8299     onLoadException : function()
8300     {
8301         this.hasQuery = false;
8302         
8303         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8304             this.loading.hide();
8305         }
8306         
8307         this.collapse();
8308         Roo.log(this.store.reader.jsonData);
8309         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8310             // fixme
8311             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8312         }
8313         
8314         
8315     },
8316     // private
8317     onTypeAhead : function(){
8318         if(this.store.getCount() > 0){
8319             var r = this.store.getAt(0);
8320             var newValue = r.data[this.displayField];
8321             var len = newValue.length;
8322             var selStart = this.getRawValue().length;
8323             
8324             if(selStart != len){
8325                 this.setRawValue(newValue);
8326                 this.selectText(selStart, newValue.length);
8327             }
8328         }
8329     },
8330
8331     // private
8332     onSelect : function(record, index){
8333         
8334         if(this.fireEvent('beforeselect', this, record, index) !== false){
8335         
8336             this.setFromData(index > -1 ? record.data : false);
8337             
8338             this.collapse();
8339             this.fireEvent('select', this, record, index);
8340         }
8341     },
8342
8343     /**
8344      * Returns the currently selected field value or empty string if no value is set.
8345      * @return {String} value The selected value
8346      */
8347     getValue : function(){
8348         
8349         if(this.multiple){
8350             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8351         }
8352         
8353         if(this.valueField){
8354             return typeof this.value != 'undefined' ? this.value : '';
8355         }else{
8356             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8357         }
8358     },
8359
8360     /**
8361      * Clears any text/value currently set in the field
8362      */
8363     clearValue : function(){
8364         if(this.hiddenField){
8365             this.hiddenField.dom.value = '';
8366         }
8367         this.value = '';
8368         this.setRawValue('');
8369         this.lastSelectionText = '';
8370         
8371     },
8372
8373     /**
8374      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8375      * will be displayed in the field.  If the value does not match the data value of an existing item,
8376      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8377      * Otherwise the field will be blank (although the value will still be set).
8378      * @param {String} value The value to match
8379      */
8380     setValue : function(v){
8381         if(this.multiple){
8382             this.syncValue();
8383             return;
8384         }
8385         
8386         var text = v;
8387         if(this.valueField){
8388             var r = this.findRecord(this.valueField, v);
8389             if(r){
8390                 text = r.data[this.displayField];
8391             }else if(this.valueNotFoundText !== undefined){
8392                 text = this.valueNotFoundText;
8393             }
8394         }
8395         this.lastSelectionText = text;
8396         if(this.hiddenField){
8397             this.hiddenField.dom.value = v;
8398         }
8399         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8400         this.value = v;
8401     },
8402     /**
8403      * @property {Object} the last set data for the element
8404      */
8405     
8406     lastData : false,
8407     /**
8408      * Sets the value of the field based on a object which is related to the record format for the store.
8409      * @param {Object} value the value to set as. or false on reset?
8410      */
8411     setFromData : function(o){
8412         
8413         if(this.multiple){
8414             this.addItem(o);
8415             return;
8416         }
8417             
8418         var dv = ''; // display value
8419         var vv = ''; // value value..
8420         this.lastData = o;
8421         if (this.displayField) {
8422             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8423         } else {
8424             // this is an error condition!!!
8425             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8426         }
8427         
8428         if(this.valueField){
8429             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8430         }
8431         
8432         if(this.hiddenField){
8433             this.hiddenField.dom.value = vv;
8434             
8435             this.lastSelectionText = dv;
8436             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8437             this.value = vv;
8438             return;
8439         }
8440         // no hidden field.. - we store the value in 'value', but still display
8441         // display field!!!!
8442         this.lastSelectionText = dv;
8443         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8444         this.value = vv;
8445         
8446         
8447     },
8448     // private
8449     reset : function(){
8450         // overridden so that last data is reset..
8451         this.setValue(this.originalValue);
8452         this.clearInvalid();
8453         this.lastData = false;
8454         if (this.view) {
8455             this.view.clearSelections();
8456         }
8457     },
8458     // private
8459     findRecord : function(prop, value){
8460         var record;
8461         if(this.store.getCount() > 0){
8462             this.store.each(function(r){
8463                 if(r.data[prop] == value){
8464                     record = r;
8465                     return false;
8466                 }
8467                 return true;
8468             });
8469         }
8470         return record;
8471     },
8472     
8473     getName: function()
8474     {
8475         // returns hidden if it's set..
8476         if (!this.rendered) {return ''};
8477         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8478         
8479     },
8480     // private
8481     onViewMove : function(e, t){
8482         this.inKeyMode = false;
8483     },
8484
8485     // private
8486     onViewOver : function(e, t){
8487         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8488             return;
8489         }
8490         var item = this.view.findItemFromChild(t);
8491         if(item){
8492             var index = this.view.indexOf(item);
8493             this.select(index, false);
8494         }
8495     },
8496
8497     // private
8498     onViewClick : function(doFocus)
8499     {
8500         var index = this.view.getSelectedIndexes()[0];
8501         var r = this.store.getAt(index);
8502         if(r){
8503             this.onSelect(r, index);
8504         }
8505         if(doFocus !== false && !this.blockFocus){
8506             this.inputEl().focus();
8507         }
8508     },
8509
8510     // private
8511     restrictHeight : function(){
8512         //this.innerList.dom.style.height = '';
8513         //var inner = this.innerList.dom;
8514         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8515         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8516         //this.list.beginUpdate();
8517         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8518         this.list.alignTo(this.inputEl(), this.listAlign);
8519         //this.list.endUpdate();
8520     },
8521
8522     // private
8523     onEmptyResults : function(){
8524         this.collapse();
8525     },
8526
8527     /**
8528      * Returns true if the dropdown list is expanded, else false.
8529      */
8530     isExpanded : function(){
8531         return this.list.isVisible();
8532     },
8533
8534     /**
8535      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8536      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8537      * @param {String} value The data value of the item to select
8538      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8539      * selected item if it is not currently in view (defaults to true)
8540      * @return {Boolean} True if the value matched an item in the list, else false
8541      */
8542     selectByValue : function(v, scrollIntoView){
8543         if(v !== undefined && v !== null){
8544             var r = this.findRecord(this.valueField || this.displayField, v);
8545             if(r){
8546                 this.select(this.store.indexOf(r), scrollIntoView);
8547                 return true;
8548             }
8549         }
8550         return false;
8551     },
8552
8553     /**
8554      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8555      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8556      * @param {Number} index The zero-based index of the list item to select
8557      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8558      * selected item if it is not currently in view (defaults to true)
8559      */
8560     select : function(index, scrollIntoView){
8561         this.selectedIndex = index;
8562         this.view.select(index);
8563         if(scrollIntoView !== false){
8564             var el = this.view.getNode(index);
8565             if(el){
8566                 //this.innerList.scrollChildIntoView(el, false);
8567                 
8568             }
8569         }
8570     },
8571
8572     // private
8573     selectNext : function(){
8574         var ct = this.store.getCount();
8575         if(ct > 0){
8576             if(this.selectedIndex == -1){
8577                 this.select(0);
8578             }else if(this.selectedIndex < ct-1){
8579                 this.select(this.selectedIndex+1);
8580             }
8581         }
8582     },
8583
8584     // private
8585     selectPrev : function(){
8586         var ct = this.store.getCount();
8587         if(ct > 0){
8588             if(this.selectedIndex == -1){
8589                 this.select(0);
8590             }else if(this.selectedIndex != 0){
8591                 this.select(this.selectedIndex-1);
8592             }
8593         }
8594     },
8595
8596     // private
8597     onKeyUp : function(e){
8598         if(this.editable !== false && !e.isSpecialKey()){
8599             this.lastKey = e.getKey();
8600             this.dqTask.delay(this.queryDelay);
8601         }
8602     },
8603
8604     // private
8605     validateBlur : function(){
8606         return !this.list || !this.list.isVisible();   
8607     },
8608
8609     // private
8610     initQuery : function(){
8611         this.doQuery(this.getRawValue());
8612     },
8613
8614     // private
8615     doForce : function(){
8616         if(this.el.dom.value.length > 0){
8617             this.el.dom.value =
8618                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8619              
8620         }
8621     },
8622
8623     /**
8624      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8625      * query allowing the query action to be canceled if needed.
8626      * @param {String} query The SQL query to execute
8627      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8628      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8629      * saved in the current store (defaults to false)
8630      */
8631     doQuery : function(q, forceAll){
8632         
8633         if(q === undefined || q === null){
8634             q = '';
8635         }
8636         var qe = {
8637             query: q,
8638             forceAll: forceAll,
8639             combo: this,
8640             cancel:false
8641         };
8642         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8643             return false;
8644         }
8645         q = qe.query;
8646         
8647         forceAll = qe.forceAll;
8648         if(forceAll === true || (q.length >= this.minChars)){
8649             
8650             this.hasQuery = true;
8651             
8652             if(this.lastQuery != q || this.alwaysQuery){
8653                 this.lastQuery = q;
8654                 if(this.mode == 'local'){
8655                     this.selectedIndex = -1;
8656                     if(forceAll){
8657                         this.store.clearFilter();
8658                     }else{
8659                         this.store.filter(this.displayField, q);
8660                     }
8661                     this.onLoad();
8662                 }else{
8663                     this.store.baseParams[this.queryParam] = q;
8664                     
8665                     var options = {params : this.getParams(q)};
8666                     
8667                     if(this.loadNext){
8668                         options.add = true;
8669                         options.params.start = this.page * this.pageSize;
8670                     }
8671                     
8672                     this.store.load(options);
8673                     this.expand();
8674                 }
8675             }else{
8676                 this.selectedIndex = -1;
8677                 this.onLoad();   
8678             }
8679         }
8680         
8681         this.loadNext = false;
8682     },
8683
8684     // private
8685     getParams : function(q){
8686         var p = {};
8687         //p[this.queryParam] = q;
8688         
8689         if(this.pageSize){
8690             p.start = 0;
8691             p.limit = this.pageSize;
8692         }
8693         return p;
8694     },
8695
8696     /**
8697      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8698      */
8699     collapse : function(){
8700         if(!this.isExpanded()){
8701             return;
8702         }
8703         
8704         this.list.hide();
8705         Roo.get(document).un('mousedown', this.collapseIf, this);
8706         Roo.get(document).un('mousewheel', this.collapseIf, this);
8707         if (!this.editable) {
8708             Roo.get(document).un('keydown', this.listKeyPress, this);
8709         }
8710         this.fireEvent('collapse', this);
8711     },
8712
8713     // private
8714     collapseIf : function(e){
8715         var in_combo  = e.within(this.el);
8716         var in_list =  e.within(this.list);
8717         
8718         if (in_combo || in_list) {
8719             //e.stopPropagation();
8720             return;
8721         }
8722
8723         this.collapse();
8724         
8725     },
8726
8727     /**
8728      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8729      */
8730     expand : function(){
8731        
8732         if(this.isExpanded() || !this.hasFocus){
8733             return;
8734         }
8735          Roo.log('expand');
8736         this.list.alignTo(this.inputEl(), this.listAlign);
8737         this.list.show();
8738         Roo.get(document).on('mousedown', this.collapseIf, this);
8739         Roo.get(document).on('mousewheel', this.collapseIf, this);
8740         if (!this.editable) {
8741             Roo.get(document).on('keydown', this.listKeyPress, this);
8742         }
8743         
8744         this.fireEvent('expand', this);
8745     },
8746
8747     // private
8748     // Implements the default empty TriggerField.onTriggerClick function
8749     onTriggerClick : function()
8750     {
8751         Roo.log('trigger click');
8752         
8753         if(this.disabled){
8754             return;
8755         }
8756         
8757         this.page = 0;
8758         this.loadNext = false;
8759         
8760         if(this.isExpanded()){
8761             this.collapse();
8762             if (!this.blockFocus) {
8763                 this.inputEl().focus();
8764             }
8765             
8766         }else {
8767             this.hasFocus = true;
8768             if(this.triggerAction == 'all') {
8769                 this.doQuery(this.allQuery, true);
8770             } else {
8771                 this.doQuery(this.getRawValue());
8772             }
8773             if (!this.blockFocus) {
8774                 this.inputEl().focus();
8775             }
8776         }
8777     },
8778     listKeyPress : function(e)
8779     {
8780         //Roo.log('listkeypress');
8781         // scroll to first matching element based on key pres..
8782         if (e.isSpecialKey()) {
8783             return false;
8784         }
8785         var k = String.fromCharCode(e.getKey()).toUpperCase();
8786         //Roo.log(k);
8787         var match  = false;
8788         var csel = this.view.getSelectedNodes();
8789         var cselitem = false;
8790         if (csel.length) {
8791             var ix = this.view.indexOf(csel[0]);
8792             cselitem  = this.store.getAt(ix);
8793             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8794                 cselitem = false;
8795             }
8796             
8797         }
8798         
8799         this.store.each(function(v) { 
8800             if (cselitem) {
8801                 // start at existing selection.
8802                 if (cselitem.id == v.id) {
8803                     cselitem = false;
8804                 }
8805                 return true;
8806             }
8807                 
8808             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8809                 match = this.store.indexOf(v);
8810                 return false;
8811             }
8812             return true;
8813         }, this);
8814         
8815         if (match === false) {
8816             return true; // no more action?
8817         }
8818         // scroll to?
8819         this.view.select(match);
8820         var sn = Roo.get(this.view.getSelectedNodes()[0])
8821         //sn.scrollIntoView(sn.dom.parentNode, false);
8822     },
8823     
8824     onViewScroll : function(e, t){
8825         
8826         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8827             return;
8828         }
8829         
8830         this.hasQuery = true;
8831         
8832         this.loading = this.list.select('.loading', true).first();
8833         
8834         if(this.loading === null){
8835             this.list.createChild({
8836                 tag: 'div',
8837                 cls: 'loading select2-more-results select2-active',
8838                 html: 'Loading more results...'
8839             })
8840             
8841             this.loading = this.list.select('.loading', true).first();
8842             
8843             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8844             
8845             this.loading.hide();
8846         }
8847         
8848         this.loading.show();
8849         
8850         var _combo = this;
8851         
8852         this.page++;
8853         this.loadNext = true;
8854         
8855         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8856         
8857         return;
8858     },
8859     
8860     addItem : function(o)
8861     {   
8862         var dv = ''; // display value
8863         
8864         if (this.displayField) {
8865             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8866         } else {
8867             // this is an error condition!!!
8868             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8869         }
8870         
8871         if(!dv.length){
8872             return;
8873         }
8874         
8875         var choice = this.choices.createChild({
8876             tag: 'li',
8877             cls: 'select2-search-choice',
8878             cn: [
8879                 {
8880                     tag: 'div',
8881                     html: dv
8882                 },
8883                 {
8884                     tag: 'a',
8885                     href: '#',
8886                     cls: 'select2-search-choice-close',
8887                     tabindex: '-1'
8888                 }
8889             ]
8890             
8891         }, this.searchField);
8892         
8893         var close = choice.select('a.select2-search-choice-close', true).first()
8894         
8895         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8896         
8897         this.item.push(o);
8898         this.lastData = o;
8899         
8900         this.syncValue();
8901         
8902         this.inputEl().dom.value = '';
8903         
8904     },
8905     
8906     onRemoveItem : function(e, _self, o)
8907     {
8908         Roo.log('remove item');
8909         var index = this.item.indexOf(o.data) * 1;
8910         
8911         if( index < 0){
8912             Roo.log('not this item?!');
8913             return;
8914         }
8915         
8916         this.item.splice(index, 1);
8917         o.item.remove();
8918         
8919         this.syncValue();
8920         
8921         this.fireEvent('remove', this);
8922         
8923     },
8924     
8925     syncValue : function()
8926     {
8927         if(!this.item.length){
8928             this.clearValue();
8929             return;
8930         }
8931             
8932         var value = [];
8933         var _this = this;
8934         Roo.each(this.item, function(i){
8935             if(_this.valueField){
8936                 value.push(i[_this.valueField]);
8937                 return;
8938             }
8939
8940             value.push(i);
8941         });
8942
8943         this.value = value.join(',');
8944
8945         if(this.hiddenField){
8946             this.hiddenField.dom.value = this.value;
8947         }
8948     },
8949     
8950     clearItem : function()
8951     {
8952         if(!this.multiple){
8953             return;
8954         }
8955         
8956         this.item = [];
8957         
8958         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8959            c.remove();
8960         });
8961         
8962         this.syncValue();
8963     }
8964     
8965     
8966
8967     /** 
8968     * @cfg {Boolean} grow 
8969     * @hide 
8970     */
8971     /** 
8972     * @cfg {Number} growMin 
8973     * @hide 
8974     */
8975     /** 
8976     * @cfg {Number} growMax 
8977     * @hide 
8978     */
8979     /**
8980      * @hide
8981      * @method autoSize
8982      */
8983 });
8984 /*
8985  * Based on:
8986  * Ext JS Library 1.1.1
8987  * Copyright(c) 2006-2007, Ext JS, LLC.
8988  *
8989  * Originally Released Under LGPL - original licence link has changed is not relivant.
8990  *
8991  * Fork - LGPL
8992  * <script type="text/javascript">
8993  */
8994
8995 /**
8996  * @class Roo.View
8997  * @extends Roo.util.Observable
8998  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8999  * This class also supports single and multi selection modes. <br>
9000  * Create a data model bound view:
9001  <pre><code>
9002  var store = new Roo.data.Store(...);
9003
9004  var view = new Roo.View({
9005     el : "my-element",
9006     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9007  
9008     singleSelect: true,
9009     selectedClass: "ydataview-selected",
9010     store: store
9011  });
9012
9013  // listen for node click?
9014  view.on("click", function(vw, index, node, e){
9015  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9016  });
9017
9018  // load XML data
9019  dataModel.load("foobar.xml");
9020  </code></pre>
9021  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9022  * <br><br>
9023  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9024  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9025  * 
9026  * Note: old style constructor is still suported (container, template, config)
9027  * 
9028  * @constructor
9029  * Create a new View
9030  * @param {Object} config The config object
9031  * 
9032  */
9033 Roo.View = function(config, depreciated_tpl, depreciated_config){
9034     
9035     if (typeof(depreciated_tpl) == 'undefined') {
9036         // new way.. - universal constructor.
9037         Roo.apply(this, config);
9038         this.el  = Roo.get(this.el);
9039     } else {
9040         // old format..
9041         this.el  = Roo.get(config);
9042         this.tpl = depreciated_tpl;
9043         Roo.apply(this, depreciated_config);
9044     }
9045     this.wrapEl  = this.el.wrap().wrap();
9046     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9047     
9048     
9049     if(typeof(this.tpl) == "string"){
9050         this.tpl = new Roo.Template(this.tpl);
9051     } else {
9052         // support xtype ctors..
9053         this.tpl = new Roo.factory(this.tpl, Roo);
9054     }
9055     
9056     
9057     this.tpl.compile();
9058    
9059   
9060     
9061      
9062     /** @private */
9063     this.addEvents({
9064         /**
9065          * @event beforeclick
9066          * Fires before a click is processed. Returns false to cancel the default action.
9067          * @param {Roo.View} this
9068          * @param {Number} index The index of the target node
9069          * @param {HTMLElement} node The target node
9070          * @param {Roo.EventObject} e The raw event object
9071          */
9072             "beforeclick" : true,
9073         /**
9074          * @event click
9075          * Fires when a template node is clicked.
9076          * @param {Roo.View} this
9077          * @param {Number} index The index of the target node
9078          * @param {HTMLElement} node The target node
9079          * @param {Roo.EventObject} e The raw event object
9080          */
9081             "click" : true,
9082         /**
9083          * @event dblclick
9084          * Fires when a template node is double clicked.
9085          * @param {Roo.View} this
9086          * @param {Number} index The index of the target node
9087          * @param {HTMLElement} node The target node
9088          * @param {Roo.EventObject} e The raw event object
9089          */
9090             "dblclick" : true,
9091         /**
9092          * @event contextmenu
9093          * Fires when a template node is right clicked.
9094          * @param {Roo.View} this
9095          * @param {Number} index The index of the target node
9096          * @param {HTMLElement} node The target node
9097          * @param {Roo.EventObject} e The raw event object
9098          */
9099             "contextmenu" : true,
9100         /**
9101          * @event selectionchange
9102          * Fires when the selected nodes change.
9103          * @param {Roo.View} this
9104          * @param {Array} selections Array of the selected nodes
9105          */
9106             "selectionchange" : true,
9107     
9108         /**
9109          * @event beforeselect
9110          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9111          * @param {Roo.View} this
9112          * @param {HTMLElement} node The node to be selected
9113          * @param {Array} selections Array of currently selected nodes
9114          */
9115             "beforeselect" : true,
9116         /**
9117          * @event preparedata
9118          * Fires on every row to render, to allow you to change the data.
9119          * @param {Roo.View} this
9120          * @param {Object} data to be rendered (change this)
9121          */
9122           "preparedata" : true
9123           
9124           
9125         });
9126
9127
9128
9129     this.el.on({
9130         "click": this.onClick,
9131         "dblclick": this.onDblClick,
9132         "contextmenu": this.onContextMenu,
9133         scope:this
9134     });
9135
9136     this.selections = [];
9137     this.nodes = [];
9138     this.cmp = new Roo.CompositeElementLite([]);
9139     if(this.store){
9140         this.store = Roo.factory(this.store, Roo.data);
9141         this.setStore(this.store, true);
9142     }
9143     
9144     if ( this.footer && this.footer.xtype) {
9145            
9146          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9147         
9148         this.footer.dataSource = this.store
9149         this.footer.container = fctr;
9150         this.footer = Roo.factory(this.footer, Roo);
9151         fctr.insertFirst(this.el);
9152         
9153         // this is a bit insane - as the paging toolbar seems to detach the el..
9154 //        dom.parentNode.parentNode.parentNode
9155          // they get detached?
9156     }
9157     
9158     
9159     Roo.View.superclass.constructor.call(this);
9160     
9161     
9162 };
9163
9164 Roo.extend(Roo.View, Roo.util.Observable, {
9165     
9166      /**
9167      * @cfg {Roo.data.Store} store Data store to load data from.
9168      */
9169     store : false,
9170     
9171     /**
9172      * @cfg {String|Roo.Element} el The container element.
9173      */
9174     el : '',
9175     
9176     /**
9177      * @cfg {String|Roo.Template} tpl The template used by this View 
9178      */
9179     tpl : false,
9180     /**
9181      * @cfg {String} dataName the named area of the template to use as the data area
9182      *                          Works with domtemplates roo-name="name"
9183      */
9184     dataName: false,
9185     /**
9186      * @cfg {String} selectedClass The css class to add to selected nodes
9187      */
9188     selectedClass : "x-view-selected",
9189      /**
9190      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9191      */
9192     emptyText : "",
9193     
9194     /**
9195      * @cfg {String} text to display on mask (default Loading)
9196      */
9197     mask : false,
9198     /**
9199      * @cfg {Boolean} multiSelect Allow multiple selection
9200      */
9201     multiSelect : false,
9202     /**
9203      * @cfg {Boolean} singleSelect Allow single selection
9204      */
9205     singleSelect:  false,
9206     
9207     /**
9208      * @cfg {Boolean} toggleSelect - selecting 
9209      */
9210     toggleSelect : false,
9211     
9212     /**
9213      * Returns the element this view is bound to.
9214      * @return {Roo.Element}
9215      */
9216     getEl : function(){
9217         return this.wrapEl;
9218     },
9219     
9220     
9221
9222     /**
9223      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9224      */
9225     refresh : function(){
9226         Roo.log('refresh');
9227         var t = this.tpl;
9228         
9229         // if we are using something like 'domtemplate', then
9230         // the what gets used is:
9231         // t.applySubtemplate(NAME, data, wrapping data..)
9232         // the outer template then get' applied with
9233         //     the store 'extra data'
9234         // and the body get's added to the
9235         //      roo-name="data" node?
9236         //      <span class='roo-tpl-{name}'></span> ?????
9237         
9238         
9239         
9240         this.clearSelections();
9241         this.el.update("");
9242         var html = [];
9243         var records = this.store.getRange();
9244         if(records.length < 1) {
9245             
9246             // is this valid??  = should it render a template??
9247             
9248             this.el.update(this.emptyText);
9249             return;
9250         }
9251         var el = this.el;
9252         if (this.dataName) {
9253             this.el.update(t.apply(this.store.meta)); //????
9254             el = this.el.child('.roo-tpl-' + this.dataName);
9255         }
9256         
9257         for(var i = 0, len = records.length; i < len; i++){
9258             var data = this.prepareData(records[i].data, i, records[i]);
9259             this.fireEvent("preparedata", this, data, i, records[i]);
9260             html[html.length] = Roo.util.Format.trim(
9261                 this.dataName ?
9262                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9263                     t.apply(data)
9264             );
9265         }
9266         
9267         
9268         
9269         el.update(html.join(""));
9270         this.nodes = el.dom.childNodes;
9271         this.updateIndexes(0);
9272     },
9273     
9274
9275     /**
9276      * Function to override to reformat the data that is sent to
9277      * the template for each node.
9278      * DEPRICATED - use the preparedata event handler.
9279      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9280      * a JSON object for an UpdateManager bound view).
9281      */
9282     prepareData : function(data, index, record)
9283     {
9284         this.fireEvent("preparedata", this, data, index, record);
9285         return data;
9286     },
9287
9288     onUpdate : function(ds, record){
9289          Roo.log('on update');   
9290         this.clearSelections();
9291         var index = this.store.indexOf(record);
9292         var n = this.nodes[index];
9293         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9294         n.parentNode.removeChild(n);
9295         this.updateIndexes(index, index);
9296     },
9297
9298     
9299     
9300 // --------- FIXME     
9301     onAdd : function(ds, records, index)
9302     {
9303         Roo.log(['on Add', ds, records, index] );        
9304         this.clearSelections();
9305         if(this.nodes.length == 0){
9306             this.refresh();
9307             return;
9308         }
9309         var n = this.nodes[index];
9310         for(var i = 0, len = records.length; i < len; i++){
9311             var d = this.prepareData(records[i].data, i, records[i]);
9312             if(n){
9313                 this.tpl.insertBefore(n, d);
9314             }else{
9315                 
9316                 this.tpl.append(this.el, d);
9317             }
9318         }
9319         this.updateIndexes(index);
9320     },
9321
9322     onRemove : function(ds, record, index){
9323         Roo.log('onRemove');
9324         this.clearSelections();
9325         var el = this.dataName  ?
9326             this.el.child('.roo-tpl-' + this.dataName) :
9327             this.el; 
9328         
9329         el.dom.removeChild(this.nodes[index]);
9330         this.updateIndexes(index);
9331     },
9332
9333     /**
9334      * Refresh an individual node.
9335      * @param {Number} index
9336      */
9337     refreshNode : function(index){
9338         this.onUpdate(this.store, this.store.getAt(index));
9339     },
9340
9341     updateIndexes : function(startIndex, endIndex){
9342         var ns = this.nodes;
9343         startIndex = startIndex || 0;
9344         endIndex = endIndex || ns.length - 1;
9345         for(var i = startIndex; i <= endIndex; i++){
9346             ns[i].nodeIndex = i;
9347         }
9348     },
9349
9350     /**
9351      * Changes the data store this view uses and refresh the view.
9352      * @param {Store} store
9353      */
9354     setStore : function(store, initial){
9355         if(!initial && this.store){
9356             this.store.un("datachanged", this.refresh);
9357             this.store.un("add", this.onAdd);
9358             this.store.un("remove", this.onRemove);
9359             this.store.un("update", this.onUpdate);
9360             this.store.un("clear", this.refresh);
9361             this.store.un("beforeload", this.onBeforeLoad);
9362             this.store.un("load", this.onLoad);
9363             this.store.un("loadexception", this.onLoad);
9364         }
9365         if(store){
9366           
9367             store.on("datachanged", this.refresh, this);
9368             store.on("add", this.onAdd, this);
9369             store.on("remove", this.onRemove, this);
9370             store.on("update", this.onUpdate, this);
9371             store.on("clear", this.refresh, this);
9372             store.on("beforeload", this.onBeforeLoad, this);
9373             store.on("load", this.onLoad, this);
9374             store.on("loadexception", this.onLoad, this);
9375         }
9376         
9377         if(store){
9378             this.refresh();
9379         }
9380     },
9381     /**
9382      * onbeforeLoad - masks the loading area.
9383      *
9384      */
9385     onBeforeLoad : function(store,opts)
9386     {
9387          Roo.log('onBeforeLoad');   
9388         if (!opts.add) {
9389             this.el.update("");
9390         }
9391         this.el.mask(this.mask ? this.mask : "Loading" ); 
9392     },
9393     onLoad : function ()
9394     {
9395         this.el.unmask();
9396     },
9397     
9398
9399     /**
9400      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9401      * @param {HTMLElement} node
9402      * @return {HTMLElement} The template node
9403      */
9404     findItemFromChild : function(node){
9405         var el = this.dataName  ?
9406             this.el.child('.roo-tpl-' + this.dataName,true) :
9407             this.el.dom; 
9408         
9409         if(!node || node.parentNode == el){
9410                     return node;
9411             }
9412             var p = node.parentNode;
9413             while(p && p != el){
9414             if(p.parentNode == el){
9415                 return p;
9416             }
9417             p = p.parentNode;
9418         }
9419             return null;
9420     },
9421
9422     /** @ignore */
9423     onClick : function(e){
9424         var item = this.findItemFromChild(e.getTarget());
9425         if(item){
9426             var index = this.indexOf(item);
9427             if(this.onItemClick(item, index, e) !== false){
9428                 this.fireEvent("click", this, index, item, e);
9429             }
9430         }else{
9431             this.clearSelections();
9432         }
9433     },
9434
9435     /** @ignore */
9436     onContextMenu : function(e){
9437         var item = this.findItemFromChild(e.getTarget());
9438         if(item){
9439             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9440         }
9441     },
9442
9443     /** @ignore */
9444     onDblClick : function(e){
9445         var item = this.findItemFromChild(e.getTarget());
9446         if(item){
9447             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9448         }
9449     },
9450
9451     onItemClick : function(item, index, e)
9452     {
9453         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9454             return false;
9455         }
9456         if (this.toggleSelect) {
9457             var m = this.isSelected(item) ? 'unselect' : 'select';
9458             Roo.log(m);
9459             var _t = this;
9460             _t[m](item, true, false);
9461             return true;
9462         }
9463         if(this.multiSelect || this.singleSelect){
9464             if(this.multiSelect && e.shiftKey && this.lastSelection){
9465                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9466             }else{
9467                 this.select(item, this.multiSelect && e.ctrlKey);
9468                 this.lastSelection = item;
9469             }
9470             e.preventDefault();
9471         }
9472         return true;
9473     },
9474
9475     /**
9476      * Get the number of selected nodes.
9477      * @return {Number}
9478      */
9479     getSelectionCount : function(){
9480         return this.selections.length;
9481     },
9482
9483     /**
9484      * Get the currently selected nodes.
9485      * @return {Array} An array of HTMLElements
9486      */
9487     getSelectedNodes : function(){
9488         return this.selections;
9489     },
9490
9491     /**
9492      * Get the indexes of the selected nodes.
9493      * @return {Array}
9494      */
9495     getSelectedIndexes : function(){
9496         var indexes = [], s = this.selections;
9497         for(var i = 0, len = s.length; i < len; i++){
9498             indexes.push(s[i].nodeIndex);
9499         }
9500         return indexes;
9501     },
9502
9503     /**
9504      * Clear all selections
9505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9506      */
9507     clearSelections : function(suppressEvent){
9508         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9509             this.cmp.elements = this.selections;
9510             this.cmp.removeClass(this.selectedClass);
9511             this.selections = [];
9512             if(!suppressEvent){
9513                 this.fireEvent("selectionchange", this, this.selections);
9514             }
9515         }
9516     },
9517
9518     /**
9519      * Returns true if the passed node is selected
9520      * @param {HTMLElement/Number} node The node or node index
9521      * @return {Boolean}
9522      */
9523     isSelected : function(node){
9524         var s = this.selections;
9525         if(s.length < 1){
9526             return false;
9527         }
9528         node = this.getNode(node);
9529         return s.indexOf(node) !== -1;
9530     },
9531
9532     /**
9533      * Selects nodes.
9534      * @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
9535      * @param {Boolean} keepExisting (optional) true to keep existing selections
9536      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9537      */
9538     select : function(nodeInfo, keepExisting, suppressEvent){
9539         if(nodeInfo instanceof Array){
9540             if(!keepExisting){
9541                 this.clearSelections(true);
9542             }
9543             for(var i = 0, len = nodeInfo.length; i < len; i++){
9544                 this.select(nodeInfo[i], true, true);
9545             }
9546             return;
9547         } 
9548         var node = this.getNode(nodeInfo);
9549         if(!node || this.isSelected(node)){
9550             return; // already selected.
9551         }
9552         if(!keepExisting){
9553             this.clearSelections(true);
9554         }
9555         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9556             Roo.fly(node).addClass(this.selectedClass);
9557             this.selections.push(node);
9558             if(!suppressEvent){
9559                 this.fireEvent("selectionchange", this, this.selections);
9560             }
9561         }
9562         
9563         
9564     },
9565       /**
9566      * Unselects nodes.
9567      * @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
9568      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9569      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9570      */
9571     unselect : function(nodeInfo, keepExisting, suppressEvent)
9572     {
9573         if(nodeInfo instanceof Array){
9574             Roo.each(this.selections, function(s) {
9575                 this.unselect(s, nodeInfo);
9576             }, this);
9577             return;
9578         }
9579         var node = this.getNode(nodeInfo);
9580         if(!node || !this.isSelected(node)){
9581             Roo.log("not selected");
9582             return; // not selected.
9583         }
9584         // fireevent???
9585         var ns = [];
9586         Roo.each(this.selections, function(s) {
9587             if (s == node ) {
9588                 Roo.fly(node).removeClass(this.selectedClass);
9589
9590                 return;
9591             }
9592             ns.push(s);
9593         },this);
9594         
9595         this.selections= ns;
9596         this.fireEvent("selectionchange", this, this.selections);
9597     },
9598
9599     /**
9600      * Gets a template node.
9601      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9602      * @return {HTMLElement} The node or null if it wasn't found
9603      */
9604     getNode : function(nodeInfo){
9605         if(typeof nodeInfo == "string"){
9606             return document.getElementById(nodeInfo);
9607         }else if(typeof nodeInfo == "number"){
9608             return this.nodes[nodeInfo];
9609         }
9610         return nodeInfo;
9611     },
9612
9613     /**
9614      * Gets a range template nodes.
9615      * @param {Number} startIndex
9616      * @param {Number} endIndex
9617      * @return {Array} An array of nodes
9618      */
9619     getNodes : function(start, end){
9620         var ns = this.nodes;
9621         start = start || 0;
9622         end = typeof end == "undefined" ? ns.length - 1 : end;
9623         var nodes = [];
9624         if(start <= end){
9625             for(var i = start; i <= end; i++){
9626                 nodes.push(ns[i]);
9627             }
9628         } else{
9629             for(var i = start; i >= end; i--){
9630                 nodes.push(ns[i]);
9631             }
9632         }
9633         return nodes;
9634     },
9635
9636     /**
9637      * Finds the index of the passed node
9638      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9639      * @return {Number} The index of the node or -1
9640      */
9641     indexOf : function(node){
9642         node = this.getNode(node);
9643         if(typeof node.nodeIndex == "number"){
9644             return node.nodeIndex;
9645         }
9646         var ns = this.nodes;
9647         for(var i = 0, len = ns.length; i < len; i++){
9648             if(ns[i] == node){
9649                 return i;
9650             }
9651         }
9652         return -1;
9653     }
9654 });
9655 /*
9656  * - LGPL
9657  *
9658  * based on jquery fullcalendar
9659  * 
9660  */
9661
9662 Roo.bootstrap = Roo.bootstrap || {};
9663 /**
9664  * @class Roo.bootstrap.Calendar
9665  * @extends Roo.bootstrap.Component
9666  * Bootstrap Calendar class
9667  * @cfg {Boolean} loadMask (true|false) default false
9668     
9669  * @constructor
9670  * Create a new Container
9671  * @param {Object} config The config object
9672  */
9673
9674
9675
9676 Roo.bootstrap.Calendar = function(config){
9677     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9678      this.addEvents({
9679         /**
9680              * @event select
9681              * Fires when a date is selected
9682              * @param {DatePicker} this
9683              * @param {Date} date The selected date
9684              */
9685         'select': true,
9686         /**
9687              * @event monthchange
9688              * Fires when the displayed month changes 
9689              * @param {DatePicker} this
9690              * @param {Date} date The selected month
9691              */
9692         'monthchange': true,
9693         /**
9694              * @event evententer
9695              * Fires when mouse over an event
9696              * @param {Calendar} this
9697              * @param {event} Event
9698              */
9699         'evententer': true,
9700         /**
9701              * @event eventleave
9702              * Fires when the mouse leaves an
9703              * @param {Calendar} this
9704              * @param {event}
9705              */
9706         'eventleave': true,
9707         /**
9708              * @event eventclick
9709              * Fires when the mouse click an
9710              * @param {Calendar} this
9711              * @param {event}
9712              */
9713         'eventclick': true
9714         
9715     });
9716
9717 };
9718
9719 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9720     
9721      /**
9722      * @cfg {Number} startDay
9723      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9724      */
9725     startDay : 0,
9726     
9727     loadMask : false,
9728       
9729     getAutoCreate : function(){
9730         
9731         
9732         var fc_button = function(name, corner, style, content ) {
9733             return Roo.apply({},{
9734                 tag : 'span',
9735                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9736                          (corner.length ?
9737                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9738                             ''
9739                         ),
9740                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9741                 unselectable: 'on'
9742             });
9743         };
9744         
9745         var header = {
9746             tag : 'table',
9747             cls : 'fc-header',
9748             style : 'width:100%',
9749             cn : [
9750                 {
9751                     tag: 'tr',
9752                     cn : [
9753                         {
9754                             tag : 'td',
9755                             cls : 'fc-header-left',
9756                             cn : [
9757                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9758                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9759                                 { tag: 'span', cls: 'fc-header-space' },
9760                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9761                                 
9762                                 
9763                             ]
9764                         },
9765                         
9766                         {
9767                             tag : 'td',
9768                             cls : 'fc-header-center',
9769                             cn : [
9770                                 {
9771                                     tag: 'span',
9772                                     cls: 'fc-header-title',
9773                                     cn : {
9774                                         tag: 'H2',
9775                                         html : 'month / year'
9776                                     }
9777                                 }
9778                                 
9779                             ]
9780                         },
9781                         {
9782                             tag : 'td',
9783                             cls : 'fc-header-right',
9784                             cn : [
9785                           /*      fc_button('month', 'left', '', 'month' ),
9786                                 fc_button('week', '', '', 'week' ),
9787                                 fc_button('day', 'right', '', 'day' )
9788                             */    
9789                                 
9790                             ]
9791                         }
9792                         
9793                     ]
9794                 }
9795             ]
9796         };
9797         
9798        
9799         var cal_heads = function() {
9800             var ret = [];
9801             // fixme - handle this.
9802             
9803             for (var i =0; i < Date.dayNames.length; i++) {
9804                 var d = Date.dayNames[i];
9805                 ret.push({
9806                     tag: 'th',
9807                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9808                     html : d.substring(0,3)
9809                 });
9810                 
9811             }
9812             ret[0].cls += ' fc-first';
9813             ret[6].cls += ' fc-last';
9814             return ret;
9815         };
9816         var cal_cell = function(n) {
9817             return  {
9818                 tag: 'td',
9819                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9820                 cn : [
9821                     {
9822                         cn : [
9823                             {
9824                                 cls: 'fc-day-number',
9825                                 html: 'D'
9826                             },
9827                             {
9828                                 cls: 'fc-day-content',
9829                              
9830                                 cn : [
9831                                      {
9832                                         style: 'position: relative;' // height: 17px;
9833                                     }
9834                                 ]
9835                             }
9836                             
9837                             
9838                         ]
9839                     }
9840                 ]
9841                 
9842             }
9843         };
9844         var cal_rows = function() {
9845             
9846             var ret = []
9847             for (var r = 0; r < 6; r++) {
9848                 var row= {
9849                     tag : 'tr',
9850                     cls : 'fc-week',
9851                     cn : []
9852                 };
9853                 
9854                 for (var i =0; i < Date.dayNames.length; i++) {
9855                     var d = Date.dayNames[i];
9856                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9857
9858                 }
9859                 row.cn[0].cls+=' fc-first';
9860                 row.cn[0].cn[0].style = 'min-height:90px';
9861                 row.cn[6].cls+=' fc-last';
9862                 ret.push(row);
9863                 
9864             }
9865             ret[0].cls += ' fc-first';
9866             ret[4].cls += ' fc-prev-last';
9867             ret[5].cls += ' fc-last';
9868             return ret;
9869             
9870         };
9871         
9872         var cal_table = {
9873             tag: 'table',
9874             cls: 'fc-border-separate',
9875             style : 'width:100%',
9876             cellspacing  : 0,
9877             cn : [
9878                 { 
9879                     tag: 'thead',
9880                     cn : [
9881                         { 
9882                             tag: 'tr',
9883                             cls : 'fc-first fc-last',
9884                             cn : cal_heads()
9885                         }
9886                     ]
9887                 },
9888                 { 
9889                     tag: 'tbody',
9890                     cn : cal_rows()
9891                 }
9892                   
9893             ]
9894         };
9895          
9896          var cfg = {
9897             cls : 'fc fc-ltr',
9898             cn : [
9899                 header,
9900                 {
9901                     cls : 'fc-content',
9902                     style : "position: relative;",
9903                     cn : [
9904                         {
9905                             cls : 'fc-view fc-view-month fc-grid',
9906                             style : 'position: relative',
9907                             unselectable : 'on',
9908                             cn : [
9909                                 {
9910                                     cls : 'fc-event-container',
9911                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9912                                 },
9913                                 cal_table
9914                             ]
9915                         }
9916                     ]
9917     
9918                 }
9919            ] 
9920             
9921         };
9922         
9923          
9924         
9925         return cfg;
9926     },
9927     
9928     
9929     initEvents : function()
9930     {
9931         if(!this.store){
9932             throw "can not find store for calendar";
9933         }
9934         
9935         var mark = {
9936             tag: "div",
9937             cls:"x-dlg-mask",
9938             style: "text-align:center",
9939             cn: [
9940                 {
9941                     tag: "div",
9942                     style: "background-color:white;width:50%;margin:250 auto",
9943                     cn: [
9944                         {
9945                             tag: "img",
9946                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9947                         },
9948                         {
9949                             tag: "span",
9950                             html: "Loading"
9951                         }
9952                         
9953                     ]
9954                 }
9955             ]
9956         }
9957         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9958         
9959         var size = this.el.select('.fc-content', true).first().getSize();
9960         this.maskEl.setSize(size.width, size.height);
9961         this.maskEl.enableDisplayMode("block");
9962         if(!this.loadMask){
9963             this.maskEl.hide();
9964         }
9965         
9966         this.store = Roo.factory(this.store, Roo.data);
9967         this.store.on('load', this.onLoad, this);
9968         this.store.on('beforeload', this.onBeforeLoad, this);
9969         
9970         this.resize();
9971         
9972         this.cells = this.el.select('.fc-day',true);
9973         //Roo.log(this.cells);
9974         this.textNodes = this.el.query('.fc-day-number');
9975         this.cells.addClassOnOver('fc-state-hover');
9976         
9977         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9978         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9979         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9980         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9981         
9982         this.on('monthchange', this.onMonthChange, this);
9983         
9984         this.update(new Date().clearTime());
9985     },
9986     
9987     resize : function() {
9988         var sz  = this.el.getSize();
9989         
9990         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9991         this.el.select('.fc-day-content div',true).setHeight(34);
9992     },
9993     
9994     
9995     // private
9996     showPrevMonth : function(e){
9997         this.update(this.activeDate.add("mo", -1));
9998     },
9999     showToday : function(e){
10000         this.update(new Date().clearTime());
10001     },
10002     // private
10003     showNextMonth : function(e){
10004         this.update(this.activeDate.add("mo", 1));
10005     },
10006
10007     // private
10008     showPrevYear : function(){
10009         this.update(this.activeDate.add("y", -1));
10010     },
10011
10012     // private
10013     showNextYear : function(){
10014         this.update(this.activeDate.add("y", 1));
10015     },
10016
10017     
10018    // private
10019     update : function(date)
10020     {
10021         var vd = this.activeDate;
10022         this.activeDate = date;
10023 //        if(vd && this.el){
10024 //            var t = date.getTime();
10025 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10026 //                Roo.log('using add remove');
10027 //                
10028 //                this.fireEvent('monthchange', this, date);
10029 //                
10030 //                this.cells.removeClass("fc-state-highlight");
10031 //                this.cells.each(function(c){
10032 //                   if(c.dateValue == t){
10033 //                       c.addClass("fc-state-highlight");
10034 //                       setTimeout(function(){
10035 //                            try{c.dom.firstChild.focus();}catch(e){}
10036 //                       }, 50);
10037 //                       return false;
10038 //                   }
10039 //                   return true;
10040 //                });
10041 //                return;
10042 //            }
10043 //        }
10044         
10045         var days = date.getDaysInMonth();
10046         
10047         var firstOfMonth = date.getFirstDateOfMonth();
10048         var startingPos = firstOfMonth.getDay()-this.startDay;
10049         
10050         if(startingPos < this.startDay){
10051             startingPos += 7;
10052         }
10053         
10054         var pm = date.add(Date.MONTH, -1);
10055         var prevStart = pm.getDaysInMonth()-startingPos;
10056 //        
10057         this.cells = this.el.select('.fc-day',true);
10058         this.textNodes = this.el.query('.fc-day-number');
10059         this.cells.addClassOnOver('fc-state-hover');
10060         
10061         var cells = this.cells.elements;
10062         var textEls = this.textNodes;
10063         
10064         Roo.each(cells, function(cell){
10065             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10066         });
10067         
10068         days += startingPos;
10069
10070         // convert everything to numbers so it's fast
10071         var day = 86400000;
10072         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10073         //Roo.log(d);
10074         //Roo.log(pm);
10075         //Roo.log(prevStart);
10076         
10077         var today = new Date().clearTime().getTime();
10078         var sel = date.clearTime().getTime();
10079         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10080         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10081         var ddMatch = this.disabledDatesRE;
10082         var ddText = this.disabledDatesText;
10083         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10084         var ddaysText = this.disabledDaysText;
10085         var format = this.format;
10086         
10087         var setCellClass = function(cal, cell){
10088             
10089             //Roo.log('set Cell Class');
10090             cell.title = "";
10091             var t = d.getTime();
10092             
10093             //Roo.log(d);
10094             
10095             cell.dateValue = t;
10096             if(t == today){
10097                 cell.className += " fc-today";
10098                 cell.className += " fc-state-highlight";
10099                 cell.title = cal.todayText;
10100             }
10101             if(t == sel){
10102                 // disable highlight in other month..
10103                 //cell.className += " fc-state-highlight";
10104                 
10105             }
10106             // disabling
10107             if(t < min) {
10108                 cell.className = " fc-state-disabled";
10109                 cell.title = cal.minText;
10110                 return;
10111             }
10112             if(t > max) {
10113                 cell.className = " fc-state-disabled";
10114                 cell.title = cal.maxText;
10115                 return;
10116             }
10117             if(ddays){
10118                 if(ddays.indexOf(d.getDay()) != -1){
10119                     cell.title = ddaysText;
10120                     cell.className = " fc-state-disabled";
10121                 }
10122             }
10123             if(ddMatch && format){
10124                 var fvalue = d.dateFormat(format);
10125                 if(ddMatch.test(fvalue)){
10126                     cell.title = ddText.replace("%0", fvalue);
10127                     cell.className = " fc-state-disabled";
10128                 }
10129             }
10130             
10131             if (!cell.initialClassName) {
10132                 cell.initialClassName = cell.dom.className;
10133             }
10134             
10135             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10136         };
10137
10138         var i = 0;
10139         
10140         for(; i < startingPos; i++) {
10141             textEls[i].innerHTML = (++prevStart);
10142             d.setDate(d.getDate()+1);
10143             
10144             cells[i].className = "fc-past fc-other-month";
10145             setCellClass(this, cells[i]);
10146         }
10147         
10148         var intDay = 0;
10149         
10150         for(; i < days; i++){
10151             intDay = i - startingPos + 1;
10152             textEls[i].innerHTML = (intDay);
10153             d.setDate(d.getDate()+1);
10154             
10155             cells[i].className = ''; // "x-date-active";
10156             setCellClass(this, cells[i]);
10157         }
10158         var extraDays = 0;
10159         
10160         for(; i < 42; i++) {
10161             textEls[i].innerHTML = (++extraDays);
10162             d.setDate(d.getDate()+1);
10163             
10164             cells[i].className = "fc-future fc-other-month";
10165             setCellClass(this, cells[i]);
10166         }
10167         
10168         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10169         
10170         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10171         
10172         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10173         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10174         
10175         if(totalRows != 6){
10176             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10177             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10178         }
10179         
10180         this.fireEvent('monthchange', this, date);
10181         
10182         
10183         /*
10184         if(!this.internalRender){
10185             var main = this.el.dom.firstChild;
10186             var w = main.offsetWidth;
10187             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10188             Roo.fly(main).setWidth(w);
10189             this.internalRender = true;
10190             // opera does not respect the auto grow header center column
10191             // then, after it gets a width opera refuses to recalculate
10192             // without a second pass
10193             if(Roo.isOpera && !this.secondPass){
10194                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10195                 this.secondPass = true;
10196                 this.update.defer(10, this, [date]);
10197             }
10198         }
10199         */
10200         
10201     },
10202     
10203     findCell : function(dt) {
10204         dt = dt.clearTime().getTime();
10205         var ret = false;
10206         this.cells.each(function(c){
10207             //Roo.log("check " +c.dateValue + '?=' + dt);
10208             if(c.dateValue == dt){
10209                 ret = c;
10210                 return false;
10211             }
10212             return true;
10213         });
10214         
10215         return ret;
10216     },
10217     
10218     findCells : function(ev) {
10219         var s = ev.start.clone().clearTime().getTime();
10220        // Roo.log(s);
10221         var e= ev.end.clone().clearTime().getTime();
10222        // Roo.log(e);
10223         var ret = [];
10224         this.cells.each(function(c){
10225              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10226             
10227             if(c.dateValue > e){
10228                 return ;
10229             }
10230             if(c.dateValue < s){
10231                 return ;
10232             }
10233             ret.push(c);
10234         });
10235         
10236         return ret;    
10237     },
10238     
10239     findBestRow: function(cells)
10240     {
10241         var ret = 0;
10242         
10243         for (var i =0 ; i < cells.length;i++) {
10244             ret  = Math.max(cells[i].rows || 0,ret);
10245         }
10246         return ret;
10247         
10248     },
10249     
10250     
10251     addItem : function(ev)
10252     {
10253         // look for vertical location slot in
10254         var cells = this.findCells(ev);
10255         
10256         ev.row = this.findBestRow(cells);
10257         
10258         // work out the location.
10259         
10260         var crow = false;
10261         var rows = [];
10262         for(var i =0; i < cells.length; i++) {
10263             if (!crow) {
10264                 crow = {
10265                     start : cells[i],
10266                     end :  cells[i]
10267                 };
10268                 continue;
10269             }
10270             if (crow.start.getY() == cells[i].getY()) {
10271                 // on same row.
10272                 crow.end = cells[i];
10273                 continue;
10274             }
10275             // different row.
10276             rows.push(crow);
10277             crow = {
10278                 start: cells[i],
10279                 end : cells[i]
10280             };
10281             
10282         }
10283         
10284         rows.push(crow);
10285         ev.els = [];
10286         ev.rows = rows;
10287         ev.cells = cells;
10288         for (var i = 0; i < cells.length;i++) {
10289             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10290             
10291         }
10292         
10293         this.calevents.push(ev);
10294     },
10295     
10296     clearEvents: function() {
10297         
10298         if(!this.calevents){
10299             return;
10300         }
10301         
10302         Roo.each(this.cells.elements, function(c){
10303             c.rows = 0;
10304         });
10305         
10306         Roo.each(this.calevents, function(e) {
10307             Roo.each(e.els, function(el) {
10308                 el.un('mouseenter' ,this.onEventEnter, this);
10309                 el.un('mouseleave' ,this.onEventLeave, this);
10310                 el.remove();
10311             },this);
10312         },this);
10313         
10314     },
10315     
10316     renderEvents: function()
10317     {   
10318         // first make sure there is enough space..
10319         
10320         this.cells.each(function(c) {
10321         
10322             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10323         });
10324         
10325         for (var e = 0; e < this.calevents.length; e++) {
10326             var ev = this.calevents[e];
10327             var cells = ev.cells;
10328             var rows = ev.rows;
10329             
10330             for(var i =0; i < rows.length; i++) {
10331                 
10332                  
10333                 // how many rows should it span..
10334                 
10335                 var  cfg = {
10336                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10337                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10338                     
10339                     unselectable : "on",
10340                     cn : [
10341                         {
10342                             cls: 'fc-event-inner',
10343                             cn : [
10344 //                                {
10345 //                                  tag:'span',
10346 //                                  cls: 'fc-event-time',
10347 //                                  html : cells.length > 1 ? '' : ev.time
10348 //                                },
10349                                 {
10350                                   tag:'span',
10351                                   cls: 'fc-event-title',
10352                                   html : String.format('{0}', ev.title)
10353                                 }
10354                                 
10355                                 
10356                             ]
10357                         },
10358                         {
10359                             cls: 'ui-resizable-handle ui-resizable-e',
10360                             html : '&nbsp;&nbsp;&nbsp'
10361                         }
10362                         
10363                     ]
10364                 };
10365                 if (i == 0) {
10366                     cfg.cls += ' fc-event-start';
10367                 }
10368                 if ((i+1) == rows.length) {
10369                     cfg.cls += ' fc-event-end';
10370                 }
10371                 
10372                 var ctr = this.el.select('.fc-event-container',true).first();
10373                 var cg = ctr.createChild(cfg);
10374                 
10375                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10376                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10377                 cg.on('click', this.onEventClick, this, ev);
10378                 
10379                 ev.els.push(cg);
10380                 
10381                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10382                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10383                 //Roo.log(cg);
10384                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10385                 cg.setWidth(ebox.right - sbox.x -2);
10386             }
10387             
10388             
10389         }
10390         
10391     },
10392     
10393     onEventEnter: function (e, el,event,d) {
10394         this.fireEvent('evententer', this, el, event);
10395     },
10396     
10397     onEventLeave: function (e, el,event,d) {
10398         this.fireEvent('eventleave', this, el, event);
10399     },
10400     
10401     onEventClick: function (e, el,event,d) {
10402         this.fireEvent('eventclick', this, el, event);
10403     },
10404     
10405     onMonthChange: function () {
10406         this.store.load();
10407     },
10408     
10409     onLoad: function () 
10410     {   
10411         this.calevents = [];
10412         var cal = this;
10413         
10414         if(this.store.getCount() > 0){
10415             this.store.data.each(function(d){
10416                cal.addItem({
10417                     id : d.data.id,
10418                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10419                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10420                     time : d.data.start_time,
10421                     title : d.data.title,
10422                     description : d.data.description,
10423                     venue : d.data.venue
10424                 });
10425             });
10426         }
10427         
10428         this.renderEvents();
10429         
10430         if(this.loadMask){
10431             this.maskEl.hide();
10432         }
10433     },
10434     
10435     onBeforeLoad: function()
10436     {
10437         this.clearEvents();
10438         
10439         if(this.loadMask){
10440             this.maskEl.show();
10441         }
10442     }
10443 });
10444
10445  
10446  /*
10447  * - LGPL
10448  *
10449  * element
10450  * 
10451  */
10452
10453 /**
10454  * @class Roo.bootstrap.Popover
10455  * @extends Roo.bootstrap.Component
10456  * Bootstrap Popover class
10457  * @cfg {String} html contents of the popover   (or false to use children..)
10458  * @cfg {String} title of popover (or false to hide)
10459  * @cfg {String} placement how it is placed
10460  * @cfg {String} trigger click || hover (or false to trigger manually)
10461  * @cfg {String} over what (parent or false to trigger manually.)
10462  * 
10463  * @constructor
10464  * Create a new Popover
10465  * @param {Object} config The config object
10466  */
10467
10468 Roo.bootstrap.Popover = function(config){
10469     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10470 };
10471
10472 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10473     
10474     title: 'Fill in a title',
10475     html: false,
10476     
10477     placement : 'right',
10478     trigger : 'hover', // hover
10479     
10480     over: 'parent',
10481     
10482     can_build_overlaid : false,
10483     
10484     getChildContainer : function()
10485     {
10486         return this.el.select('.popover-content',true).first();
10487     },
10488     
10489     getAutoCreate : function(){
10490          Roo.log('make popover?');
10491         var cfg = {
10492            cls : 'popover roo-dynamic',
10493            style: 'display:block',
10494            cn : [
10495                 {
10496                     cls : 'arrow'
10497                 },
10498                 {
10499                     cls : 'popover-inner',
10500                     cn : [
10501                         {
10502                             tag: 'h3',
10503                             cls: 'popover-title',
10504                             html : this.title
10505                         },
10506                         {
10507                             cls : 'popover-content',
10508                             html : this.html
10509                         }
10510                     ]
10511                     
10512                 }
10513            ]
10514         };
10515         
10516         return cfg;
10517     },
10518     setTitle: function(str)
10519     {
10520         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10521     },
10522     setContent: function(str)
10523     {
10524         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10525     },
10526     // as it get's added to the bottom of the page.
10527     onRender : function(ct, position)
10528     {
10529         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10530         if(!this.el){
10531             var cfg = Roo.apply({},  this.getAutoCreate());
10532             cfg.id = Roo.id();
10533             
10534             if (this.cls) {
10535                 cfg.cls += ' ' + this.cls;
10536             }
10537             if (this.style) {
10538                 cfg.style = this.style;
10539             }
10540             Roo.log("adding to ")
10541             this.el = Roo.get(document.body).createChild(cfg, position);
10542             Roo.log(this.el);
10543         }
10544         this.initEvents();
10545     },
10546     
10547     initEvents : function()
10548     {
10549         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10550         this.el.enableDisplayMode('block');
10551         this.el.hide();
10552         if (this.over === false) {
10553             return; 
10554         }
10555         if (this.triggers === false) {
10556             return;
10557         }
10558         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10559         var triggers = this.trigger ? this.trigger.split(' ') : [];
10560         Roo.each(triggers, function(trigger) {
10561         
10562             if (trigger == 'click') {
10563                 on_el.on('click', this.toggle, this);
10564             } else if (trigger != 'manual') {
10565                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10566                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10567       
10568                 on_el.on(eventIn  ,this.enter, this);
10569                 on_el.on(eventOut, this.leave, this);
10570             }
10571         }, this);
10572         
10573     },
10574     
10575     
10576     // private
10577     timeout : null,
10578     hoverState : null,
10579     
10580     toggle : function () {
10581         this.hoverState == 'in' ? this.leave() : this.enter();
10582     },
10583     
10584     enter : function () {
10585        
10586     
10587         clearTimeout(this.timeout);
10588     
10589         this.hoverState = 'in'
10590     
10591         if (!this.delay || !this.delay.show) {
10592             this.show();
10593             return 
10594         }
10595         var _t = this;
10596         this.timeout = setTimeout(function () {
10597             if (_t.hoverState == 'in') {
10598                 _t.show();
10599             }
10600         }, this.delay.show)
10601     },
10602     leave : function() {
10603         clearTimeout(this.timeout);
10604     
10605         this.hoverState = 'out'
10606     
10607         if (!this.delay || !this.delay.hide) {
10608             this.hide();
10609             return 
10610         }
10611         var _t = this;
10612         this.timeout = setTimeout(function () {
10613             if (_t.hoverState == 'out') {
10614                 _t.hide();
10615             }
10616         }, this.delay.hide)
10617     },
10618     
10619     show : function (on_el)
10620     {
10621         if (!on_el) {
10622             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10623         }
10624         // set content.
10625         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10626         if (this.html !== false) {
10627             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10628         }
10629         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10630         if (!this.title.length) {
10631             this.el.select('.popover-title',true).hide();
10632         }
10633         
10634         var placement = typeof this.placement == 'function' ?
10635             this.placement.call(this, this.el, on_el) :
10636             this.placement;
10637             
10638         var autoToken = /\s?auto?\s?/i;
10639         var autoPlace = autoToken.test(placement);
10640         if (autoPlace) {
10641             placement = placement.replace(autoToken, '') || 'top';
10642         }
10643         
10644         //this.el.detach()
10645         //this.el.setXY([0,0]);
10646         this.el.show();
10647         this.el.dom.style.display='block';
10648         this.el.addClass(placement);
10649         
10650         //this.el.appendTo(on_el);
10651         
10652         var p = this.getPosition();
10653         var box = this.el.getBox();
10654         
10655         if (autoPlace) {
10656             // fixme..
10657         }
10658         var align = Roo.bootstrap.Popover.alignment[placement]
10659         this.el.alignTo(on_el, align[0],align[1]);
10660         //var arrow = this.el.select('.arrow',true).first();
10661         //arrow.set(align[2], 
10662         
10663         this.el.addClass('in');
10664         this.hoverState = null;
10665         
10666         if (this.el.hasClass('fade')) {
10667             // fade it?
10668         }
10669         
10670     },
10671     hide : function()
10672     {
10673         this.el.setXY([0,0]);
10674         this.el.removeClass('in');
10675         this.el.hide();
10676         
10677     }
10678     
10679 });
10680
10681 Roo.bootstrap.Popover.alignment = {
10682     'left' : ['r-l', [-10,0], 'right'],
10683     'right' : ['l-r', [10,0], 'left'],
10684     'bottom' : ['t-b', [0,10], 'top'],
10685     'top' : [ 'b-t', [0,-10], 'bottom']
10686 };
10687
10688  /*
10689  * - LGPL
10690  *
10691  * Progress
10692  * 
10693  */
10694
10695 /**
10696  * @class Roo.bootstrap.Progress
10697  * @extends Roo.bootstrap.Component
10698  * Bootstrap Progress class
10699  * @cfg {Boolean} striped striped of the progress bar
10700  * @cfg {Boolean} active animated of the progress bar
10701  * 
10702  * 
10703  * @constructor
10704  * Create a new Progress
10705  * @param {Object} config The config object
10706  */
10707
10708 Roo.bootstrap.Progress = function(config){
10709     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10710 };
10711
10712 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10713     
10714     striped : false,
10715     active: false,
10716     
10717     getAutoCreate : function(){
10718         var cfg = {
10719             tag: 'div',
10720             cls: 'progress'
10721         };
10722         
10723         
10724         if(this.striped){
10725             cfg.cls += ' progress-striped';
10726         }
10727       
10728         if(this.active){
10729             cfg.cls += ' active';
10730         }
10731         
10732         
10733         return cfg;
10734     }
10735    
10736 });
10737
10738  
10739
10740  /*
10741  * - LGPL
10742  *
10743  * ProgressBar
10744  * 
10745  */
10746
10747 /**
10748  * @class Roo.bootstrap.ProgressBar
10749  * @extends Roo.bootstrap.Component
10750  * Bootstrap ProgressBar class
10751  * @cfg {Number} aria_valuenow aria-value now
10752  * @cfg {Number} aria_valuemin aria-value min
10753  * @cfg {Number} aria_valuemax aria-value max
10754  * @cfg {String} label label for the progress bar
10755  * @cfg {String} panel (success | info | warning | danger )
10756  * @cfg {String} role role of the progress bar
10757  * @cfg {String} sr_only text
10758  * 
10759  * 
10760  * @constructor
10761  * Create a new ProgressBar
10762  * @param {Object} config The config object
10763  */
10764
10765 Roo.bootstrap.ProgressBar = function(config){
10766     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10767 };
10768
10769 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10770     
10771     aria_valuenow : 0,
10772     aria_valuemin : 0,
10773     aria_valuemax : 100,
10774     label : false,
10775     panel : false,
10776     role : false,
10777     sr_only: false,
10778     
10779     getAutoCreate : function()
10780     {
10781         
10782         var cfg = {
10783             tag: 'div',
10784             cls: 'progress-bar',
10785             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10786         };
10787         
10788         if(this.sr_only){
10789             cfg.cn = {
10790                 tag: 'span',
10791                 cls: 'sr-only',
10792                 html: this.sr_only
10793             }
10794         }
10795         
10796         if(this.role){
10797             cfg.role = this.role;
10798         }
10799         
10800         if(this.aria_valuenow){
10801             cfg['aria-valuenow'] = this.aria_valuenow;
10802         }
10803         
10804         if(this.aria_valuemin){
10805             cfg['aria-valuemin'] = this.aria_valuemin;
10806         }
10807         
10808         if(this.aria_valuemax){
10809             cfg['aria-valuemax'] = this.aria_valuemax;
10810         }
10811         
10812         if(this.label && !this.sr_only){
10813             cfg.html = this.label;
10814         }
10815         
10816         if(this.panel){
10817             cfg.cls += ' progress-bar-' + this.panel;
10818         }
10819         
10820         return cfg;
10821     },
10822     
10823     update : function(aria_valuenow)
10824     {
10825         this.aria_valuenow = aria_valuenow;
10826         
10827         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10828     }
10829    
10830 });
10831
10832  
10833
10834  /*
10835  * - LGPL
10836  *
10837  * TabPanel
10838  * 
10839  */
10840
10841 /**
10842  * @class Roo.bootstrap.TabPanel
10843  * @extends Roo.bootstrap.Component
10844  * Bootstrap TabPanel class
10845  * @cfg {Boolean} active panel active
10846  * @cfg {String} html panel content
10847  * @cfg {String} tabId tab relate id
10848  * 
10849  * 
10850  * @constructor
10851  * Create a new TabPanel
10852  * @param {Object} config The config object
10853  */
10854
10855 Roo.bootstrap.TabPanel = function(config){
10856     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10857 };
10858
10859 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10860     
10861     active: false,
10862     html: false,
10863     tabId: false,
10864     
10865     getAutoCreate : function(){
10866         var cfg = {
10867             tag: 'div',
10868             cls: 'tab-pane',
10869             html: this.html || ''
10870         };
10871         
10872         if(this.active){
10873             cfg.cls += ' active';
10874         }
10875         
10876         if(this.tabId){
10877             cfg.tabId = this.tabId;
10878         }
10879         
10880         return cfg;
10881     }
10882    
10883 });
10884
10885  
10886
10887  /*
10888  * - LGPL
10889  *
10890  * DateField
10891  * 
10892  */
10893
10894 /**
10895  * @class Roo.bootstrap.DateField
10896  * @extends Roo.bootstrap.Input
10897  * Bootstrap DateField class
10898  * @cfg {Number} weekStart default 0
10899  * @cfg {Number} weekStart default 0
10900  * @cfg {Number} viewMode default empty, (months|years)
10901  * @cfg {Number} minViewMode default empty, (months|years)
10902  * @cfg {Number} startDate default -Infinity
10903  * @cfg {Number} endDate default Infinity
10904  * @cfg {Boolean} todayHighlight default false
10905  * @cfg {Boolean} todayBtn default false
10906  * @cfg {Boolean} calendarWeeks default false
10907  * @cfg {Object} daysOfWeekDisabled default empty
10908  * 
10909  * @cfg {Boolean} keyboardNavigation default true
10910  * @cfg {String} language default en
10911  * 
10912  * @constructor
10913  * Create a new DateField
10914  * @param {Object} config The config object
10915  */
10916
10917 Roo.bootstrap.DateField = function(config){
10918     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10919      this.addEvents({
10920             /**
10921              * @event show
10922              * Fires when this field show.
10923              * @param {Roo.bootstrap.DateField} this
10924              * @param {Mixed} date The date value
10925              */
10926             show : true,
10927             /**
10928              * @event show
10929              * Fires when this field hide.
10930              * @param {Roo.bootstrap.DateField} this
10931              * @param {Mixed} date The date value
10932              */
10933             hide : true,
10934             /**
10935              * @event select
10936              * Fires when select a date.
10937              * @param {Roo.bootstrap.DateField} this
10938              * @param {Mixed} date The date value
10939              */
10940             select : true
10941         });
10942 };
10943
10944 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10945     
10946     /**
10947      * @cfg {String} format
10948      * The default date format string which can be overriden for localization support.  The format must be
10949      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10950      */
10951     format : "m/d/y",
10952     /**
10953      * @cfg {String} altFormats
10954      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10955      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10956      */
10957     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10958     
10959     weekStart : 0,
10960     
10961     viewMode : '',
10962     
10963     minViewMode : '',
10964     
10965     todayHighlight : false,
10966     
10967     todayBtn: false,
10968     
10969     language: 'en',
10970     
10971     keyboardNavigation: true,
10972     
10973     calendarWeeks: false,
10974     
10975     startDate: -Infinity,
10976     
10977     endDate: Infinity,
10978     
10979     daysOfWeekDisabled: [],
10980     
10981     _events: [],
10982     
10983     UTCDate: function()
10984     {
10985         return new Date(Date.UTC.apply(Date, arguments));
10986     },
10987     
10988     UTCToday: function()
10989     {
10990         var today = new Date();
10991         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10992     },
10993     
10994     getDate: function() {
10995             var d = this.getUTCDate();
10996             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10997     },
10998     
10999     getUTCDate: function() {
11000             return this.date;
11001     },
11002     
11003     setDate: function(d) {
11004             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11005     },
11006     
11007     setUTCDate: function(d) {
11008             this.date = d;
11009             this.setValue(this.formatDate(this.date));
11010     },
11011         
11012     onRender: function(ct, position)
11013     {
11014         
11015         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11016         
11017         this.language = this.language || 'en';
11018         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11019         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11020         
11021         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11022         this.format = this.format || 'm/d/y';
11023         this.isInline = false;
11024         this.isInput = true;
11025         this.component = this.el.select('.add-on', true).first() || false;
11026         this.component = (this.component && this.component.length === 0) ? false : this.component;
11027         this.hasInput = this.component && this.inputEL().length;
11028         
11029         if (typeof(this.minViewMode === 'string')) {
11030             switch (this.minViewMode) {
11031                 case 'months':
11032                     this.minViewMode = 1;
11033                     break;
11034                 case 'years':
11035                     this.minViewMode = 2;
11036                     break;
11037                 default:
11038                     this.minViewMode = 0;
11039                     break;
11040             }
11041         }
11042         
11043         if (typeof(this.viewMode === 'string')) {
11044             switch (this.viewMode) {
11045                 case 'months':
11046                     this.viewMode = 1;
11047                     break;
11048                 case 'years':
11049                     this.viewMode = 2;
11050                     break;
11051                 default:
11052                     this.viewMode = 0;
11053                     break;
11054             }
11055         }
11056                 
11057         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11058         
11059         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11060         
11061         this.picker().on('mousedown', this.onMousedown, this);
11062         this.picker().on('click', this.onClick, this);
11063         
11064         this.picker().addClass('datepicker-dropdown');
11065         
11066         this.startViewMode = this.viewMode;
11067         
11068         
11069         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11070             if(!this.calendarWeeks){
11071                 v.remove();
11072                 return;
11073             };
11074             
11075             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11076             v.attr('colspan', function(i, val){
11077                 return parseInt(val) + 1;
11078             });
11079         })
11080                         
11081         
11082         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11083         
11084         this.setStartDate(this.startDate);
11085         this.setEndDate(this.endDate);
11086         
11087         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11088         
11089         this.fillDow();
11090         this.fillMonths();
11091         this.update();
11092         this.showMode();
11093         
11094         if(this.isInline) {
11095             this.show();
11096         }
11097     },
11098     
11099     picker : function()
11100     {
11101         return this.el.select('.datepicker', true).first();
11102     },
11103     
11104     fillDow: function()
11105     {
11106         var dowCnt = this.weekStart;
11107         
11108         var dow = {
11109             tag: 'tr',
11110             cn: [
11111                 
11112             ]
11113         };
11114         
11115         if(this.calendarWeeks){
11116             dow.cn.push({
11117                 tag: 'th',
11118                 cls: 'cw',
11119                 html: '&nbsp;'
11120             })
11121         }
11122         
11123         while (dowCnt < this.weekStart + 7) {
11124             dow.cn.push({
11125                 tag: 'th',
11126                 cls: 'dow',
11127                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11128             });
11129         }
11130         
11131         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11132     },
11133     
11134     fillMonths: function()
11135     {    
11136         var i = 0
11137         var months = this.picker().select('>.datepicker-months td', true).first();
11138         
11139         months.dom.innerHTML = '';
11140         
11141         while (i < 12) {
11142             var month = {
11143                 tag: 'span',
11144                 cls: 'month',
11145                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11146             }
11147             
11148             months.createChild(month);
11149         }
11150         
11151     },
11152     
11153     update: function(){
11154         
11155         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11156         
11157         if (this.date < this.startDate) {
11158             this.viewDate = new Date(this.startDate);
11159         } else if (this.date > this.endDate) {
11160             this.viewDate = new Date(this.endDate);
11161         } else {
11162             this.viewDate = new Date(this.date);
11163         }
11164         
11165         this.fill();
11166     },
11167     
11168     fill: function() {
11169         var d = new Date(this.viewDate),
11170                 year = d.getUTCFullYear(),
11171                 month = d.getUTCMonth(),
11172                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11173                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11174                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11175                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11176                 currentDate = this.date && this.date.valueOf(),
11177                 today = this.UTCToday();
11178         
11179         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11180         
11181 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11182         
11183 //        this.picker.select('>tfoot th.today').
11184 //                                              .text(dates[this.language].today)
11185 //                                              .toggle(this.todayBtn !== false);
11186     
11187         this.updateNavArrows();
11188         this.fillMonths();
11189                                                 
11190         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11191         
11192         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11193          
11194         prevMonth.setUTCDate(day);
11195         
11196         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11197         
11198         var nextMonth = new Date(prevMonth);
11199         
11200         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11201         
11202         nextMonth = nextMonth.valueOf();
11203         
11204         var fillMonths = false;
11205         
11206         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11207         
11208         while(prevMonth.valueOf() < nextMonth) {
11209             var clsName = '';
11210             
11211             if (prevMonth.getUTCDay() === this.weekStart) {
11212                 if(fillMonths){
11213                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11214                 }
11215                     
11216                 fillMonths = {
11217                     tag: 'tr',
11218                     cn: []
11219                 };
11220                 
11221                 if(this.calendarWeeks){
11222                     // ISO 8601: First week contains first thursday.
11223                     // ISO also states week starts on Monday, but we can be more abstract here.
11224                     var
11225                     // Start of current week: based on weekstart/current date
11226                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11227                     // Thursday of this week
11228                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11229                     // First Thursday of year, year from thursday
11230                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11231                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11232                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11233                     
11234                     fillMonths.cn.push({
11235                         tag: 'td',
11236                         cls: 'cw',
11237                         html: calWeek
11238                     });
11239                 }
11240             }
11241             
11242             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11243                 clsName += ' old';
11244             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11245                 clsName += ' new';
11246             }
11247             if (this.todayHighlight &&
11248                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11249                 prevMonth.getUTCMonth() == today.getMonth() &&
11250                 prevMonth.getUTCDate() == today.getDate()) {
11251                 clsName += ' today';
11252             }
11253             
11254             if (currentDate && prevMonth.valueOf() === currentDate) {
11255                 clsName += ' active';
11256             }
11257             
11258             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11259                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11260                     clsName += ' disabled';
11261             }
11262             
11263             fillMonths.cn.push({
11264                 tag: 'td',
11265                 cls: 'day ' + clsName,
11266                 html: prevMonth.getDate()
11267             })
11268             
11269             prevMonth.setDate(prevMonth.getDate()+1);
11270         }
11271           
11272         var currentYear = this.date && this.date.getUTCFullYear();
11273         var currentMonth = this.date && this.date.getUTCMonth();
11274         
11275         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11276         
11277         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11278             v.removeClass('active');
11279             
11280             if(currentYear === year && k === currentMonth){
11281                 v.addClass('active');
11282             }
11283             
11284             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11285                 v.addClass('disabled');
11286             }
11287             
11288         });
11289         
11290         
11291         year = parseInt(year/10, 10) * 10;
11292         
11293         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11294         
11295         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11296         
11297         year -= 1;
11298         for (var i = -1; i < 11; i++) {
11299             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11300                 tag: 'span',
11301                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11302                 html: year
11303             })
11304             
11305             year += 1;
11306         }
11307     },
11308     
11309     showMode: function(dir) {
11310         if (dir) {
11311             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11312         }
11313         Roo.each(this.picker().select('>div',true).elements, function(v){
11314             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11315             v.hide();
11316         });
11317         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11318     },
11319     
11320     place: function()
11321     {
11322         if(this.isInline) return;
11323         
11324         this.picker().removeClass(['bottom', 'top']);
11325         
11326         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11327             /*
11328              * place to the top of element!
11329              *
11330              */
11331             
11332             this.picker().addClass('top');
11333             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11334             
11335             return;
11336         }
11337         
11338         this.picker().addClass('bottom');
11339         
11340         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11341     },
11342     
11343     parseDate : function(value){
11344         if(!value || value instanceof Date){
11345             return value;
11346         }
11347         var v = Date.parseDate(value, this.format);
11348         if (!v && this.useIso) {
11349             v = Date.parseDate(value, 'Y-m-d');
11350         }
11351         if(!v && this.altFormats){
11352             if(!this.altFormatsArray){
11353                 this.altFormatsArray = this.altFormats.split("|");
11354             }
11355             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11356                 v = Date.parseDate(value, this.altFormatsArray[i]);
11357             }
11358         }
11359         return v;
11360     },
11361     
11362     formatDate : function(date, fmt){
11363         return (!date || !(date instanceof Date)) ?
11364         date : date.dateFormat(fmt || this.format);
11365     },
11366     
11367     onFocus : function()
11368     {
11369         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11370         this.show();
11371     },
11372     
11373     onBlur : function()
11374     {
11375         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11376         this.hide();
11377     },
11378     
11379     show : function()
11380     {
11381         this.picker().show();
11382         this.update();
11383         this.place();
11384         
11385         this.fireEvent('show', this, this.date);
11386     },
11387     
11388     hide : function()
11389     {
11390         if(this.isInline) return;
11391         this.picker().hide();
11392         this.viewMode = this.startViewMode;
11393         this.showMode();
11394         
11395         this.fireEvent('hide', this, this.date);
11396         
11397     },
11398     
11399     onMousedown: function(e){
11400         e.stopPropagation();
11401         e.preventDefault();
11402     },
11403     
11404     keyup: function(e){
11405         Roo.bootstrap.DateField.superclass.keyup.call(this);
11406         this.update();
11407         
11408     },
11409
11410     setValue: function(v){
11411         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11412         
11413         this.fireEvent('select', this, this.date);
11414         
11415     },
11416     
11417     fireKey: function(e){
11418         if (!this.picker().isVisible()){
11419             if (e.keyCode == 27) // allow escape to hide and re-show picker
11420                 this.show();
11421             return;
11422         }
11423         var dateChanged = false,
11424         dir, day, month,
11425         newDate, newViewDate;
11426         switch(e.keyCode){
11427             case 27: // escape
11428                 this.hide();
11429                 e.preventDefault();
11430                 break;
11431             case 37: // left
11432             case 39: // right
11433                 if (!this.keyboardNavigation) break;
11434                 dir = e.keyCode == 37 ? -1 : 1;
11435                 
11436                 if (e.ctrlKey){
11437                     newDate = this.moveYear(this.date, dir);
11438                     newViewDate = this.moveYear(this.viewDate, dir);
11439                 } else if (e.shiftKey){
11440                     newDate = this.moveMonth(this.date, dir);
11441                     newViewDate = this.moveMonth(this.viewDate, dir);
11442                 } else {
11443                     newDate = new Date(this.date);
11444                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11445                     newViewDate = new Date(this.viewDate);
11446                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11447                 }
11448                 if (this.dateWithinRange(newDate)){
11449                     this.date = newDate;
11450                     this.viewDate = newViewDate;
11451                     this.setValue(this.formatDate(this.date));
11452                     this.update();
11453                     e.preventDefault();
11454                     dateChanged = true;
11455                 }
11456                 break;
11457             case 38: // up
11458             case 40: // down
11459                 if (!this.keyboardNavigation) break;
11460                 dir = e.keyCode == 38 ? -1 : 1;
11461                 if (e.ctrlKey){
11462                     newDate = this.moveYear(this.date, dir);
11463                     newViewDate = this.moveYear(this.viewDate, dir);
11464                 } else if (e.shiftKey){
11465                     newDate = this.moveMonth(this.date, dir);
11466                     newViewDate = this.moveMonth(this.viewDate, dir);
11467                 } else {
11468                     newDate = new Date(this.date);
11469                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11470                     newViewDate = new Date(this.viewDate);
11471                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11472                 }
11473                 if (this.dateWithinRange(newDate)){
11474                     this.date = newDate;
11475                     this.viewDate = newViewDate;
11476                     this.setValue(this.formatDate(this.date));
11477                     this.update();
11478                     e.preventDefault();
11479                     dateChanged = true;
11480                 }
11481                 break;
11482             case 13: // enter
11483                 this.setValue(this.formatDate(this.date));
11484                 this.hide();
11485                 e.preventDefault();
11486                 break;
11487             case 9: // tab
11488                 this.setValue(this.formatDate(this.date));
11489                 this.hide();
11490                 break;
11491         }
11492     },
11493     
11494     
11495     onClick: function(e) {
11496         e.stopPropagation();
11497         e.preventDefault();
11498         
11499         var target = e.getTarget();
11500         
11501         if(target.nodeName.toLowerCase() === 'i'){
11502             target = Roo.get(target).dom.parentNode;
11503         }
11504         
11505         var nodeName = target.nodeName;
11506         var className = target.className;
11507         var html = target.innerHTML;
11508         
11509         switch(nodeName.toLowerCase()) {
11510             case 'th':
11511                 switch(className) {
11512                     case 'switch':
11513                         this.showMode(1);
11514                         break;
11515                     case 'prev':
11516                     case 'next':
11517                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11518                         switch(this.viewMode){
11519                                 case 0:
11520                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11521                                         break;
11522                                 case 1:
11523                                 case 2:
11524                                         this.viewDate = this.moveYear(this.viewDate, dir);
11525                                         break;
11526                         }
11527                         this.fill();
11528                         break;
11529                     case 'today':
11530                         var date = new Date();
11531                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11532                         this.fill()
11533                         this.setValue(this.formatDate(this.date));
11534                         this.hide();
11535                         break;
11536                 }
11537                 break;
11538             case 'span':
11539                 if (className.indexOf('disabled') === -1) {
11540                     this.viewDate.setUTCDate(1);
11541                     if (className.indexOf('month') !== -1) {
11542                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11543                     } else {
11544                         var year = parseInt(html, 10) || 0;
11545                         this.viewDate.setUTCFullYear(year);
11546                         
11547                     }
11548                     this.showMode(-1);
11549                     this.fill();
11550                 }
11551                 break;
11552                 
11553             case 'td':
11554                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11555                     var day = parseInt(html, 10) || 1;
11556                     var year = this.viewDate.getUTCFullYear(),
11557                         month = this.viewDate.getUTCMonth();
11558
11559                     if (className.indexOf('old') !== -1) {
11560                         if(month === 0 ){
11561                             month = 11;
11562                             year -= 1;
11563                         }else{
11564                             month -= 1;
11565                         }
11566                     } else if (className.indexOf('new') !== -1) {
11567                         if (month == 11) {
11568                             month = 0;
11569                             year += 1;
11570                         } else {
11571                             month += 1;
11572                         }
11573                     }
11574                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11575                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11576                     this.fill();
11577                     this.setValue(this.formatDate(this.date));
11578                     this.hide();
11579                 }
11580                 break;
11581         }
11582     },
11583     
11584     setStartDate: function(startDate){
11585         this.startDate = startDate || -Infinity;
11586         if (this.startDate !== -Infinity) {
11587             this.startDate = this.parseDate(this.startDate);
11588         }
11589         this.update();
11590         this.updateNavArrows();
11591     },
11592
11593     setEndDate: function(endDate){
11594         this.endDate = endDate || Infinity;
11595         if (this.endDate !== Infinity) {
11596             this.endDate = this.parseDate(this.endDate);
11597         }
11598         this.update();
11599         this.updateNavArrows();
11600     },
11601     
11602     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11603         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11604         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11605             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11606         }
11607         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11608             return parseInt(d, 10);
11609         });
11610         this.update();
11611         this.updateNavArrows();
11612     },
11613     
11614     updateNavArrows: function() {
11615         var d = new Date(this.viewDate),
11616         year = d.getUTCFullYear(),
11617         month = d.getUTCMonth();
11618         
11619         Roo.each(this.picker().select('.prev', true).elements, function(v){
11620             v.show();
11621             switch (this.viewMode) {
11622                 case 0:
11623
11624                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11625                         v.hide();
11626                     }
11627                     break;
11628                 case 1:
11629                 case 2:
11630                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11631                         v.hide();
11632                     }
11633                     break;
11634             }
11635         });
11636         
11637         Roo.each(this.picker().select('.next', true).elements, function(v){
11638             v.show();
11639             switch (this.viewMode) {
11640                 case 0:
11641
11642                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11643                         v.hide();
11644                     }
11645                     break;
11646                 case 1:
11647                 case 2:
11648                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11649                         v.hide();
11650                     }
11651                     break;
11652             }
11653         })
11654     },
11655     
11656     moveMonth: function(date, dir){
11657         if (!dir) return date;
11658         var new_date = new Date(date.valueOf()),
11659         day = new_date.getUTCDate(),
11660         month = new_date.getUTCMonth(),
11661         mag = Math.abs(dir),
11662         new_month, test;
11663         dir = dir > 0 ? 1 : -1;
11664         if (mag == 1){
11665             test = dir == -1
11666             // If going back one month, make sure month is not current month
11667             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11668             ? function(){
11669                 return new_date.getUTCMonth() == month;
11670             }
11671             // If going forward one month, make sure month is as expected
11672             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11673             : function(){
11674                 return new_date.getUTCMonth() != new_month;
11675             };
11676             new_month = month + dir;
11677             new_date.setUTCMonth(new_month);
11678             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11679             if (new_month < 0 || new_month > 11)
11680                 new_month = (new_month + 12) % 12;
11681         } else {
11682             // For magnitudes >1, move one month at a time...
11683             for (var i=0; i<mag; i++)
11684                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11685                 new_date = this.moveMonth(new_date, dir);
11686             // ...then reset the day, keeping it in the new month
11687             new_month = new_date.getUTCMonth();
11688             new_date.setUTCDate(day);
11689             test = function(){
11690                 return new_month != new_date.getUTCMonth();
11691             };
11692         }
11693         // Common date-resetting loop -- if date is beyond end of month, make it
11694         // end of month
11695         while (test()){
11696             new_date.setUTCDate(--day);
11697             new_date.setUTCMonth(new_month);
11698         }
11699         return new_date;
11700     },
11701
11702     moveYear: function(date, dir){
11703         return this.moveMonth(date, dir*12);
11704     },
11705
11706     dateWithinRange: function(date){
11707         return date >= this.startDate && date <= this.endDate;
11708     },
11709
11710     
11711     remove: function() {
11712         this.picker().remove();
11713     }
11714    
11715 });
11716
11717 Roo.apply(Roo.bootstrap.DateField,  {
11718     
11719     head : {
11720         tag: 'thead',
11721         cn: [
11722         {
11723             tag: 'tr',
11724             cn: [
11725             {
11726                 tag: 'th',
11727                 cls: 'prev',
11728                 html: '<i class="icon-arrow-left"/>'
11729             },
11730             {
11731                 tag: 'th',
11732                 cls: 'switch',
11733                 colspan: '5'
11734             },
11735             {
11736                 tag: 'th',
11737                 cls: 'next',
11738                 html: '<i class="icon-arrow-right"/>'
11739             }
11740
11741             ]
11742         }
11743         ]
11744     },
11745     
11746     content : {
11747         tag: 'tbody',
11748         cn: [
11749         {
11750             tag: 'tr',
11751             cn: [
11752             {
11753                 tag: 'td',
11754                 colspan: '7'
11755             }
11756             ]
11757         }
11758         ]
11759     },
11760     
11761     footer : {
11762         tag: 'tfoot',
11763         cn: [
11764         {
11765             tag: 'tr',
11766             cn: [
11767             {
11768                 tag: 'th',
11769                 colspan: '7',
11770                 cls: 'today'
11771             }
11772                     
11773             ]
11774         }
11775         ]
11776     },
11777     
11778     dates:{
11779         en: {
11780             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11781             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11782             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11783             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11784             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11785             today: "Today"
11786         }
11787     },
11788     
11789     modes: [
11790     {
11791         clsName: 'days',
11792         navFnc: 'Month',
11793         navStep: 1
11794     },
11795     {
11796         clsName: 'months',
11797         navFnc: 'FullYear',
11798         navStep: 1
11799     },
11800     {
11801         clsName: 'years',
11802         navFnc: 'FullYear',
11803         navStep: 10
11804     }]
11805 });
11806
11807 Roo.apply(Roo.bootstrap.DateField,  {
11808   
11809     template : {
11810         tag: 'div',
11811         cls: 'datepicker dropdown-menu',
11812         cn: [
11813         {
11814             tag: 'div',
11815             cls: 'datepicker-days',
11816             cn: [
11817             {
11818                 tag: 'table',
11819                 cls: 'table-condensed',
11820                 cn:[
11821                 Roo.bootstrap.DateField.head,
11822                 {
11823                     tag: 'tbody'
11824                 },
11825                 Roo.bootstrap.DateField.footer
11826                 ]
11827             }
11828             ]
11829         },
11830         {
11831             tag: 'div',
11832             cls: 'datepicker-months',
11833             cn: [
11834             {
11835                 tag: 'table',
11836                 cls: 'table-condensed',
11837                 cn:[
11838                 Roo.bootstrap.DateField.head,
11839                 Roo.bootstrap.DateField.content,
11840                 Roo.bootstrap.DateField.footer
11841                 ]
11842             }
11843             ]
11844         },
11845         {
11846             tag: 'div',
11847             cls: 'datepicker-years',
11848             cn: [
11849             {
11850                 tag: 'table',
11851                 cls: 'table-condensed',
11852                 cn:[
11853                 Roo.bootstrap.DateField.head,
11854                 Roo.bootstrap.DateField.content,
11855                 Roo.bootstrap.DateField.footer
11856                 ]
11857             }
11858             ]
11859         }
11860         ]
11861     }
11862 });
11863
11864  
11865
11866  /*
11867  * - LGPL
11868  *
11869  * TimeField
11870  * 
11871  */
11872
11873 /**
11874  * @class Roo.bootstrap.TimeField
11875  * @extends Roo.bootstrap.Input
11876  * Bootstrap DateField class
11877  * 
11878  * 
11879  * @constructor
11880  * Create a new TimeField
11881  * @param {Object} config The config object
11882  */
11883
11884 Roo.bootstrap.TimeField = function(config){
11885     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11886     this.addEvents({
11887             /**
11888              * @event show
11889              * Fires when this field show.
11890              * @param {Roo.bootstrap.DateField} this
11891              * @param {Mixed} date The date value
11892              */
11893             show : true,
11894             /**
11895              * @event show
11896              * Fires when this field hide.
11897              * @param {Roo.bootstrap.DateField} this
11898              * @param {Mixed} date The date value
11899              */
11900             hide : true,
11901             /**
11902              * @event select
11903              * Fires when select a date.
11904              * @param {Roo.bootstrap.DateField} this
11905              * @param {Mixed} date The date value
11906              */
11907             select : true
11908         });
11909 };
11910
11911 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11912     
11913     /**
11914      * @cfg {String} format
11915      * The default time format string which can be overriden for localization support.  The format must be
11916      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11917      */
11918     format : "H:i",
11919        
11920     onRender: function(ct, position)
11921     {
11922         
11923         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11924                 
11925         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11926         
11927         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11928         
11929         this.pop = this.picker().select('>.datepicker-time',true).first();
11930         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11931         
11932         this.picker().on('mousedown', this.onMousedown, this);
11933         this.picker().on('click', this.onClick, this);
11934         
11935         this.picker().addClass('datepicker-dropdown');
11936     
11937         this.fillTime();
11938         this.update();
11939             
11940         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11941         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11942         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11943         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11944         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11945         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11946
11947     },
11948     
11949     fireKey: function(e){
11950         if (!this.picker().isVisible()){
11951             if (e.keyCode == 27) // allow escape to hide and re-show picker
11952                 this.show();
11953             return;
11954         }
11955
11956         e.preventDefault();
11957         
11958         switch(e.keyCode){
11959             case 27: // escape
11960                 this.hide();
11961                 break;
11962             case 37: // left
11963             case 39: // right
11964                 this.onTogglePeriod();
11965                 break;
11966             case 38: // up
11967                 this.onIncrementMinutes();
11968                 break;
11969             case 40: // down
11970                 this.onDecrementMinutes();
11971                 break;
11972             case 13: // enter
11973             case 9: // tab
11974                 this.setTime();
11975                 break;
11976         }
11977     },
11978     
11979     onClick: function(e) {
11980         e.stopPropagation();
11981         e.preventDefault();
11982     },
11983     
11984     picker : function()
11985     {
11986         return this.el.select('.datepicker', true).first();
11987     },
11988     
11989     fillTime: function()
11990     {    
11991         var time = this.pop.select('tbody', true).first();
11992         
11993         time.dom.innerHTML = '';
11994         
11995         time.createChild({
11996             tag: 'tr',
11997             cn: [
11998                 {
11999                     tag: 'td',
12000                     cn: [
12001                         {
12002                             tag: 'a',
12003                             href: '#',
12004                             cls: 'btn',
12005                             cn: [
12006                                 {
12007                                     tag: 'span',
12008                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12009                                 }
12010                             ]
12011                         } 
12012                     ]
12013                 },
12014                 {
12015                     tag: 'td',
12016                     cls: 'separator'
12017                 },
12018                 {
12019                     tag: 'td',
12020                     cn: [
12021                         {
12022                             tag: 'a',
12023                             href: '#',
12024                             cls: 'btn',
12025                             cn: [
12026                                 {
12027                                     tag: 'span',
12028                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12029                                 }
12030                             ]
12031                         }
12032                     ]
12033                 },
12034                 {
12035                     tag: 'td',
12036                     cls: 'separator'
12037                 }
12038             ]
12039         });
12040         
12041         time.createChild({
12042             tag: 'tr',
12043             cn: [
12044                 {
12045                     tag: 'td',
12046                     cn: [
12047                         {
12048                             tag: 'span',
12049                             cls: 'timepicker-hour',
12050                             html: '00'
12051                         }  
12052                     ]
12053                 },
12054                 {
12055                     tag: 'td',
12056                     cls: 'separator',
12057                     html: ':'
12058                 },
12059                 {
12060                     tag: 'td',
12061                     cn: [
12062                         {
12063                             tag: 'span',
12064                             cls: 'timepicker-minute',
12065                             html: '00'
12066                         }  
12067                     ]
12068                 },
12069                 {
12070                     tag: 'td',
12071                     cls: 'separator'
12072                 },
12073                 {
12074                     tag: 'td',
12075                     cn: [
12076                         {
12077                             tag: 'button',
12078                             type: 'button',
12079                             cls: 'btn btn-primary period',
12080                             html: 'AM'
12081                             
12082                         }
12083                     ]
12084                 }
12085             ]
12086         });
12087         
12088         time.createChild({
12089             tag: 'tr',
12090             cn: [
12091                 {
12092                     tag: 'td',
12093                     cn: [
12094                         {
12095                             tag: 'a',
12096                             href: '#',
12097                             cls: 'btn',
12098                             cn: [
12099                                 {
12100                                     tag: 'span',
12101                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12102                                 }
12103                             ]
12104                         }
12105                     ]
12106                 },
12107                 {
12108                     tag: 'td',
12109                     cls: 'separator'
12110                 },
12111                 {
12112                     tag: 'td',
12113                     cn: [
12114                         {
12115                             tag: 'a',
12116                             href: '#',
12117                             cls: 'btn',
12118                             cn: [
12119                                 {
12120                                     tag: 'span',
12121                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12122                                 }
12123                             ]
12124                         }
12125                     ]
12126                 },
12127                 {
12128                     tag: 'td',
12129                     cls: 'separator'
12130                 }
12131             ]
12132         });
12133         
12134     },
12135     
12136     update: function()
12137     {
12138         
12139         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12140         
12141         this.fill();
12142     },
12143     
12144     fill: function() 
12145     {
12146         var hours = this.time.getHours();
12147         var minutes = this.time.getMinutes();
12148         var period = 'AM';
12149         
12150         if(hours > 11){
12151             period = 'PM';
12152         }
12153         
12154         if(hours == 0){
12155             hours = 12;
12156         }
12157         
12158         
12159         if(hours > 12){
12160             hours = hours - 12;
12161         }
12162         
12163         if(hours < 10){
12164             hours = '0' + hours;
12165         }
12166         
12167         if(minutes < 10){
12168             minutes = '0' + minutes;
12169         }
12170         
12171         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12172         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12173         this.pop.select('button', true).first().dom.innerHTML = period;
12174         
12175     },
12176     
12177     place: function()
12178     {   
12179         this.picker().removeClass(['bottom', 'top']);
12180         
12181         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12182             /*
12183              * place to the top of element!
12184              *
12185              */
12186             
12187             this.picker().addClass('top');
12188             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12189             
12190             return;
12191         }
12192         
12193         this.picker().addClass('bottom');
12194         
12195         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12196     },
12197   
12198     onFocus : function()
12199     {
12200         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12201         this.show();
12202     },
12203     
12204     onBlur : function()
12205     {
12206         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12207         this.hide();
12208     },
12209     
12210     show : function()
12211     {
12212         this.picker().show();
12213         this.pop.show();
12214         this.update();
12215         this.place();
12216         
12217         this.fireEvent('show', this, this.date);
12218     },
12219     
12220     hide : function()
12221     {
12222         this.picker().hide();
12223         this.pop.hide();
12224         
12225         this.fireEvent('hide', this, this.date);
12226     },
12227     
12228     setTime : function()
12229     {
12230         this.hide();
12231         this.setValue(this.time.format(this.format));
12232         
12233         this.fireEvent('select', this, this.date);
12234         
12235         
12236     },
12237     
12238     onMousedown: function(e){
12239         e.stopPropagation();
12240         e.preventDefault();
12241     },
12242     
12243     onIncrementHours: function()
12244     {
12245         Roo.log('onIncrementHours');
12246         this.time = this.time.add(Date.HOUR, 1);
12247         this.update();
12248         
12249     },
12250     
12251     onDecrementHours: function()
12252     {
12253         Roo.log('onDecrementHours');
12254         this.time = this.time.add(Date.HOUR, -1);
12255         this.update();
12256     },
12257     
12258     onIncrementMinutes: function()
12259     {
12260         Roo.log('onIncrementMinutes');
12261         this.time = this.time.add(Date.MINUTE, 1);
12262         this.update();
12263     },
12264     
12265     onDecrementMinutes: function()
12266     {
12267         Roo.log('onDecrementMinutes');
12268         this.time = this.time.add(Date.MINUTE, -1);
12269         this.update();
12270     },
12271     
12272     onTogglePeriod: function()
12273     {
12274         Roo.log('onTogglePeriod');
12275         this.time = this.time.add(Date.HOUR, 12);
12276         this.update();
12277     }
12278     
12279    
12280 });
12281
12282 Roo.apply(Roo.bootstrap.TimeField,  {
12283     
12284     content : {
12285         tag: 'tbody',
12286         cn: [
12287             {
12288                 tag: 'tr',
12289                 cn: [
12290                 {
12291                     tag: 'td',
12292                     colspan: '7'
12293                 }
12294                 ]
12295             }
12296         ]
12297     },
12298     
12299     footer : {
12300         tag: 'tfoot',
12301         cn: [
12302             {
12303                 tag: 'tr',
12304                 cn: [
12305                 {
12306                     tag: 'th',
12307                     colspan: '7',
12308                     cls: '',
12309                     cn: [
12310                         {
12311                             tag: 'button',
12312                             cls: 'btn btn-info ok',
12313                             html: 'OK'
12314                         }
12315                     ]
12316                 }
12317
12318                 ]
12319             }
12320         ]
12321     }
12322 });
12323
12324 Roo.apply(Roo.bootstrap.TimeField,  {
12325   
12326     template : {
12327         tag: 'div',
12328         cls: 'datepicker dropdown-menu',
12329         cn: [
12330             {
12331                 tag: 'div',
12332                 cls: 'datepicker-time',
12333                 cn: [
12334                 {
12335                     tag: 'table',
12336                     cls: 'table-condensed',
12337                     cn:[
12338                     Roo.bootstrap.TimeField.content,
12339                     Roo.bootstrap.TimeField.footer
12340                     ]
12341                 }
12342                 ]
12343             }
12344         ]
12345     }
12346 });
12347
12348  
12349
12350  /*
12351  * - LGPL
12352  *
12353  * CheckBox
12354  * 
12355  */
12356
12357 /**
12358  * @class Roo.bootstrap.CheckBox
12359  * @extends Roo.bootstrap.Input
12360  * Bootstrap CheckBox class
12361  * 
12362  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12363  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12364  * @cfg {String} boxLabel The text that appears beside the checkbox
12365  * @cfg {Boolean} checked initnal the element
12366  * 
12367  * @constructor
12368  * Create a new CheckBox
12369  * @param {Object} config The config object
12370  */
12371
12372 Roo.bootstrap.CheckBox = function(config){
12373     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12374    
12375         this.addEvents({
12376             /**
12377             * @event check
12378             * Fires when the element is checked or unchecked.
12379             * @param {Roo.bootstrap.CheckBox} this This input
12380             * @param {Boolean} checked The new checked value
12381             */
12382            check : true
12383         });
12384 };
12385
12386 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12387     
12388     inputType: 'checkbox',
12389     inputValue: 1,
12390     valueOff: 0,
12391     boxLabel: false,
12392     checked: false,
12393     
12394     getAutoCreate : function()
12395     {
12396         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12397         
12398         var id = Roo.id();
12399         
12400         var cfg = {};
12401         
12402         cfg.cls = 'form-group' //input-group
12403         
12404         var input =  {
12405             tag: 'input',
12406             id : id,
12407             type : this.inputType,
12408             value : (!this.checked) ? this.valueOff : this.inputValue,
12409             cls : 'form-box',
12410             placeholder : this.placeholder || ''
12411             
12412         };
12413         
12414         if (this.disabled) {
12415             input.disabled=true;
12416         }
12417         
12418         if(this.checked){
12419             input.checked = this.checked;
12420         }
12421         
12422         if (this.name) {
12423             input.name = this.name;
12424         }
12425         
12426         if (this.size) {
12427             input.cls += ' input-' + this.size;
12428         }
12429         
12430         var settings=this;
12431         ['xs','sm','md','lg'].map(function(size){
12432             if (settings[size]) {
12433                 cfg.cls += ' col-' + size + '-' + settings[size];
12434             }
12435         });
12436         
12437         var inputblock = input;
12438         
12439         if (this.before || this.after) {
12440             
12441             inputblock = {
12442                 cls : 'input-group',
12443                 cn :  [] 
12444             };
12445             if (this.before) {
12446                 inputblock.cn.push({
12447                     tag :'span',
12448                     cls : 'input-group-addon',
12449                     html : this.before
12450                 });
12451             }
12452             inputblock.cn.push(input);
12453             if (this.after) {
12454                 inputblock.cn.push({
12455                     tag :'span',
12456                     cls : 'input-group-addon',
12457                     html : this.after
12458                 });
12459             }
12460             
12461         };
12462         
12463         if (align ==='left' && this.fieldLabel.length) {
12464                 Roo.log("left and has label");
12465                 cfg.cn = [
12466                     
12467                     {
12468                         tag: 'label',
12469                         'for' :  id,
12470                         cls : 'control-label col-md-' + this.labelWidth,
12471                         html : this.fieldLabel
12472                         
12473                     },
12474                     {
12475                         cls : "col-md-" + (12 - this.labelWidth), 
12476                         cn: [
12477                             inputblock
12478                         ]
12479                     }
12480                     
12481                 ];
12482         } else if ( this.fieldLabel.length) {
12483                 Roo.log(" label");
12484                 cfg.cn = [
12485                    
12486                     {
12487                         tag: this.boxLabel ? 'span' : 'label',
12488                         'for': id,
12489                         cls: 'control-label box-input-label',
12490                         //cls : 'input-group-addon',
12491                         html : this.fieldLabel
12492                         
12493                     },
12494                     
12495                     inputblock
12496                     
12497                 ];
12498
12499         } else {
12500             
12501                    Roo.log(" no label && no align");
12502                 cfg.cn = [
12503                     
12504                         inputblock
12505                     
12506                 ];
12507                 
12508                 
12509         };
12510         
12511         if(this.boxLabel){
12512             cfg.cn.push({
12513                 tag: 'label',
12514                 'for': id,
12515                 cls: 'box-label',
12516                 html: this.boxLabel
12517             })
12518         }
12519         
12520         return cfg;
12521         
12522     },
12523     
12524     /**
12525      * return the real input element.
12526      */
12527     inputEl: function ()
12528     {
12529         return this.el.select('input.form-box',true).first();
12530     },
12531     
12532     initEvents : function()
12533     {
12534 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12535         
12536         this.inputEl().on('click', this.onClick,  this);
12537         
12538     },
12539     
12540     onClick : function()
12541     {   
12542         this.setChecked(!this.checked);
12543     },
12544     
12545     setChecked : function(state,suppressEvent)
12546     {
12547         this.checked = state;
12548         
12549         if(suppressEvent !== true){
12550             this.fireEvent('check', this, state);
12551         }
12552         
12553         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12554         
12555     }
12556     
12557 });
12558
12559  
12560 /*
12561  * - LGPL
12562  *
12563  * Radio
12564  * 
12565  */
12566
12567 /**
12568  * @class Roo.bootstrap.Radio
12569  * @extends Roo.bootstrap.CheckBox
12570  * Bootstrap Radio class
12571
12572  * @constructor
12573  * Create a new Radio
12574  * @param {Object} config The config object
12575  */
12576
12577 Roo.bootstrap.Radio = function(config){
12578     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12579    
12580 };
12581
12582 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12583     
12584     inputType: 'radio',
12585     inputValue: '',
12586     valueOff: '',
12587     
12588     getAutoCreate : function()
12589     {
12590         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12591         
12592         var id = Roo.id();
12593         
12594         var cfg = {};
12595         
12596         cfg.cls = 'form-group' //input-group
12597         
12598         var input =  {
12599             tag: 'input',
12600             id : id,
12601             type : this.inputType,
12602             value : (!this.checked) ? this.valueOff : this.inputValue,
12603             cls : 'form-box',
12604             placeholder : this.placeholder || ''
12605             
12606         };
12607         
12608         if (this.disabled) {
12609             input.disabled=true;
12610         }
12611         
12612         if(this.checked){
12613             input.checked = this.checked;
12614         }
12615         
12616         if (this.name) {
12617             input.name = this.name;
12618         }
12619         
12620         if (this.size) {
12621             input.cls += ' input-' + this.size;
12622         }
12623         
12624         var settings=this;
12625         ['xs','sm','md','lg'].map(function(size){
12626             if (settings[size]) {
12627                 cfg.cls += ' col-' + size + '-' + settings[size];
12628             }
12629         });
12630         
12631         var inputblock = input;
12632         
12633         if (this.before || this.after) {
12634             
12635             inputblock = {
12636                 cls : 'input-group',
12637                 cn :  [] 
12638             };
12639             if (this.before) {
12640                 inputblock.cn.push({
12641                     tag :'span',
12642                     cls : 'input-group-addon',
12643                     html : this.before
12644                 });
12645             }
12646             inputblock.cn.push(input);
12647             if (this.after) {
12648                 inputblock.cn.push({
12649                     tag :'span',
12650                     cls : 'input-group-addon',
12651                     html : this.after
12652                 });
12653             }
12654             
12655         };
12656         
12657         if (align ==='left' && this.fieldLabel.length) {
12658                 Roo.log("left and has label");
12659                 cfg.cn = [
12660                     
12661                     {
12662                         tag: 'label',
12663                         'for' :  id,
12664                         cls : 'control-label col-md-' + this.labelWidth,
12665                         html : this.fieldLabel
12666                         
12667                     },
12668                     {
12669                         cls : "col-md-" + (12 - this.labelWidth), 
12670                         cn: [
12671                             inputblock
12672                         ]
12673                     }
12674                     
12675                 ];
12676         } else if ( this.fieldLabel.length) {
12677                 Roo.log(" label");
12678                  cfg.cn = [
12679                    
12680                     {
12681                         tag: 'label',
12682                         'for': id,
12683                         cls: 'control-label box-input-label',
12684                         //cls : 'input-group-addon',
12685                         html : this.fieldLabel
12686                         
12687                     },
12688                     
12689                     inputblock
12690                     
12691                 ];
12692
12693         } else {
12694             
12695                    Roo.log(" no label && no align");
12696                 cfg.cn = [
12697                     
12698                         inputblock
12699                     
12700                 ];
12701                 
12702                 
12703         };
12704         
12705         if(this.boxLabel){
12706             cfg.cn.push({
12707                 tag: 'label',
12708                 'for': id,
12709                 cls: 'box-label',
12710                 html: this.boxLabel
12711             })
12712         }
12713         
12714         return cfg;
12715         
12716     },
12717    
12718     onClick : function()
12719     {   
12720         this.setChecked(true);
12721     },
12722     
12723     setChecked : function(state,suppressEvent)
12724     {
12725         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12726             v.dom.checked = false;
12727         });
12728         
12729         this.checked = state;
12730         this.inputEl().dom.checked = state;
12731         
12732         if(suppressEvent !== true){
12733             this.fireEvent('check', this, state);
12734         }
12735         
12736         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12737         
12738     },
12739     
12740     getGroupValue : function()
12741     {
12742         var value = ''
12743         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12744             if(v.dom.checked == true){
12745                 value = v.dom.value;
12746             }
12747         });
12748         
12749         return value;
12750     },
12751     
12752     /**
12753      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12754      * @return {Mixed} value The field value
12755      */
12756     getValue : function(){
12757         return this.getGroupValue();
12758     }
12759     
12760 });
12761
12762  
12763 //<script type="text/javascript">
12764
12765 /*
12766  * Based  Ext JS Library 1.1.1
12767  * Copyright(c) 2006-2007, Ext JS, LLC.
12768  * LGPL
12769  *
12770  */
12771  
12772 /**
12773  * @class Roo.HtmlEditorCore
12774  * @extends Roo.Component
12775  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12776  *
12777  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12778  */
12779
12780 Roo.HtmlEditorCore = function(config){
12781     
12782     
12783     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12784     this.addEvents({
12785         /**
12786          * @event initialize
12787          * Fires when the editor is fully initialized (including the iframe)
12788          * @param {Roo.HtmlEditorCore} this
12789          */
12790         initialize: true,
12791         /**
12792          * @event activate
12793          * Fires when the editor is first receives the focus. Any insertion must wait
12794          * until after this event.
12795          * @param {Roo.HtmlEditorCore} this
12796          */
12797         activate: true,
12798          /**
12799          * @event beforesync
12800          * Fires before the textarea is updated with content from the editor iframe. Return false
12801          * to cancel the sync.
12802          * @param {Roo.HtmlEditorCore} this
12803          * @param {String} html
12804          */
12805         beforesync: true,
12806          /**
12807          * @event beforepush
12808          * Fires before the iframe editor is updated with content from the textarea. Return false
12809          * to cancel the push.
12810          * @param {Roo.HtmlEditorCore} this
12811          * @param {String} html
12812          */
12813         beforepush: true,
12814          /**
12815          * @event sync
12816          * Fires when the textarea is updated with content from the editor iframe.
12817          * @param {Roo.HtmlEditorCore} this
12818          * @param {String} html
12819          */
12820         sync: true,
12821          /**
12822          * @event push
12823          * Fires when the iframe editor is updated with content from the textarea.
12824          * @param {Roo.HtmlEditorCore} this
12825          * @param {String} html
12826          */
12827         push: true,
12828         
12829         /**
12830          * @event editorevent
12831          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12832          * @param {Roo.HtmlEditorCore} this
12833          */
12834         editorevent: true
12835     });
12836      
12837 };
12838
12839
12840 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12841
12842
12843      /**
12844      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12845      */
12846     
12847     owner : false,
12848     
12849      /**
12850      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12851      *                        Roo.resizable.
12852      */
12853     resizable : false,
12854      /**
12855      * @cfg {Number} height (in pixels)
12856      */   
12857     height: 300,
12858    /**
12859      * @cfg {Number} width (in pixels)
12860      */   
12861     width: 500,
12862     
12863     /**
12864      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12865      * 
12866      */
12867     stylesheets: false,
12868     
12869     // id of frame..
12870     frameId: false,
12871     
12872     // private properties
12873     validationEvent : false,
12874     deferHeight: true,
12875     initialized : false,
12876     activated : false,
12877     sourceEditMode : false,
12878     onFocus : Roo.emptyFn,
12879     iframePad:3,
12880     hideMode:'offsets',
12881     
12882      
12883     
12884
12885     /**
12886      * Protected method that will not generally be called directly. It
12887      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12888      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12889      */
12890     getDocMarkup : function(){
12891         // body styles..
12892         var st = '';
12893         Roo.log(this.stylesheets);
12894         
12895         // inherit styels from page...?? 
12896         if (this.stylesheets === false) {
12897             
12898             Roo.get(document.head).select('style').each(function(node) {
12899                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12900             });
12901             
12902             Roo.get(document.head).select('link').each(function(node) { 
12903                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12904             });
12905             
12906         } else if (!this.stylesheets.length) {
12907                 // simple..
12908                 st = '<style type="text/css">' +
12909                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12910                    '</style>';
12911         } else {
12912             Roo.each(this.stylesheets, function(s) {
12913                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12914             });
12915             
12916         }
12917         
12918         st +=  '<style type="text/css">' +
12919             'IMG { cursor: pointer } ' +
12920         '</style>';
12921
12922         
12923         return '<html><head>' + st  +
12924             //<style type="text/css">' +
12925             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12926             //'</style>' +
12927             ' </head><body class="roo-htmleditor-body"></body></html>';
12928     },
12929
12930     // private
12931     onRender : function(ct, position)
12932     {
12933         var _t = this;
12934         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12935         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12936         
12937         
12938         this.el.dom.style.border = '0 none';
12939         this.el.dom.setAttribute('tabIndex', -1);
12940         this.el.addClass('x-hidden hide');
12941         
12942         
12943         
12944         if(Roo.isIE){ // fix IE 1px bogus margin
12945             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12946         }
12947        
12948         
12949         this.frameId = Roo.id();
12950         
12951          
12952         
12953         var iframe = this.owner.wrap.createChild({
12954             tag: 'iframe',
12955             cls: 'form-control', // bootstrap..
12956             id: this.frameId,
12957             name: this.frameId,
12958             frameBorder : 'no',
12959             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
12960         }, this.el
12961         );
12962         
12963         
12964         this.iframe = iframe.dom;
12965
12966          this.assignDocWin();
12967         
12968         this.doc.designMode = 'on';
12969        
12970         this.doc.open();
12971         this.doc.write(this.getDocMarkup());
12972         this.doc.close();
12973
12974         
12975         var task = { // must defer to wait for browser to be ready
12976             run : function(){
12977                 //console.log("run task?" + this.doc.readyState);
12978                 this.assignDocWin();
12979                 if(this.doc.body || this.doc.readyState == 'complete'){
12980                     try {
12981                         this.doc.designMode="on";
12982                     } catch (e) {
12983                         return;
12984                     }
12985                     Roo.TaskMgr.stop(task);
12986                     this.initEditor.defer(10, this);
12987                 }
12988             },
12989             interval : 10,
12990             duration: 10000,
12991             scope: this
12992         };
12993         Roo.TaskMgr.start(task);
12994
12995         
12996          
12997     },
12998
12999     // private
13000     onResize : function(w, h)
13001     {
13002          Roo.log('resize: ' +w + ',' + h );
13003         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13004         if(!this.iframe){
13005             return;
13006         }
13007         if(typeof w == 'number'){
13008             
13009             this.iframe.style.width = w + 'px';
13010         }
13011         if(typeof h == 'number'){
13012             
13013             this.iframe.style.height = h + 'px';
13014             if(this.doc){
13015                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13016             }
13017         }
13018         
13019     },
13020
13021     /**
13022      * Toggles the editor between standard and source edit mode.
13023      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13024      */
13025     toggleSourceEdit : function(sourceEditMode){
13026         
13027         this.sourceEditMode = sourceEditMode === true;
13028         
13029         if(this.sourceEditMode){
13030  
13031             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13032             
13033         }else{
13034             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13035             //this.iframe.className = '';
13036             this.deferFocus();
13037         }
13038         //this.setSize(this.owner.wrap.getSize());
13039         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13040     },
13041
13042     
13043   
13044
13045     /**
13046      * Protected method that will not generally be called directly. If you need/want
13047      * custom HTML cleanup, this is the method you should override.
13048      * @param {String} html The HTML to be cleaned
13049      * return {String} The cleaned HTML
13050      */
13051     cleanHtml : function(html){
13052         html = String(html);
13053         if(html.length > 5){
13054             if(Roo.isSafari){ // strip safari nonsense
13055                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13056             }
13057         }
13058         if(html == '&nbsp;'){
13059             html = '';
13060         }
13061         return html;
13062     },
13063
13064     /**
13065      * HTML Editor -> Textarea
13066      * Protected method that will not generally be called directly. Syncs the contents
13067      * of the editor iframe with the textarea.
13068      */
13069     syncValue : function(){
13070         if(this.initialized){
13071             var bd = (this.doc.body || this.doc.documentElement);
13072             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13073             var html = bd.innerHTML;
13074             if(Roo.isSafari){
13075                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13076                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13077                 if(m && m[1]){
13078                     html = '<div style="'+m[0]+'">' + html + '</div>';
13079                 }
13080             }
13081             html = this.cleanHtml(html);
13082             // fix up the special chars.. normaly like back quotes in word...
13083             // however we do not want to do this with chinese..
13084             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13085                 var cc = b.charCodeAt();
13086                 if (
13087                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13088                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13089                     (cc >= 0xf900 && cc < 0xfb00 )
13090                 ) {
13091                         return b;
13092                 }
13093                 return "&#"+cc+";" 
13094             });
13095             if(this.owner.fireEvent('beforesync', this, html) !== false){
13096                 this.el.dom.value = html;
13097                 this.owner.fireEvent('sync', this, html);
13098             }
13099         }
13100     },
13101
13102     /**
13103      * Protected method that will not generally be called directly. Pushes the value of the textarea
13104      * into the iframe editor.
13105      */
13106     pushValue : function(){
13107         if(this.initialized){
13108             var v = this.el.dom.value.trim();
13109             
13110 //            if(v.length < 1){
13111 //                v = '&#160;';
13112 //            }
13113             
13114             if(this.owner.fireEvent('beforepush', this, v) !== false){
13115                 var d = (this.doc.body || this.doc.documentElement);
13116                 d.innerHTML = v;
13117                 this.cleanUpPaste();
13118                 this.el.dom.value = d.innerHTML;
13119                 this.owner.fireEvent('push', this, v);
13120             }
13121         }
13122     },
13123
13124     // private
13125     deferFocus : function(){
13126         this.focus.defer(10, this);
13127     },
13128
13129     // doc'ed in Field
13130     focus : function(){
13131         if(this.win && !this.sourceEditMode){
13132             this.win.focus();
13133         }else{
13134             this.el.focus();
13135         }
13136     },
13137     
13138     assignDocWin: function()
13139     {
13140         var iframe = this.iframe;
13141         
13142          if(Roo.isIE){
13143             this.doc = iframe.contentWindow.document;
13144             this.win = iframe.contentWindow;
13145         } else {
13146             if (!Roo.get(this.frameId)) {
13147                 return;
13148             }
13149             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13150             this.win = Roo.get(this.frameId).dom.contentWindow;
13151         }
13152     },
13153     
13154     // private
13155     initEditor : function(){
13156         //console.log("INIT EDITOR");
13157         this.assignDocWin();
13158         
13159         
13160         
13161         this.doc.designMode="on";
13162         this.doc.open();
13163         this.doc.write(this.getDocMarkup());
13164         this.doc.close();
13165         
13166         var dbody = (this.doc.body || this.doc.documentElement);
13167         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13168         // this copies styles from the containing element into thsi one..
13169         // not sure why we need all of this..
13170         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13171         ss['background-attachment'] = 'fixed'; // w3c
13172         dbody.bgProperties = 'fixed'; // ie
13173         Roo.DomHelper.applyStyles(dbody, ss);
13174         Roo.EventManager.on(this.doc, {
13175             //'mousedown': this.onEditorEvent,
13176             'mouseup': this.onEditorEvent,
13177             'dblclick': this.onEditorEvent,
13178             'click': this.onEditorEvent,
13179             'keyup': this.onEditorEvent,
13180             buffer:100,
13181             scope: this
13182         });
13183         if(Roo.isGecko){
13184             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13185         }
13186         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13187             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13188         }
13189         this.initialized = true;
13190
13191         this.owner.fireEvent('initialize', this);
13192         this.pushValue();
13193     },
13194
13195     // private
13196     onDestroy : function(){
13197         
13198         
13199         
13200         if(this.rendered){
13201             
13202             //for (var i =0; i < this.toolbars.length;i++) {
13203             //    // fixme - ask toolbars for heights?
13204             //    this.toolbars[i].onDestroy();
13205            // }
13206             
13207             //this.wrap.dom.innerHTML = '';
13208             //this.wrap.remove();
13209         }
13210     },
13211
13212     // private
13213     onFirstFocus : function(){
13214         
13215         this.assignDocWin();
13216         
13217         
13218         this.activated = true;
13219          
13220     
13221         if(Roo.isGecko){ // prevent silly gecko errors
13222             this.win.focus();
13223             var s = this.win.getSelection();
13224             if(!s.focusNode || s.focusNode.nodeType != 3){
13225                 var r = s.getRangeAt(0);
13226                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13227                 r.collapse(true);
13228                 this.deferFocus();
13229             }
13230             try{
13231                 this.execCmd('useCSS', true);
13232                 this.execCmd('styleWithCSS', false);
13233             }catch(e){}
13234         }
13235         this.owner.fireEvent('activate', this);
13236     },
13237
13238     // private
13239     adjustFont: function(btn){
13240         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13241         //if(Roo.isSafari){ // safari
13242         //    adjust *= 2;
13243        // }
13244         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13245         if(Roo.isSafari){ // safari
13246             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13247             v =  (v < 10) ? 10 : v;
13248             v =  (v > 48) ? 48 : v;
13249             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13250             
13251         }
13252         
13253         
13254         v = Math.max(1, v+adjust);
13255         
13256         this.execCmd('FontSize', v  );
13257     },
13258
13259     onEditorEvent : function(e){
13260         this.owner.fireEvent('editorevent', this, e);
13261       //  this.updateToolbar();
13262         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13263     },
13264
13265     insertTag : function(tg)
13266     {
13267         // could be a bit smarter... -> wrap the current selected tRoo..
13268         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13269             
13270             range = this.createRange(this.getSelection());
13271             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13272             wrappingNode.appendChild(range.extractContents());
13273             range.insertNode(wrappingNode);
13274
13275             return;
13276             
13277             
13278             
13279         }
13280         this.execCmd("formatblock",   tg);
13281         
13282     },
13283     
13284     insertText : function(txt)
13285     {
13286         
13287         
13288         var range = this.createRange();
13289         range.deleteContents();
13290                //alert(Sender.getAttribute('label'));
13291                
13292         range.insertNode(this.doc.createTextNode(txt));
13293     } ,
13294     
13295      
13296
13297     /**
13298      * Executes a Midas editor command on the editor document and performs necessary focus and
13299      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13300      * @param {String} cmd The Midas command
13301      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13302      */
13303     relayCmd : function(cmd, value){
13304         this.win.focus();
13305         this.execCmd(cmd, value);
13306         this.owner.fireEvent('editorevent', this);
13307         //this.updateToolbar();
13308         this.owner.deferFocus();
13309     },
13310
13311     /**
13312      * Executes a Midas editor command directly on the editor document.
13313      * For visual commands, you should use {@link #relayCmd} instead.
13314      * <b>This should only be called after the editor is initialized.</b>
13315      * @param {String} cmd The Midas command
13316      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13317      */
13318     execCmd : function(cmd, value){
13319         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13320         this.syncValue();
13321     },
13322  
13323  
13324    
13325     /**
13326      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13327      * to insert tRoo.
13328      * @param {String} text | dom node.. 
13329      */
13330     insertAtCursor : function(text)
13331     {
13332         
13333         
13334         
13335         if(!this.activated){
13336             return;
13337         }
13338         /*
13339         if(Roo.isIE){
13340             this.win.focus();
13341             var r = this.doc.selection.createRange();
13342             if(r){
13343                 r.collapse(true);
13344                 r.pasteHTML(text);
13345                 this.syncValue();
13346                 this.deferFocus();
13347             
13348             }
13349             return;
13350         }
13351         */
13352         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13353             this.win.focus();
13354             
13355             
13356             // from jquery ui (MIT licenced)
13357             var range, node;
13358             var win = this.win;
13359             
13360             if (win.getSelection && win.getSelection().getRangeAt) {
13361                 range = win.getSelection().getRangeAt(0);
13362                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13363                 range.insertNode(node);
13364             } else if (win.document.selection && win.document.selection.createRange) {
13365                 // no firefox support
13366                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13367                 win.document.selection.createRange().pasteHTML(txt);
13368             } else {
13369                 // no firefox support
13370                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13371                 this.execCmd('InsertHTML', txt);
13372             } 
13373             
13374             this.syncValue();
13375             
13376             this.deferFocus();
13377         }
13378     },
13379  // private
13380     mozKeyPress : function(e){
13381         if(e.ctrlKey){
13382             var c = e.getCharCode(), cmd;
13383           
13384             if(c > 0){
13385                 c = String.fromCharCode(c).toLowerCase();
13386                 switch(c){
13387                     case 'b':
13388                         cmd = 'bold';
13389                         break;
13390                     case 'i':
13391                         cmd = 'italic';
13392                         break;
13393                     
13394                     case 'u':
13395                         cmd = 'underline';
13396                         break;
13397                     
13398                     case 'v':
13399                         this.cleanUpPaste.defer(100, this);
13400                         return;
13401                         
13402                 }
13403                 if(cmd){
13404                     this.win.focus();
13405                     this.execCmd(cmd);
13406                     this.deferFocus();
13407                     e.preventDefault();
13408                 }
13409                 
13410             }
13411         }
13412     },
13413
13414     // private
13415     fixKeys : function(){ // load time branching for fastest keydown performance
13416         if(Roo.isIE){
13417             return function(e){
13418                 var k = e.getKey(), r;
13419                 if(k == e.TAB){
13420                     e.stopEvent();
13421                     r = this.doc.selection.createRange();
13422                     if(r){
13423                         r.collapse(true);
13424                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13425                         this.deferFocus();
13426                     }
13427                     return;
13428                 }
13429                 
13430                 if(k == e.ENTER){
13431                     r = this.doc.selection.createRange();
13432                     if(r){
13433                         var target = r.parentElement();
13434                         if(!target || target.tagName.toLowerCase() != 'li'){
13435                             e.stopEvent();
13436                             r.pasteHTML('<br />');
13437                             r.collapse(false);
13438                             r.select();
13439                         }
13440                     }
13441                 }
13442                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13443                     this.cleanUpPaste.defer(100, this);
13444                     return;
13445                 }
13446                 
13447                 
13448             };
13449         }else if(Roo.isOpera){
13450             return function(e){
13451                 var k = e.getKey();
13452                 if(k == e.TAB){
13453                     e.stopEvent();
13454                     this.win.focus();
13455                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13456                     this.deferFocus();
13457                 }
13458                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13459                     this.cleanUpPaste.defer(100, this);
13460                     return;
13461                 }
13462                 
13463             };
13464         }else if(Roo.isSafari){
13465             return function(e){
13466                 var k = e.getKey();
13467                 
13468                 if(k == e.TAB){
13469                     e.stopEvent();
13470                     this.execCmd('InsertText','\t');
13471                     this.deferFocus();
13472                     return;
13473                 }
13474                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13475                     this.cleanUpPaste.defer(100, this);
13476                     return;
13477                 }
13478                 
13479              };
13480         }
13481     }(),
13482     
13483     getAllAncestors: function()
13484     {
13485         var p = this.getSelectedNode();
13486         var a = [];
13487         if (!p) {
13488             a.push(p); // push blank onto stack..
13489             p = this.getParentElement();
13490         }
13491         
13492         
13493         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13494             a.push(p);
13495             p = p.parentNode;
13496         }
13497         a.push(this.doc.body);
13498         return a;
13499     },
13500     lastSel : false,
13501     lastSelNode : false,
13502     
13503     
13504     getSelection : function() 
13505     {
13506         this.assignDocWin();
13507         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13508     },
13509     
13510     getSelectedNode: function() 
13511     {
13512         // this may only work on Gecko!!!
13513         
13514         // should we cache this!!!!
13515         
13516         
13517         
13518          
13519         var range = this.createRange(this.getSelection()).cloneRange();
13520         
13521         if (Roo.isIE) {
13522             var parent = range.parentElement();
13523             while (true) {
13524                 var testRange = range.duplicate();
13525                 testRange.moveToElementText(parent);
13526                 if (testRange.inRange(range)) {
13527                     break;
13528                 }
13529                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13530                     break;
13531                 }
13532                 parent = parent.parentElement;
13533             }
13534             return parent;
13535         }
13536         
13537         // is ancestor a text element.
13538         var ac =  range.commonAncestorContainer;
13539         if (ac.nodeType == 3) {
13540             ac = ac.parentNode;
13541         }
13542         
13543         var ar = ac.childNodes;
13544          
13545         var nodes = [];
13546         var other_nodes = [];
13547         var has_other_nodes = false;
13548         for (var i=0;i<ar.length;i++) {
13549             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13550                 continue;
13551             }
13552             // fullly contained node.
13553             
13554             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13555                 nodes.push(ar[i]);
13556                 continue;
13557             }
13558             
13559             // probably selected..
13560             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13561                 other_nodes.push(ar[i]);
13562                 continue;
13563             }
13564             // outer..
13565             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13566                 continue;
13567             }
13568             
13569             
13570             has_other_nodes = true;
13571         }
13572         if (!nodes.length && other_nodes.length) {
13573             nodes= other_nodes;
13574         }
13575         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13576             return false;
13577         }
13578         
13579         return nodes[0];
13580     },
13581     createRange: function(sel)
13582     {
13583         // this has strange effects when using with 
13584         // top toolbar - not sure if it's a great idea.
13585         //this.editor.contentWindow.focus();
13586         if (typeof sel != "undefined") {
13587             try {
13588                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13589             } catch(e) {
13590                 return this.doc.createRange();
13591             }
13592         } else {
13593             return this.doc.createRange();
13594         }
13595     },
13596     getParentElement: function()
13597     {
13598         
13599         this.assignDocWin();
13600         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13601         
13602         var range = this.createRange(sel);
13603          
13604         try {
13605             var p = range.commonAncestorContainer;
13606             while (p.nodeType == 3) { // text node
13607                 p = p.parentNode;
13608             }
13609             return p;
13610         } catch (e) {
13611             return null;
13612         }
13613     
13614     },
13615     /***
13616      *
13617      * Range intersection.. the hard stuff...
13618      *  '-1' = before
13619      *  '0' = hits..
13620      *  '1' = after.
13621      *         [ -- selected range --- ]
13622      *   [fail]                        [fail]
13623      *
13624      *    basically..
13625      *      if end is before start or  hits it. fail.
13626      *      if start is after end or hits it fail.
13627      *
13628      *   if either hits (but other is outside. - then it's not 
13629      *   
13630      *    
13631      **/
13632     
13633     
13634     // @see http://www.thismuchiknow.co.uk/?p=64.
13635     rangeIntersectsNode : function(range, node)
13636     {
13637         var nodeRange = node.ownerDocument.createRange();
13638         try {
13639             nodeRange.selectNode(node);
13640         } catch (e) {
13641             nodeRange.selectNodeContents(node);
13642         }
13643     
13644         var rangeStartRange = range.cloneRange();
13645         rangeStartRange.collapse(true);
13646     
13647         var rangeEndRange = range.cloneRange();
13648         rangeEndRange.collapse(false);
13649     
13650         var nodeStartRange = nodeRange.cloneRange();
13651         nodeStartRange.collapse(true);
13652     
13653         var nodeEndRange = nodeRange.cloneRange();
13654         nodeEndRange.collapse(false);
13655     
13656         return rangeStartRange.compareBoundaryPoints(
13657                  Range.START_TO_START, nodeEndRange) == -1 &&
13658                rangeEndRange.compareBoundaryPoints(
13659                  Range.START_TO_START, nodeStartRange) == 1;
13660         
13661          
13662     },
13663     rangeCompareNode : function(range, node)
13664     {
13665         var nodeRange = node.ownerDocument.createRange();
13666         try {
13667             nodeRange.selectNode(node);
13668         } catch (e) {
13669             nodeRange.selectNodeContents(node);
13670         }
13671         
13672         
13673         range.collapse(true);
13674     
13675         nodeRange.collapse(true);
13676      
13677         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13678         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13679          
13680         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13681         
13682         var nodeIsBefore   =  ss == 1;
13683         var nodeIsAfter    = ee == -1;
13684         
13685         if (nodeIsBefore && nodeIsAfter)
13686             return 0; // outer
13687         if (!nodeIsBefore && nodeIsAfter)
13688             return 1; //right trailed.
13689         
13690         if (nodeIsBefore && !nodeIsAfter)
13691             return 2;  // left trailed.
13692         // fully contined.
13693         return 3;
13694     },
13695
13696     // private? - in a new class?
13697     cleanUpPaste :  function()
13698     {
13699         // cleans up the whole document..
13700          Roo.log('cleanuppaste');
13701         this.cleanUpChildren(this.doc.body);
13702         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13703         if (clean != this.doc.body.innerHTML) {
13704             this.doc.body.innerHTML = clean;
13705         }
13706         
13707     },
13708     
13709     cleanWordChars : function(input) {// change the chars to hex code
13710         var he = Roo.HtmlEditorCore;
13711         
13712         var output = input;
13713         Roo.each(he.swapCodes, function(sw) { 
13714             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13715             
13716             output = output.replace(swapper, sw[1]);
13717         });
13718         
13719         return output;
13720     },
13721     
13722     
13723     cleanUpChildren : function (n)
13724     {
13725         if (!n.childNodes.length) {
13726             return;
13727         }
13728         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13729            this.cleanUpChild(n.childNodes[i]);
13730         }
13731     },
13732     
13733     
13734         
13735     
13736     cleanUpChild : function (node)
13737     {
13738         var ed = this;
13739         //console.log(node);
13740         if (node.nodeName == "#text") {
13741             // clean up silly Windows -- stuff?
13742             return; 
13743         }
13744         if (node.nodeName == "#comment") {
13745             node.parentNode.removeChild(node);
13746             // clean up silly Windows -- stuff?
13747             return; 
13748         }
13749         
13750         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13751             // remove node.
13752             node.parentNode.removeChild(node);
13753             return;
13754             
13755         }
13756         
13757         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13758         
13759         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13760         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13761         
13762         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13763         //    remove_keep_children = true;
13764         //}
13765         
13766         if (remove_keep_children) {
13767             this.cleanUpChildren(node);
13768             // inserts everything just before this node...
13769             while (node.childNodes.length) {
13770                 var cn = node.childNodes[0];
13771                 node.removeChild(cn);
13772                 node.parentNode.insertBefore(cn, node);
13773             }
13774             node.parentNode.removeChild(node);
13775             return;
13776         }
13777         
13778         if (!node.attributes || !node.attributes.length) {
13779             this.cleanUpChildren(node);
13780             return;
13781         }
13782         
13783         function cleanAttr(n,v)
13784         {
13785             
13786             if (v.match(/^\./) || v.match(/^\//)) {
13787                 return;
13788             }
13789             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13790                 return;
13791             }
13792             if (v.match(/^#/)) {
13793                 return;
13794             }
13795 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13796             node.removeAttribute(n);
13797             
13798         }
13799         
13800         function cleanStyle(n,v)
13801         {
13802             if (v.match(/expression/)) { //XSS?? should we even bother..
13803                 node.removeAttribute(n);
13804                 return;
13805             }
13806             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13807             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13808             
13809             
13810             var parts = v.split(/;/);
13811             var clean = [];
13812             
13813             Roo.each(parts, function(p) {
13814                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13815                 if (!p.length) {
13816                     return true;
13817                 }
13818                 var l = p.split(':').shift().replace(/\s+/g,'');
13819                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13820                 
13821                 
13822                 if ( cblack.indexOf(l) > -1) {
13823 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13824                     //node.removeAttribute(n);
13825                     return true;
13826                 }
13827                 //Roo.log()
13828                 // only allow 'c whitelisted system attributes'
13829                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13830 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13831                     //node.removeAttribute(n);
13832                     return true;
13833                 }
13834                 
13835                 
13836                  
13837                 
13838                 clean.push(p);
13839                 return true;
13840             });
13841             if (clean.length) { 
13842                 node.setAttribute(n, clean.join(';'));
13843             } else {
13844                 node.removeAttribute(n);
13845             }
13846             
13847         }
13848         
13849         
13850         for (var i = node.attributes.length-1; i > -1 ; i--) {
13851             var a = node.attributes[i];
13852             //console.log(a);
13853             
13854             if (a.name.toLowerCase().substr(0,2)=='on')  {
13855                 node.removeAttribute(a.name);
13856                 continue;
13857             }
13858             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13859                 node.removeAttribute(a.name);
13860                 continue;
13861             }
13862             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13863                 cleanAttr(a.name,a.value); // fixme..
13864                 continue;
13865             }
13866             if (a.name == 'style') {
13867                 cleanStyle(a.name,a.value);
13868                 continue;
13869             }
13870             /// clean up MS crap..
13871             // tecnically this should be a list of valid class'es..
13872             
13873             
13874             if (a.name == 'class') {
13875                 if (a.value.match(/^Mso/)) {
13876                     node.className = '';
13877                 }
13878                 
13879                 if (a.value.match(/body/)) {
13880                     node.className = '';
13881                 }
13882                 continue;
13883             }
13884             
13885             // style cleanup!?
13886             // class cleanup?
13887             
13888         }
13889         
13890         
13891         this.cleanUpChildren(node);
13892         
13893         
13894     }
13895     
13896     
13897     // hide stuff that is not compatible
13898     /**
13899      * @event blur
13900      * @hide
13901      */
13902     /**
13903      * @event change
13904      * @hide
13905      */
13906     /**
13907      * @event focus
13908      * @hide
13909      */
13910     /**
13911      * @event specialkey
13912      * @hide
13913      */
13914     /**
13915      * @cfg {String} fieldClass @hide
13916      */
13917     /**
13918      * @cfg {String} focusClass @hide
13919      */
13920     /**
13921      * @cfg {String} autoCreate @hide
13922      */
13923     /**
13924      * @cfg {String} inputType @hide
13925      */
13926     /**
13927      * @cfg {String} invalidClass @hide
13928      */
13929     /**
13930      * @cfg {String} invalidText @hide
13931      */
13932     /**
13933      * @cfg {String} msgFx @hide
13934      */
13935     /**
13936      * @cfg {String} validateOnBlur @hide
13937      */
13938 });
13939
13940 Roo.HtmlEditorCore.white = [
13941         'area', 'br', 'img', 'input', 'hr', 'wbr',
13942         
13943        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13944        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13945        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13946        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13947        'table',   'ul',         'xmp', 
13948        
13949        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13950       'thead',   'tr', 
13951      
13952       'dir', 'menu', 'ol', 'ul', 'dl',
13953        
13954       'embed',  'object'
13955 ];
13956
13957
13958 Roo.HtmlEditorCore.black = [
13959     //    'embed',  'object', // enable - backend responsiblity to clean thiese
13960         'applet', // 
13961         'base',   'basefont', 'bgsound', 'blink',  'body', 
13962         'frame',  'frameset', 'head',    'html',   'ilayer', 
13963         'iframe', 'layer',  'link',     'meta',    'object',   
13964         'script', 'style' ,'title',  'xml' // clean later..
13965 ];
13966 Roo.HtmlEditorCore.clean = [
13967     'script', 'style', 'title', 'xml'
13968 ];
13969 Roo.HtmlEditorCore.remove = [
13970     'font'
13971 ];
13972 // attributes..
13973
13974 Roo.HtmlEditorCore.ablack = [
13975     'on'
13976 ];
13977     
13978 Roo.HtmlEditorCore.aclean = [ 
13979     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
13980 ];
13981
13982 // protocols..
13983 Roo.HtmlEditorCore.pwhite= [
13984         'http',  'https',  'mailto'
13985 ];
13986
13987 // white listed style attributes.
13988 Roo.HtmlEditorCore.cwhite= [
13989       //  'text-align', /// default is to allow most things..
13990       
13991          
13992 //        'font-size'//??
13993 ];
13994
13995 // black listed style attributes.
13996 Roo.HtmlEditorCore.cblack= [
13997       //  'font-size' -- this can be set by the project 
13998 ];
13999
14000
14001 Roo.HtmlEditorCore.swapCodes   =[ 
14002     [    8211, "--" ], 
14003     [    8212, "--" ], 
14004     [    8216,  "'" ],  
14005     [    8217, "'" ],  
14006     [    8220, '"' ],  
14007     [    8221, '"' ],  
14008     [    8226, "*" ],  
14009     [    8230, "..." ]
14010 ]; 
14011
14012     /*
14013  * - LGPL
14014  *
14015  * HtmlEditor
14016  * 
14017  */
14018
14019 /**
14020  * @class Roo.bootstrap.HtmlEditor
14021  * @extends Roo.bootstrap.TextArea
14022  * Bootstrap HtmlEditor class
14023
14024  * @constructor
14025  * Create a new HtmlEditor
14026  * @param {Object} config The config object
14027  */
14028
14029 Roo.bootstrap.HtmlEditor = function(config){
14030     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14031     if (!this.toolbars) {
14032         this.toolbars = [];
14033     }
14034     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14035     this.addEvents({
14036             /**
14037              * @event initialize
14038              * Fires when the editor is fully initialized (including the iframe)
14039              * @param {HtmlEditor} this
14040              */
14041             initialize: true,
14042             /**
14043              * @event activate
14044              * Fires when the editor is first receives the focus. Any insertion must wait
14045              * until after this event.
14046              * @param {HtmlEditor} this
14047              */
14048             activate: true,
14049              /**
14050              * @event beforesync
14051              * Fires before the textarea is updated with content from the editor iframe. Return false
14052              * to cancel the sync.
14053              * @param {HtmlEditor} this
14054              * @param {String} html
14055              */
14056             beforesync: true,
14057              /**
14058              * @event beforepush
14059              * Fires before the iframe editor is updated with content from the textarea. Return false
14060              * to cancel the push.
14061              * @param {HtmlEditor} this
14062              * @param {String} html
14063              */
14064             beforepush: true,
14065              /**
14066              * @event sync
14067              * Fires when the textarea is updated with content from the editor iframe.
14068              * @param {HtmlEditor} this
14069              * @param {String} html
14070              */
14071             sync: true,
14072              /**
14073              * @event push
14074              * Fires when the iframe editor is updated with content from the textarea.
14075              * @param {HtmlEditor} this
14076              * @param {String} html
14077              */
14078             push: true,
14079              /**
14080              * @event editmodechange
14081              * Fires when the editor switches edit modes
14082              * @param {HtmlEditor} this
14083              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14084              */
14085             editmodechange: true,
14086             /**
14087              * @event editorevent
14088              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14089              * @param {HtmlEditor} this
14090              */
14091             editorevent: true,
14092             /**
14093              * @event firstfocus
14094              * Fires when on first focus - needed by toolbars..
14095              * @param {HtmlEditor} this
14096              */
14097             firstfocus: true,
14098             /**
14099              * @event autosave
14100              * Auto save the htmlEditor value as a file into Events
14101              * @param {HtmlEditor} this
14102              */
14103             autosave: true,
14104             /**
14105              * @event savedpreview
14106              * preview the saved version of htmlEditor
14107              * @param {HtmlEditor} this
14108              */
14109             savedpreview: true
14110         });
14111 };
14112
14113
14114 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14115     
14116     
14117       /**
14118      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14119      */
14120     toolbars : false,
14121    
14122      /**
14123      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14124      *                        Roo.resizable.
14125      */
14126     resizable : false,
14127      /**
14128      * @cfg {Number} height (in pixels)
14129      */   
14130     height: 300,
14131    /**
14132      * @cfg {Number} width (in pixels)
14133      */   
14134     width: false,
14135     
14136     /**
14137      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14138      * 
14139      */
14140     stylesheets: false,
14141     
14142     // id of frame..
14143     frameId: false,
14144     
14145     // private properties
14146     validationEvent : false,
14147     deferHeight: true,
14148     initialized : false,
14149     activated : false,
14150     
14151     onFocus : Roo.emptyFn,
14152     iframePad:3,
14153     hideMode:'offsets',
14154     
14155     
14156     tbContainer : false,
14157     
14158     toolbarContainer :function() {
14159         return this.wrap.select('.x-html-editor-tb',true).first();
14160     },
14161
14162     /**
14163      * Protected method that will not generally be called directly. It
14164      * is called when the editor creates its toolbar. Override this method if you need to
14165      * add custom toolbar buttons.
14166      * @param {HtmlEditor} editor
14167      */
14168     createToolbar : function(){
14169         
14170         Roo.log("create toolbars");
14171         
14172         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14173         this.toolbars[0].render(this.toolbarContainer());
14174         
14175         return;
14176         
14177 //        if (!editor.toolbars || !editor.toolbars.length) {
14178 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14179 //        }
14180 //        
14181 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14182 //            editor.toolbars[i] = Roo.factory(
14183 //                    typeof(editor.toolbars[i]) == 'string' ?
14184 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14185 //                Roo.bootstrap.HtmlEditor);
14186 //            editor.toolbars[i].init(editor);
14187 //        }
14188     },
14189
14190      
14191     // private
14192     onRender : function(ct, position)
14193     {
14194        // Roo.log("Call onRender: " + this.xtype);
14195         var _t = this;
14196         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14197       
14198         this.wrap = this.inputEl().wrap({
14199             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14200         });
14201         
14202         this.editorcore.onRender(ct, position);
14203          
14204         if (this.resizable) {
14205             this.resizeEl = new Roo.Resizable(this.wrap, {
14206                 pinned : true,
14207                 wrap: true,
14208                 dynamic : true,
14209                 minHeight : this.height,
14210                 height: this.height,
14211                 handles : this.resizable,
14212                 width: this.width,
14213                 listeners : {
14214                     resize : function(r, w, h) {
14215                         _t.onResize(w,h); // -something
14216                     }
14217                 }
14218             });
14219             
14220         }
14221         this.createToolbar(this);
14222        
14223         
14224         if(!this.width && this.resizable){
14225             this.setSize(this.wrap.getSize());
14226         }
14227         if (this.resizeEl) {
14228             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14229             // should trigger onReize..
14230         }
14231         
14232     },
14233
14234     // private
14235     onResize : function(w, h)
14236     {
14237         Roo.log('resize: ' +w + ',' + h );
14238         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14239         var ew = false;
14240         var eh = false;
14241         
14242         if(this.inputEl() ){
14243             if(typeof w == 'number'){
14244                 var aw = w - this.wrap.getFrameWidth('lr');
14245                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14246                 ew = aw;
14247             }
14248             if(typeof h == 'number'){
14249                  var tbh = -11;  // fixme it needs to tool bar size!
14250                 for (var i =0; i < this.toolbars.length;i++) {
14251                     // fixme - ask toolbars for heights?
14252                     tbh += this.toolbars[i].el.getHeight();
14253                     //if (this.toolbars[i].footer) {
14254                     //    tbh += this.toolbars[i].footer.el.getHeight();
14255                     //}
14256                 }
14257               
14258                 
14259                 
14260                 
14261                 
14262                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14263                 ah -= 5; // knock a few pixes off for look..
14264                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14265                 var eh = ah;
14266             }
14267         }
14268         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14269         this.editorcore.onResize(ew,eh);
14270         
14271     },
14272
14273     /**
14274      * Toggles the editor between standard and source edit mode.
14275      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14276      */
14277     toggleSourceEdit : function(sourceEditMode)
14278     {
14279         this.editorcore.toggleSourceEdit(sourceEditMode);
14280         
14281         if(this.editorcore.sourceEditMode){
14282             Roo.log('editor - showing textarea');
14283             
14284 //            Roo.log('in');
14285 //            Roo.log(this.syncValue());
14286             this.syncValue();
14287             this.inputEl().removeClass('hide');
14288             this.inputEl().dom.removeAttribute('tabIndex');
14289             this.inputEl().focus();
14290         }else{
14291             Roo.log('editor - hiding textarea');
14292 //            Roo.log('out')
14293 //            Roo.log(this.pushValue()); 
14294             this.pushValue();
14295             
14296             this.inputEl().addClass('hide');
14297             this.inputEl().dom.setAttribute('tabIndex', -1);
14298             //this.deferFocus();
14299         }
14300          
14301         if(this.resizable){
14302             this.setSize(this.wrap.getSize());
14303         }
14304         
14305         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14306     },
14307  
14308     // private (for BoxComponent)
14309     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14310
14311     // private (for BoxComponent)
14312     getResizeEl : function(){
14313         return this.wrap;
14314     },
14315
14316     // private (for BoxComponent)
14317     getPositionEl : function(){
14318         return this.wrap;
14319     },
14320
14321     // private
14322     initEvents : function(){
14323         this.originalValue = this.getValue();
14324     },
14325
14326 //    /**
14327 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14328 //     * @method
14329 //     */
14330 //    markInvalid : Roo.emptyFn,
14331 //    /**
14332 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14333 //     * @method
14334 //     */
14335 //    clearInvalid : Roo.emptyFn,
14336
14337     setValue : function(v){
14338         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14339         this.editorcore.pushValue();
14340     },
14341
14342      
14343     // private
14344     deferFocus : function(){
14345         this.focus.defer(10, this);
14346     },
14347
14348     // doc'ed in Field
14349     focus : function(){
14350         this.editorcore.focus();
14351         
14352     },
14353       
14354
14355     // private
14356     onDestroy : function(){
14357         
14358         
14359         
14360         if(this.rendered){
14361             
14362             for (var i =0; i < this.toolbars.length;i++) {
14363                 // fixme - ask toolbars for heights?
14364                 this.toolbars[i].onDestroy();
14365             }
14366             
14367             this.wrap.dom.innerHTML = '';
14368             this.wrap.remove();
14369         }
14370     },
14371
14372     // private
14373     onFirstFocus : function(){
14374         //Roo.log("onFirstFocus");
14375         this.editorcore.onFirstFocus();
14376          for (var i =0; i < this.toolbars.length;i++) {
14377             this.toolbars[i].onFirstFocus();
14378         }
14379         
14380     },
14381     
14382     // private
14383     syncValue : function()
14384     {   
14385         this.editorcore.syncValue();
14386     },
14387     
14388     pushValue : function()
14389     {   
14390         this.editorcore.pushValue();
14391     }
14392      
14393     
14394     // hide stuff that is not compatible
14395     /**
14396      * @event blur
14397      * @hide
14398      */
14399     /**
14400      * @event change
14401      * @hide
14402      */
14403     /**
14404      * @event focus
14405      * @hide
14406      */
14407     /**
14408      * @event specialkey
14409      * @hide
14410      */
14411     /**
14412      * @cfg {String} fieldClass @hide
14413      */
14414     /**
14415      * @cfg {String} focusClass @hide
14416      */
14417     /**
14418      * @cfg {String} autoCreate @hide
14419      */
14420     /**
14421      * @cfg {String} inputType @hide
14422      */
14423     /**
14424      * @cfg {String} invalidClass @hide
14425      */
14426     /**
14427      * @cfg {String} invalidText @hide
14428      */
14429     /**
14430      * @cfg {String} msgFx @hide
14431      */
14432     /**
14433      * @cfg {String} validateOnBlur @hide
14434      */
14435 });
14436  
14437     
14438    
14439    
14440    
14441       
14442
14443 /**
14444  * @class Roo.bootstrap.HtmlEditorToolbar1
14445  * Basic Toolbar
14446  * 
14447  * Usage:
14448  *
14449  new Roo.bootstrap.HtmlEditor({
14450     ....
14451     toolbars : [
14452         new Roo.bootstrap.HtmlEditorToolbar1({
14453             disable : { fonts: 1 , format: 1, ..., ... , ...],
14454             btns : [ .... ]
14455         })
14456     }
14457      
14458  * 
14459  * @cfg {Object} disable List of elements to disable..
14460  * @cfg {Array} btns List of additional buttons.
14461  * 
14462  * 
14463  * NEEDS Extra CSS? 
14464  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14465  */
14466  
14467 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14468 {
14469     
14470     Roo.apply(this, config);
14471     
14472     // default disabled, based on 'good practice'..
14473     this.disable = this.disable || {};
14474     Roo.applyIf(this.disable, {
14475         fontSize : true,
14476         colors : true,
14477         specialElements : true
14478     });
14479     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14480     
14481     this.editor = config.editor;
14482     this.editorcore = config.editor.editorcore;
14483     
14484     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14485     
14486     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14487     // dont call parent... till later.
14488 }
14489 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14490     
14491     
14492     bar : true,
14493     
14494     editor : false,
14495     editorcore : false,
14496     
14497     
14498     formats : [
14499         "p" ,  
14500         "h1","h2","h3","h4","h5","h6", 
14501         "pre", "code", 
14502         "abbr", "acronym", "address", "cite", "samp", "var",
14503         'div','span'
14504     ],
14505     
14506     onRender : function(ct, position)
14507     {
14508        // Roo.log("Call onRender: " + this.xtype);
14509         
14510        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14511        Roo.log(this.el);
14512        this.el.dom.style.marginBottom = '0';
14513        var _this = this;
14514        var editorcore = this.editorcore;
14515        var editor= this.editor;
14516        
14517        var children = [];
14518        var btn = function(id,cmd , toggle, handler){
14519        
14520             var  event = toggle ? 'toggle' : 'click';
14521        
14522             var a = {
14523                 size : 'sm',
14524                 xtype: 'Button',
14525                 xns: Roo.bootstrap,
14526                 glyphicon : id,
14527                 cmd : id || cmd,
14528                 enableToggle:toggle !== false,
14529                 //html : 'submit'
14530                 pressed : toggle ? false : null,
14531                 listeners : {}
14532             }
14533             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14534                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14535             }
14536             children.push(a);
14537             return a;
14538        }
14539         
14540         var style = {
14541                 xtype: 'Button',
14542                 size : 'sm',
14543                 xns: Roo.bootstrap,
14544                 glyphicon : 'font',
14545                 //html : 'submit'
14546                 menu : {
14547                     xtype: 'Menu',
14548                     xns: Roo.bootstrap,
14549                     items:  []
14550                 }
14551         };
14552         Roo.each(this.formats, function(f) {
14553             style.menu.items.push({
14554                 xtype :'MenuItem',
14555                 xns: Roo.bootstrap,
14556                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14557                 tagname : f,
14558                 listeners : {
14559                     click : function()
14560                     {
14561                         editorcore.insertTag(this.tagname);
14562                         editor.focus();
14563                     }
14564                 }
14565                 
14566             });
14567         });
14568          children.push(style);   
14569             
14570             
14571         btn('bold',false,true);
14572         btn('italic',false,true);
14573         btn('align-left', 'justifyleft',true);
14574         btn('align-center', 'justifycenter',true);
14575         btn('align-right' , 'justifyright',true);
14576         btn('link', false, false, function(btn) {
14577             //Roo.log("create link?");
14578             var url = prompt(this.createLinkText, this.defaultLinkValue);
14579             if(url && url != 'http:/'+'/'){
14580                 this.editorcore.relayCmd('createlink', url);
14581             }
14582         }),
14583         btn('list','insertunorderedlist',true);
14584         btn('pencil', false,true, function(btn){
14585                 Roo.log(this);
14586                 
14587                 this.toggleSourceEdit(btn.pressed);
14588         });
14589         /*
14590         var cog = {
14591                 xtype: 'Button',
14592                 size : 'sm',
14593                 xns: Roo.bootstrap,
14594                 glyphicon : 'cog',
14595                 //html : 'submit'
14596                 menu : {
14597                     xtype: 'Menu',
14598                     xns: Roo.bootstrap,
14599                     items:  []
14600                 }
14601         };
14602         
14603         cog.menu.items.push({
14604             xtype :'MenuItem',
14605             xns: Roo.bootstrap,
14606             html : Clean styles,
14607             tagname : f,
14608             listeners : {
14609                 click : function()
14610                 {
14611                     editorcore.insertTag(this.tagname);
14612                     editor.focus();
14613                 }
14614             }
14615             
14616         });
14617        */
14618         
14619          
14620        this.xtype = 'Navbar';
14621         
14622         for(var i=0;i< children.length;i++) {
14623             
14624             this.buttons.add(this.addxtypeChild(children[i]));
14625             
14626         }
14627         
14628         editor.on('editorevent', this.updateToolbar, this);
14629     },
14630     onBtnClick : function(id)
14631     {
14632        this.editorcore.relayCmd(id);
14633        this.editorcore.focus();
14634     },
14635     
14636     /**
14637      * Protected method that will not generally be called directly. It triggers
14638      * a toolbar update by reading the markup state of the current selection in the editor.
14639      */
14640     updateToolbar: function(){
14641
14642         if(!this.editorcore.activated){
14643             this.editor.onFirstFocus(); // is this neeed?
14644             return;
14645         }
14646
14647         var btns = this.buttons; 
14648         var doc = this.editorcore.doc;
14649         btns.get('bold').setActive(doc.queryCommandState('bold'));
14650         btns.get('italic').setActive(doc.queryCommandState('italic'));
14651         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14652         
14653         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14654         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14655         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14656         
14657         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14658         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14659          /*
14660         
14661         var ans = this.editorcore.getAllAncestors();
14662         if (this.formatCombo) {
14663             
14664             
14665             var store = this.formatCombo.store;
14666             this.formatCombo.setValue("");
14667             for (var i =0; i < ans.length;i++) {
14668                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14669                     // select it..
14670                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14671                     break;
14672                 }
14673             }
14674         }
14675         
14676         
14677         
14678         // hides menus... - so this cant be on a menu...
14679         Roo.bootstrap.MenuMgr.hideAll();
14680         */
14681         Roo.bootstrap.MenuMgr.hideAll();
14682         //this.editorsyncValue();
14683     },
14684     onFirstFocus: function() {
14685         this.buttons.each(function(item){
14686            item.enable();
14687         });
14688     },
14689     toggleSourceEdit : function(sourceEditMode){
14690         
14691           
14692         if(sourceEditMode){
14693             Roo.log("disabling buttons");
14694            this.buttons.each( function(item){
14695                 if(item.cmd != 'pencil'){
14696                     item.disable();
14697                 }
14698             });
14699           
14700         }else{
14701             Roo.log("enabling buttons");
14702             if(this.editorcore.initialized){
14703                 this.buttons.each( function(item){
14704                     item.enable();
14705                 });
14706             }
14707             
14708         }
14709         Roo.log("calling toggole on editor");
14710         // tell the editor that it's been pressed..
14711         this.editor.toggleSourceEdit(sourceEditMode);
14712        
14713     }
14714 });
14715
14716
14717
14718
14719
14720 /**
14721  * @class Roo.bootstrap.Table.AbstractSelectionModel
14722  * @extends Roo.util.Observable
14723  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14724  * implemented by descendant classes.  This class should not be directly instantiated.
14725  * @constructor
14726  */
14727 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14728     this.locked = false;
14729     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14730 };
14731
14732
14733 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14734     /** @ignore Called by the grid automatically. Do not call directly. */
14735     init : function(grid){
14736         this.grid = grid;
14737         this.initEvents();
14738     },
14739
14740     /**
14741      * Locks the selections.
14742      */
14743     lock : function(){
14744         this.locked = true;
14745     },
14746
14747     /**
14748      * Unlocks the selections.
14749      */
14750     unlock : function(){
14751         this.locked = false;
14752     },
14753
14754     /**
14755      * Returns true if the selections are locked.
14756      * @return {Boolean}
14757      */
14758     isLocked : function(){
14759         return this.locked;
14760     }
14761 });
14762 /**
14763  * @class Roo.bootstrap.Table.ColumnModel
14764  * @extends Roo.util.Observable
14765  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14766  * the columns in the table.
14767  
14768  * @constructor
14769  * @param {Object} config An Array of column config objects. See this class's
14770  * config objects for details.
14771 */
14772 Roo.bootstrap.Table.ColumnModel = function(config){
14773         /**
14774      * The config passed into the constructor
14775      */
14776     this.config = config;
14777     this.lookup = {};
14778
14779     // if no id, create one
14780     // if the column does not have a dataIndex mapping,
14781     // map it to the order it is in the config
14782     for(var i = 0, len = config.length; i < len; i++){
14783         var c = config[i];
14784         if(typeof c.dataIndex == "undefined"){
14785             c.dataIndex = i;
14786         }
14787         if(typeof c.renderer == "string"){
14788             c.renderer = Roo.util.Format[c.renderer];
14789         }
14790         if(typeof c.id == "undefined"){
14791             c.id = Roo.id();
14792         }
14793 //        if(c.editor && c.editor.xtype){
14794 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14795 //        }
14796 //        if(c.editor && c.editor.isFormField){
14797 //            c.editor = new Roo.grid.GridEditor(c.editor);
14798 //        }
14799
14800         this.lookup[c.id] = c;
14801     }
14802
14803     /**
14804      * The width of columns which have no width specified (defaults to 100)
14805      * @type Number
14806      */
14807     this.defaultWidth = 100;
14808
14809     /**
14810      * Default sortable of columns which have no sortable specified (defaults to false)
14811      * @type Boolean
14812      */
14813     this.defaultSortable = false;
14814
14815     this.addEvents({
14816         /**
14817              * @event widthchange
14818              * Fires when the width of a column changes.
14819              * @param {ColumnModel} this
14820              * @param {Number} columnIndex The column index
14821              * @param {Number} newWidth The new width
14822              */
14823             "widthchange": true,
14824         /**
14825              * @event headerchange
14826              * Fires when the text of a header changes.
14827              * @param {ColumnModel} this
14828              * @param {Number} columnIndex The column index
14829              * @param {Number} newText The new header text
14830              */
14831             "headerchange": true,
14832         /**
14833              * @event hiddenchange
14834              * Fires when a column is hidden or "unhidden".
14835              * @param {ColumnModel} this
14836              * @param {Number} columnIndex The column index
14837              * @param {Boolean} hidden true if hidden, false otherwise
14838              */
14839             "hiddenchange": true,
14840             /**
14841          * @event columnmoved
14842          * Fires when a column is moved.
14843          * @param {ColumnModel} this
14844          * @param {Number} oldIndex
14845          * @param {Number} newIndex
14846          */
14847         "columnmoved" : true,
14848         /**
14849          * @event columlockchange
14850          * Fires when a column's locked state is changed
14851          * @param {ColumnModel} this
14852          * @param {Number} colIndex
14853          * @param {Boolean} locked true if locked
14854          */
14855         "columnlockchange" : true
14856     });
14857     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14858 };
14859 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14860     /**
14861      * @cfg {String} header The header text to display in the Grid view.
14862      */
14863     /**
14864      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14865      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14866      * specified, the column's index is used as an index into the Record's data Array.
14867      */
14868     /**
14869      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14870      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14871      */
14872     /**
14873      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14874      * Defaults to the value of the {@link #defaultSortable} property.
14875      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14876      */
14877     /**
14878      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14879      */
14880     /**
14881      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14882      */
14883     /**
14884      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14885      */
14886     /**
14887      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14888      */
14889     /**
14890      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14891      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14892      * default renderer uses the raw data value.
14893      */
14894     /**
14895      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14896      */
14897
14898     /**
14899      * Returns the id of the column at the specified index.
14900      * @param {Number} index The column index
14901      * @return {String} the id
14902      */
14903     getColumnId : function(index){
14904         return this.config[index].id;
14905     },
14906
14907     /**
14908      * Returns the column for a specified id.
14909      * @param {String} id The column id
14910      * @return {Object} the column
14911      */
14912     getColumnById : function(id){
14913         return this.lookup[id];
14914     },
14915
14916     
14917     /**
14918      * Returns the column for a specified dataIndex.
14919      * @param {String} dataIndex The column dataIndex
14920      * @return {Object|Boolean} the column or false if not found
14921      */
14922     getColumnByDataIndex: function(dataIndex){
14923         var index = this.findColumnIndex(dataIndex);
14924         return index > -1 ? this.config[index] : false;
14925     },
14926     
14927     /**
14928      * Returns the index for a specified column id.
14929      * @param {String} id The column id
14930      * @return {Number} the index, or -1 if not found
14931      */
14932     getIndexById : function(id){
14933         for(var i = 0, len = this.config.length; i < len; i++){
14934             if(this.config[i].id == id){
14935                 return i;
14936             }
14937         }
14938         return -1;
14939     },
14940     
14941     /**
14942      * Returns the index for a specified column dataIndex.
14943      * @param {String} dataIndex The column dataIndex
14944      * @return {Number} the index, or -1 if not found
14945      */
14946     
14947     findColumnIndex : function(dataIndex){
14948         for(var i = 0, len = this.config.length; i < len; i++){
14949             if(this.config[i].dataIndex == dataIndex){
14950                 return i;
14951             }
14952         }
14953         return -1;
14954     },
14955     
14956     
14957     moveColumn : function(oldIndex, newIndex){
14958         var c = this.config[oldIndex];
14959         this.config.splice(oldIndex, 1);
14960         this.config.splice(newIndex, 0, c);
14961         this.dataMap = null;
14962         this.fireEvent("columnmoved", this, oldIndex, newIndex);
14963     },
14964
14965     isLocked : function(colIndex){
14966         return this.config[colIndex].locked === true;
14967     },
14968
14969     setLocked : function(colIndex, value, suppressEvent){
14970         if(this.isLocked(colIndex) == value){
14971             return;
14972         }
14973         this.config[colIndex].locked = value;
14974         if(!suppressEvent){
14975             this.fireEvent("columnlockchange", this, colIndex, value);
14976         }
14977     },
14978
14979     getTotalLockedWidth : function(){
14980         var totalWidth = 0;
14981         for(var i = 0; i < this.config.length; i++){
14982             if(this.isLocked(i) && !this.isHidden(i)){
14983                 this.totalWidth += this.getColumnWidth(i);
14984             }
14985         }
14986         return totalWidth;
14987     },
14988
14989     getLockedCount : function(){
14990         for(var i = 0, len = this.config.length; i < len; i++){
14991             if(!this.isLocked(i)){
14992                 return i;
14993             }
14994         }
14995     },
14996
14997     /**
14998      * Returns the number of columns.
14999      * @return {Number}
15000      */
15001     getColumnCount : function(visibleOnly){
15002         if(visibleOnly === true){
15003             var c = 0;
15004             for(var i = 0, len = this.config.length; i < len; i++){
15005                 if(!this.isHidden(i)){
15006                     c++;
15007                 }
15008             }
15009             return c;
15010         }
15011         return this.config.length;
15012     },
15013
15014     /**
15015      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15016      * @param {Function} fn
15017      * @param {Object} scope (optional)
15018      * @return {Array} result
15019      */
15020     getColumnsBy : function(fn, scope){
15021         var r = [];
15022         for(var i = 0, len = this.config.length; i < len; i++){
15023             var c = this.config[i];
15024             if(fn.call(scope||this, c, i) === true){
15025                 r[r.length] = c;
15026             }
15027         }
15028         return r;
15029     },
15030
15031     /**
15032      * Returns true if the specified column is sortable.
15033      * @param {Number} col The column index
15034      * @return {Boolean}
15035      */
15036     isSortable : function(col){
15037         if(typeof this.config[col].sortable == "undefined"){
15038             return this.defaultSortable;
15039         }
15040         return this.config[col].sortable;
15041     },
15042
15043     /**
15044      * Returns the rendering (formatting) function defined for the column.
15045      * @param {Number} col The column index.
15046      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15047      */
15048     getRenderer : function(col){
15049         if(!this.config[col].renderer){
15050             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15051         }
15052         return this.config[col].renderer;
15053     },
15054
15055     /**
15056      * Sets the rendering (formatting) function for a column.
15057      * @param {Number} col The column index
15058      * @param {Function} fn The function to use to process the cell's raw data
15059      * to return HTML markup for the grid view. The render function is called with
15060      * the following parameters:<ul>
15061      * <li>Data value.</li>
15062      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15063      * <li>css A CSS style string to apply to the table cell.</li>
15064      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15065      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15066      * <li>Row index</li>
15067      * <li>Column index</li>
15068      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15069      */
15070     setRenderer : function(col, fn){
15071         this.config[col].renderer = fn;
15072     },
15073
15074     /**
15075      * Returns the width for the specified column.
15076      * @param {Number} col The column index
15077      * @return {Number}
15078      */
15079     getColumnWidth : function(col){
15080         return this.config[col].width * 1 || this.defaultWidth;
15081     },
15082
15083     /**
15084      * Sets the width for a column.
15085      * @param {Number} col The column index
15086      * @param {Number} width The new width
15087      */
15088     setColumnWidth : function(col, width, suppressEvent){
15089         this.config[col].width = width;
15090         this.totalWidth = null;
15091         if(!suppressEvent){
15092              this.fireEvent("widthchange", this, col, width);
15093         }
15094     },
15095
15096     /**
15097      * Returns the total width of all columns.
15098      * @param {Boolean} includeHidden True to include hidden column widths
15099      * @return {Number}
15100      */
15101     getTotalWidth : function(includeHidden){
15102         if(!this.totalWidth){
15103             this.totalWidth = 0;
15104             for(var i = 0, len = this.config.length; i < len; i++){
15105                 if(includeHidden || !this.isHidden(i)){
15106                     this.totalWidth += this.getColumnWidth(i);
15107                 }
15108             }
15109         }
15110         return this.totalWidth;
15111     },
15112
15113     /**
15114      * Returns the header for the specified column.
15115      * @param {Number} col The column index
15116      * @return {String}
15117      */
15118     getColumnHeader : function(col){
15119         return this.config[col].header;
15120     },
15121
15122     /**
15123      * Sets the header for a column.
15124      * @param {Number} col The column index
15125      * @param {String} header The new header
15126      */
15127     setColumnHeader : function(col, header){
15128         this.config[col].header = header;
15129         this.fireEvent("headerchange", this, col, header);
15130     },
15131
15132     /**
15133      * Returns the tooltip for the specified column.
15134      * @param {Number} col The column index
15135      * @return {String}
15136      */
15137     getColumnTooltip : function(col){
15138             return this.config[col].tooltip;
15139     },
15140     /**
15141      * Sets the tooltip for a column.
15142      * @param {Number} col The column index
15143      * @param {String} tooltip The new tooltip
15144      */
15145     setColumnTooltip : function(col, tooltip){
15146             this.config[col].tooltip = tooltip;
15147     },
15148
15149     /**
15150      * Returns the dataIndex for the specified column.
15151      * @param {Number} col The column index
15152      * @return {Number}
15153      */
15154     getDataIndex : function(col){
15155         return this.config[col].dataIndex;
15156     },
15157
15158     /**
15159      * Sets the dataIndex for a column.
15160      * @param {Number} col The column index
15161      * @param {Number} dataIndex The new dataIndex
15162      */
15163     setDataIndex : function(col, dataIndex){
15164         this.config[col].dataIndex = dataIndex;
15165     },
15166
15167     
15168     
15169     /**
15170      * Returns true if the cell is editable.
15171      * @param {Number} colIndex The column index
15172      * @param {Number} rowIndex The row index
15173      * @return {Boolean}
15174      */
15175     isCellEditable : function(colIndex, rowIndex){
15176         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15177     },
15178
15179     /**
15180      * Returns the editor defined for the cell/column.
15181      * return false or null to disable editing.
15182      * @param {Number} colIndex The column index
15183      * @param {Number} rowIndex The row index
15184      * @return {Object}
15185      */
15186     getCellEditor : function(colIndex, rowIndex){
15187         return this.config[colIndex].editor;
15188     },
15189
15190     /**
15191      * Sets if a column is editable.
15192      * @param {Number} col The column index
15193      * @param {Boolean} editable True if the column is editable
15194      */
15195     setEditable : function(col, editable){
15196         this.config[col].editable = editable;
15197     },
15198
15199
15200     /**
15201      * Returns true if the column is hidden.
15202      * @param {Number} colIndex The column index
15203      * @return {Boolean}
15204      */
15205     isHidden : function(colIndex){
15206         return this.config[colIndex].hidden;
15207     },
15208
15209
15210     /**
15211      * Returns true if the column width cannot be changed
15212      */
15213     isFixed : function(colIndex){
15214         return this.config[colIndex].fixed;
15215     },
15216
15217     /**
15218      * Returns true if the column can be resized
15219      * @return {Boolean}
15220      */
15221     isResizable : function(colIndex){
15222         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15223     },
15224     /**
15225      * Sets if a column is hidden.
15226      * @param {Number} colIndex The column index
15227      * @param {Boolean} hidden True if the column is hidden
15228      */
15229     setHidden : function(colIndex, hidden){
15230         this.config[colIndex].hidden = hidden;
15231         this.totalWidth = null;
15232         this.fireEvent("hiddenchange", this, colIndex, hidden);
15233     },
15234
15235     /**
15236      * Sets the editor for a column.
15237      * @param {Number} col The column index
15238      * @param {Object} editor The editor object
15239      */
15240     setEditor : function(col, editor){
15241         this.config[col].editor = editor;
15242     }
15243 });
15244
15245 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15246         if(typeof value == "string" && value.length < 1){
15247             return "&#160;";
15248         }
15249         return value;
15250 };
15251
15252 // Alias for backwards compatibility
15253 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15254
15255 /**
15256  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15257  * @class Roo.bootstrap.Table.RowSelectionModel
15258  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15259  * It supports multiple selections and keyboard selection/navigation. 
15260  * @constructor
15261  * @param {Object} config
15262  */
15263
15264 Roo.bootstrap.Table.RowSelectionModel = function(config){
15265     Roo.apply(this, config);
15266     this.selections = new Roo.util.MixedCollection(false, function(o){
15267         return o.id;
15268     });
15269
15270     this.last = false;
15271     this.lastActive = false;
15272
15273     this.addEvents({
15274         /**
15275              * @event selectionchange
15276              * Fires when the selection changes
15277              * @param {SelectionModel} this
15278              */
15279             "selectionchange" : true,
15280         /**
15281              * @event afterselectionchange
15282              * Fires after the selection changes (eg. by key press or clicking)
15283              * @param {SelectionModel} this
15284              */
15285             "afterselectionchange" : true,
15286         /**
15287              * @event beforerowselect
15288              * Fires when a row is selected being selected, return false to cancel.
15289              * @param {SelectionModel} this
15290              * @param {Number} rowIndex The selected index
15291              * @param {Boolean} keepExisting False if other selections will be cleared
15292              */
15293             "beforerowselect" : true,
15294         /**
15295              * @event rowselect
15296              * Fires when a row is selected.
15297              * @param {SelectionModel} this
15298              * @param {Number} rowIndex The selected index
15299              * @param {Roo.data.Record} r The record
15300              */
15301             "rowselect" : true,
15302         /**
15303              * @event rowdeselect
15304              * Fires when a row is deselected.
15305              * @param {SelectionModel} this
15306              * @param {Number} rowIndex The selected index
15307              */
15308         "rowdeselect" : true
15309     });
15310     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15311     this.locked = false;
15312 };
15313
15314 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15315     /**
15316      * @cfg {Boolean} singleSelect
15317      * True to allow selection of only one row at a time (defaults to false)
15318      */
15319     singleSelect : false,
15320
15321     // private
15322     initEvents : function(){
15323
15324         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15325             this.grid.on("mousedown", this.handleMouseDown, this);
15326         }else{ // allow click to work like normal
15327             this.grid.on("rowclick", this.handleDragableRowClick, this);
15328         }
15329
15330         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15331             "up" : function(e){
15332                 if(!e.shiftKey){
15333                     this.selectPrevious(e.shiftKey);
15334                 }else if(this.last !== false && this.lastActive !== false){
15335                     var last = this.last;
15336                     this.selectRange(this.last,  this.lastActive-1);
15337                     this.grid.getView().focusRow(this.lastActive);
15338                     if(last !== false){
15339                         this.last = last;
15340                     }
15341                 }else{
15342                     this.selectFirstRow();
15343                 }
15344                 this.fireEvent("afterselectionchange", this);
15345             },
15346             "down" : function(e){
15347                 if(!e.shiftKey){
15348                     this.selectNext(e.shiftKey);
15349                 }else if(this.last !== false && this.lastActive !== false){
15350                     var last = this.last;
15351                     this.selectRange(this.last,  this.lastActive+1);
15352                     this.grid.getView().focusRow(this.lastActive);
15353                     if(last !== false){
15354                         this.last = last;
15355                     }
15356                 }else{
15357                     this.selectFirstRow();
15358                 }
15359                 this.fireEvent("afterselectionchange", this);
15360             },
15361             scope: this
15362         });
15363
15364         var view = this.grid.view;
15365         view.on("refresh", this.onRefresh, this);
15366         view.on("rowupdated", this.onRowUpdated, this);
15367         view.on("rowremoved", this.onRemove, this);
15368     },
15369
15370     // private
15371     onRefresh : function(){
15372         var ds = this.grid.dataSource, i, v = this.grid.view;
15373         var s = this.selections;
15374         s.each(function(r){
15375             if((i = ds.indexOfId(r.id)) != -1){
15376                 v.onRowSelect(i);
15377             }else{
15378                 s.remove(r);
15379             }
15380         });
15381     },
15382
15383     // private
15384     onRemove : function(v, index, r){
15385         this.selections.remove(r);
15386     },
15387
15388     // private
15389     onRowUpdated : function(v, index, r){
15390         if(this.isSelected(r)){
15391             v.onRowSelect(index);
15392         }
15393     },
15394
15395     /**
15396      * Select records.
15397      * @param {Array} records The records to select
15398      * @param {Boolean} keepExisting (optional) True to keep existing selections
15399      */
15400     selectRecords : function(records, keepExisting){
15401         if(!keepExisting){
15402             this.clearSelections();
15403         }
15404         var ds = this.grid.dataSource;
15405         for(var i = 0, len = records.length; i < len; i++){
15406             this.selectRow(ds.indexOf(records[i]), true);
15407         }
15408     },
15409
15410     /**
15411      * Gets the number of selected rows.
15412      * @return {Number}
15413      */
15414     getCount : function(){
15415         return this.selections.length;
15416     },
15417
15418     /**
15419      * Selects the first row in the grid.
15420      */
15421     selectFirstRow : function(){
15422         this.selectRow(0);
15423     },
15424
15425     /**
15426      * Select the last row.
15427      * @param {Boolean} keepExisting (optional) True to keep existing selections
15428      */
15429     selectLastRow : function(keepExisting){
15430         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15431     },
15432
15433     /**
15434      * Selects the row immediately following the last selected row.
15435      * @param {Boolean} keepExisting (optional) True to keep existing selections
15436      */
15437     selectNext : function(keepExisting){
15438         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15439             this.selectRow(this.last+1, keepExisting);
15440             this.grid.getView().focusRow(this.last);
15441         }
15442     },
15443
15444     /**
15445      * Selects the row that precedes the last selected row.
15446      * @param {Boolean} keepExisting (optional) True to keep existing selections
15447      */
15448     selectPrevious : function(keepExisting){
15449         if(this.last){
15450             this.selectRow(this.last-1, keepExisting);
15451             this.grid.getView().focusRow(this.last);
15452         }
15453     },
15454
15455     /**
15456      * Returns the selected records
15457      * @return {Array} Array of selected records
15458      */
15459     getSelections : function(){
15460         return [].concat(this.selections.items);
15461     },
15462
15463     /**
15464      * Returns the first selected record.
15465      * @return {Record}
15466      */
15467     getSelected : function(){
15468         return this.selections.itemAt(0);
15469     },
15470
15471
15472     /**
15473      * Clears all selections.
15474      */
15475     clearSelections : function(fast){
15476         if(this.locked) return;
15477         if(fast !== true){
15478             var ds = this.grid.dataSource;
15479             var s = this.selections;
15480             s.each(function(r){
15481                 this.deselectRow(ds.indexOfId(r.id));
15482             }, this);
15483             s.clear();
15484         }else{
15485             this.selections.clear();
15486         }
15487         this.last = false;
15488     },
15489
15490
15491     /**
15492      * Selects all rows.
15493      */
15494     selectAll : function(){
15495         if(this.locked) return;
15496         this.selections.clear();
15497         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15498             this.selectRow(i, true);
15499         }
15500     },
15501
15502     /**
15503      * Returns True if there is a selection.
15504      * @return {Boolean}
15505      */
15506     hasSelection : function(){
15507         return this.selections.length > 0;
15508     },
15509
15510     /**
15511      * Returns True if the specified row is selected.
15512      * @param {Number/Record} record The record or index of the record to check
15513      * @return {Boolean}
15514      */
15515     isSelected : function(index){
15516         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15517         return (r && this.selections.key(r.id) ? true : false);
15518     },
15519
15520     /**
15521      * Returns True if the specified record id is selected.
15522      * @param {String} id The id of record to check
15523      * @return {Boolean}
15524      */
15525     isIdSelected : function(id){
15526         return (this.selections.key(id) ? true : false);
15527     },
15528
15529     // private
15530     handleMouseDown : function(e, t){
15531         var view = this.grid.getView(), rowIndex;
15532         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15533             return;
15534         };
15535         if(e.shiftKey && this.last !== false){
15536             var last = this.last;
15537             this.selectRange(last, rowIndex, e.ctrlKey);
15538             this.last = last; // reset the last
15539             view.focusRow(rowIndex);
15540         }else{
15541             var isSelected = this.isSelected(rowIndex);
15542             if(e.button !== 0 && isSelected){
15543                 view.focusRow(rowIndex);
15544             }else if(e.ctrlKey && isSelected){
15545                 this.deselectRow(rowIndex);
15546             }else if(!isSelected){
15547                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15548                 view.focusRow(rowIndex);
15549             }
15550         }
15551         this.fireEvent("afterselectionchange", this);
15552     },
15553     // private
15554     handleDragableRowClick :  function(grid, rowIndex, e) 
15555     {
15556         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15557             this.selectRow(rowIndex, false);
15558             grid.view.focusRow(rowIndex);
15559              this.fireEvent("afterselectionchange", this);
15560         }
15561     },
15562     
15563     /**
15564      * Selects multiple rows.
15565      * @param {Array} rows Array of the indexes of the row to select
15566      * @param {Boolean} keepExisting (optional) True to keep existing selections
15567      */
15568     selectRows : function(rows, keepExisting){
15569         if(!keepExisting){
15570             this.clearSelections();
15571         }
15572         for(var i = 0, len = rows.length; i < len; i++){
15573             this.selectRow(rows[i], true);
15574         }
15575     },
15576
15577     /**
15578      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15579      * @param {Number} startRow The index of the first row in the range
15580      * @param {Number} endRow The index of the last row in the range
15581      * @param {Boolean} keepExisting (optional) True to retain existing selections
15582      */
15583     selectRange : function(startRow, endRow, keepExisting){
15584         if(this.locked) return;
15585         if(!keepExisting){
15586             this.clearSelections();
15587         }
15588         if(startRow <= endRow){
15589             for(var i = startRow; i <= endRow; i++){
15590                 this.selectRow(i, true);
15591             }
15592         }else{
15593             for(var i = startRow; i >= endRow; i--){
15594                 this.selectRow(i, true);
15595             }
15596         }
15597     },
15598
15599     /**
15600      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15601      * @param {Number} startRow The index of the first row in the range
15602      * @param {Number} endRow The index of the last row in the range
15603      */
15604     deselectRange : function(startRow, endRow, preventViewNotify){
15605         if(this.locked) return;
15606         for(var i = startRow; i <= endRow; i++){
15607             this.deselectRow(i, preventViewNotify);
15608         }
15609     },
15610
15611     /**
15612      * Selects a row.
15613      * @param {Number} row The index of the row to select
15614      * @param {Boolean} keepExisting (optional) True to keep existing selections
15615      */
15616     selectRow : function(index, keepExisting, preventViewNotify){
15617         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15618         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15619             if(!keepExisting || this.singleSelect){
15620                 this.clearSelections();
15621             }
15622             var r = this.grid.dataSource.getAt(index);
15623             this.selections.add(r);
15624             this.last = this.lastActive = index;
15625             if(!preventViewNotify){
15626                 this.grid.getView().onRowSelect(index);
15627             }
15628             this.fireEvent("rowselect", this, index, r);
15629             this.fireEvent("selectionchange", this);
15630         }
15631     },
15632
15633     /**
15634      * Deselects a row.
15635      * @param {Number} row The index of the row to deselect
15636      */
15637     deselectRow : function(index, preventViewNotify){
15638         if(this.locked) return;
15639         if(this.last == index){
15640             this.last = false;
15641         }
15642         if(this.lastActive == index){
15643             this.lastActive = false;
15644         }
15645         var r = this.grid.dataSource.getAt(index);
15646         this.selections.remove(r);
15647         if(!preventViewNotify){
15648             this.grid.getView().onRowDeselect(index);
15649         }
15650         this.fireEvent("rowdeselect", this, index);
15651         this.fireEvent("selectionchange", this);
15652     },
15653
15654     // private
15655     restoreLast : function(){
15656         if(this._last){
15657             this.last = this._last;
15658         }
15659     },
15660
15661     // private
15662     acceptsNav : function(row, col, cm){
15663         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15664     },
15665
15666     // private
15667     onEditorKey : function(field, e){
15668         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15669         if(k == e.TAB){
15670             e.stopEvent();
15671             ed.completeEdit();
15672             if(e.shiftKey){
15673                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15674             }else{
15675                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15676             }
15677         }else if(k == e.ENTER && !e.ctrlKey){
15678             e.stopEvent();
15679             ed.completeEdit();
15680             if(e.shiftKey){
15681                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15682             }else{
15683                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15684             }
15685         }else if(k == e.ESC){
15686             ed.cancelEdit();
15687         }
15688         if(newCell){
15689             g.startEditing(newCell[0], newCell[1]);
15690         }
15691     }
15692 });/*
15693  * - LGPL
15694  *
15695  * element
15696  * 
15697  */
15698
15699 /**
15700  * @class Roo.bootstrap.MessageBar
15701  * @extends Roo.bootstrap.Component
15702  * Bootstrap MessageBar class
15703  * @cfg {String} html contents of the MessageBar
15704  * @cfg {String} weight (info | success | warning | danger) default info
15705  * @cfg {String} beforeClass insert the bar before the given class
15706  * @cfg {Boolean} closable (true | false) default false
15707  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15708  * 
15709  * @constructor
15710  * Create a new Element
15711  * @param {Object} config The config object
15712  */
15713
15714 Roo.bootstrap.MessageBar = function(config){
15715     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15716 };
15717
15718 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15719     
15720     html: '',
15721     weight: 'info',
15722     closable: false,
15723     fixed: false,
15724     beforeClass: 'bootstrap-sticky-wrap',
15725     
15726     getAutoCreate : function(){
15727         
15728         var cfg = {
15729             tag: 'div',
15730             cls: 'alert alert-dismissable alert-' + this.weight,
15731             cn: [
15732                 {
15733                     tag: 'span',
15734                     cls: 'message',
15735                     html: this.html || ''
15736                 }
15737             ]
15738         }
15739         
15740         if(this.fixed){
15741             cfg.cls += ' alert-messages-fixed';
15742         }
15743         
15744         if(this.closable){
15745             cfg.cn.push({
15746                 tag: 'button',
15747                 cls: 'close',
15748                 html: 'x'
15749             });
15750         }
15751         
15752         return cfg;
15753     },
15754     
15755     onRender : function(ct, position)
15756     {
15757         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15758         
15759         if(!this.el){
15760             var cfg = Roo.apply({},  this.getAutoCreate());
15761             cfg.id = Roo.id();
15762             
15763             if (this.cls) {
15764                 cfg.cls += ' ' + this.cls;
15765             }
15766             if (this.style) {
15767                 cfg.style = this.style;
15768             }
15769             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15770             
15771             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15772         }
15773         
15774         this.el.select('>button.close').on('click', this.hide, this);
15775         
15776     },
15777     
15778     show : function()
15779     {
15780         if (!this.rendered) {
15781             this.render();
15782         }
15783         
15784         this.el.show();
15785         
15786         this.fireEvent('show', this);
15787         
15788     },
15789     
15790     hide : function()
15791     {
15792         if (!this.rendered) {
15793             this.render();
15794         }
15795         
15796         this.el.hide();
15797         
15798         this.fireEvent('hide', this);
15799     },
15800     
15801     update : function()
15802     {
15803 //        var e = this.el.dom.firstChild;
15804 //        
15805 //        if(this.closable){
15806 //            e = e.nextSibling;
15807 //        }
15808 //        
15809 //        e.data = this.html || '';
15810
15811         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15812     }
15813    
15814 });
15815
15816  
15817
15818