Roo/bootstrap/Table.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  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         Roo.each(this.el.select('thead th.sortable').elements, function(e){
2954             Roo.log(e);
2955         });
2956 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2957 //        this.maskEl.enableDisplayMode("block");
2958 //        this.maskEl.show();
2959         
2960         this.store.on('load', this.onLoad, this);
2961         this.store.on('beforeload', this.onBeforeLoad, this);
2962         
2963         this.store.load();
2964         
2965         
2966         
2967     },
2968     
2969     renderHeader : function()
2970     {
2971         var header = {
2972             tag: 'thead',
2973             cn : []
2974         };
2975         
2976         var cm = this.cm;
2977         
2978         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2979             
2980             var config = cm.config[i];
2981             
2982             var c = {
2983                 tag: 'th',
2984                 html: cm.getColumnHeader(i)
2985             };
2986             
2987             if(typeof(config.dataIndex) != 'undefined'){
2988                 c.sort = config.dataIndex;
2989             }
2990             
2991             if(typeof(config.sortable) != 'undefined' && config.sortable){
2992                 c.cls = 'sortable';
2993             }
2994             
2995             header.cn.push(c)
2996         }
2997         
2998         return header;
2999     },
3000     
3001     renderBody : function()
3002     {
3003         var body = {
3004             tag: 'tbody',
3005             cn : []
3006         };
3007         
3008         return body;
3009     },
3010     
3011     renderFooter : function()
3012     {
3013         var footer = {
3014             tag: 'tfoot',
3015             cn : []
3016         };
3017         
3018         return footer;
3019     },
3020     
3021     onLoad : function()
3022     {
3023         Roo.log('ds onload');
3024         
3025         var cm = this.cm;
3026         
3027         var tbody = this.el.select('tbody', true).first();
3028         
3029         var renders = [];
3030         
3031         if(this.store.getCount() > 0){
3032             this.store.data.each(function(d){
3033                 var row = {
3034                     tag : 'tr',
3035                     cn : []
3036                 };
3037                 
3038                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3039                     var renderer = cm.getRenderer(i);
3040                     var config = cm.config[i];
3041                     var value = '';
3042                     var id = Roo.id();
3043                     
3044                     if(typeof(renderer) !== 'undefined'){
3045                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3046                     }
3047                     
3048                     if(typeof(value) === 'object'){
3049                         renders.push({
3050                             id : id,
3051                             cfg : value 
3052                         })
3053                     }
3054                     
3055                     var td = {
3056                         tag: 'td',
3057                         id: id,
3058                         html: (typeof(value) === 'object') ? '' : value
3059                     };
3060                     
3061                     if(typeof(config.width) != 'undefined'){
3062                         td.width = config.width;
3063                     }
3064                     
3065                     row.cn.push(td);
3066                    
3067                 }
3068                 
3069                 tbody.createChild(row);
3070                 
3071             });
3072         }
3073         
3074         
3075         if(renders.length){
3076             var _this = this;
3077             Roo.each(renders, function(r){
3078                 _this.renderColumn(r);
3079             })
3080         }
3081 //        
3082 //        if(this.loadMask){
3083 //            this.maskEl.hide();
3084 //        }
3085     },
3086     
3087     onBeforeLoad : function()
3088     {
3089         Roo.log('ds onBeforeLoad');
3090         
3091         this.clear();
3092         
3093 //        if(this.loadMask){
3094 //            this.maskEl.show();
3095 //        }
3096     },
3097     
3098     clear : function()
3099     {
3100         this.el.select('tbody', true).first().dom.innerHTML = '';
3101     },
3102     
3103     getSelectionModel : function(){
3104         if(!this.selModel){
3105             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3106         }
3107         return this.selModel;
3108     },
3109     
3110     renderColumn : function(r)
3111     {
3112         var _this = this;
3113         r.cfg.render(Roo.get(r.id));
3114         
3115         if(r.cfg.cn){
3116             Roo.each(r.cfg.cn, function(c){
3117                 var child = {
3118                     id: r.id,
3119                     cfg: c
3120                 }
3121                 _this.renderColumn(child);
3122             })
3123         }
3124     }
3125    
3126 });
3127
3128  
3129
3130  /*
3131  * - LGPL
3132  *
3133  * table cell
3134  * 
3135  */
3136
3137 /**
3138  * @class Roo.bootstrap.TableCell
3139  * @extends Roo.bootstrap.Component
3140  * Bootstrap TableCell class
3141  * @cfg {String} html cell contain text
3142  * @cfg {String} cls cell class
3143  * @cfg {String} tag cell tag (td|th) default td
3144  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3145  * @cfg {String} align Aligns the content in a cell
3146  * @cfg {String} axis Categorizes cells
3147  * @cfg {String} bgcolor Specifies the background color of a cell
3148  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3149  * @cfg {Number} colspan Specifies the number of columns a cell should span
3150  * @cfg {String} headers Specifies one or more header cells a cell is related to
3151  * @cfg {Number} height Sets the height of a cell
3152  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3153  * @cfg {Number} rowspan Sets the number of rows a cell should span
3154  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3155  * @cfg {String} valign Vertical aligns the content in a cell
3156  * @cfg {Number} width Specifies the width of a cell
3157  * 
3158  * @constructor
3159  * Create a new TableCell
3160  * @param {Object} config The config object
3161  */
3162
3163 Roo.bootstrap.TableCell = function(config){
3164     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3165 };
3166
3167 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3168     
3169     html: false,
3170     cls: false,
3171     tag: false,
3172     abbr: false,
3173     align: false,
3174     axis: false,
3175     bgcolor: false,
3176     charoff: false,
3177     colspan: false,
3178     headers: false,
3179     height: false,
3180     nowrap: false,
3181     rowspan: false,
3182     scope: false,
3183     valign: false,
3184     width: false,
3185     
3186     
3187     getAutoCreate : function(){
3188         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3189         
3190         cfg = {
3191             tag: 'td'
3192         }
3193         
3194         if(this.tag){
3195             cfg.tag = this.tag;
3196         }
3197         
3198         if (this.html) {
3199             cfg.html=this.html
3200         }
3201         if (this.cls) {
3202             cfg.cls=this.cls
3203         }
3204         if (this.abbr) {
3205             cfg.abbr=this.abbr
3206         }
3207         if (this.align) {
3208             cfg.align=this.align
3209         }
3210         if (this.axis) {
3211             cfg.axis=this.axis
3212         }
3213         if (this.bgcolor) {
3214             cfg.bgcolor=this.bgcolor
3215         }
3216         if (this.charoff) {
3217             cfg.charoff=this.charoff
3218         }
3219         if (this.colspan) {
3220             cfg.colspan=this.colspan
3221         }
3222         if (this.headers) {
3223             cfg.headers=this.headers
3224         }
3225         if (this.height) {
3226             cfg.height=this.height
3227         }
3228         if (this.nowrap) {
3229             cfg.nowrap=this.nowrap
3230         }
3231         if (this.rowspan) {
3232             cfg.rowspan=this.rowspan
3233         }
3234         if (this.scope) {
3235             cfg.scope=this.scope
3236         }
3237         if (this.valign) {
3238             cfg.valign=this.valign
3239         }
3240         if (this.width) {
3241             cfg.width=this.width
3242         }
3243         
3244         
3245         return cfg;
3246     }
3247    
3248 });
3249
3250  
3251
3252  /*
3253  * - LGPL
3254  *
3255  * table row
3256  * 
3257  */
3258
3259 /**
3260  * @class Roo.bootstrap.TableRow
3261  * @extends Roo.bootstrap.Component
3262  * Bootstrap TableRow class
3263  * @cfg {String} cls row class
3264  * @cfg {String} align Aligns the content in a table row
3265  * @cfg {String} bgcolor Specifies a background color for a table row
3266  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3267  * @cfg {String} valign Vertical aligns the content in a table row
3268  * 
3269  * @constructor
3270  * Create a new TableRow
3271  * @param {Object} config The config object
3272  */
3273
3274 Roo.bootstrap.TableRow = function(config){
3275     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3276 };
3277
3278 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3279     
3280     cls: false,
3281     align: false,
3282     bgcolor: false,
3283     charoff: false,
3284     valign: false,
3285     
3286     getAutoCreate : function(){
3287         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3288         
3289         cfg = {
3290             tag: 'tr'
3291         }
3292             
3293         if(this.cls){
3294             cfg.cls = this.cls;
3295         }
3296         if(this.align){
3297             cfg.align = this.align;
3298         }
3299         if(this.bgcolor){
3300             cfg.bgcolor = this.bgcolor;
3301         }
3302         if(this.charoff){
3303             cfg.charoff = this.charoff;
3304         }
3305         if(this.valign){
3306             cfg.valign = this.valign;
3307         }
3308         
3309         return cfg;
3310     }
3311    
3312 });
3313
3314  
3315
3316  /*
3317  * - LGPL
3318  *
3319  * table body
3320  * 
3321  */
3322
3323 /**
3324  * @class Roo.bootstrap.TableBody
3325  * @extends Roo.bootstrap.Component
3326  * Bootstrap TableBody class
3327  * @cfg {String} cls element class
3328  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3329  * @cfg {String} align Aligns the content inside the element
3330  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3331  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3332  * 
3333  * @constructor
3334  * Create a new TableBody
3335  * @param {Object} config The config object
3336  */
3337
3338 Roo.bootstrap.TableBody = function(config){
3339     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3340 };
3341
3342 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3343     
3344     cls: false,
3345     tag: false,
3346     align: false,
3347     charoff: false,
3348     valign: false,
3349     
3350     getAutoCreate : function(){
3351         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3352         
3353         cfg = {
3354             tag: 'tbody'
3355         }
3356             
3357         if (this.cls) {
3358             cfg.cls=this.cls
3359         }
3360         if(this.tag){
3361             cfg.tag = this.tag;
3362         }
3363         
3364         if(this.align){
3365             cfg.align = this.align;
3366         }
3367         if(this.charoff){
3368             cfg.charoff = this.charoff;
3369         }
3370         if(this.valign){
3371             cfg.valign = this.valign;
3372         }
3373         
3374         return cfg;
3375     }
3376     
3377     
3378 //    initEvents : function()
3379 //    {
3380 //        
3381 //        if(!this.store){
3382 //            return;
3383 //        }
3384 //        
3385 //        this.store = Roo.factory(this.store, Roo.data);
3386 //        this.store.on('load', this.onLoad, this);
3387 //        
3388 //        this.store.load();
3389 //        
3390 //    },
3391 //    
3392 //    onLoad: function () 
3393 //    {   
3394 //        this.fireEvent('load', this);
3395 //    }
3396 //    
3397 //   
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412
3413 // as we use this in bootstrap.
3414 Roo.namespace('Roo.form');
3415  /**
3416  * @class Roo.form.Action
3417  * Internal Class used to handle form actions
3418  * @constructor
3419  * @param {Roo.form.BasicForm} el The form element or its id
3420  * @param {Object} config Configuration options
3421  */
3422
3423  
3424  
3425 // define the action interface
3426 Roo.form.Action = function(form, options){
3427     this.form = form;
3428     this.options = options || {};
3429 };
3430 /**
3431  * Client Validation Failed
3432  * @const 
3433  */
3434 Roo.form.Action.CLIENT_INVALID = 'client';
3435 /**
3436  * Server Validation Failed
3437  * @const 
3438  */
3439 Roo.form.Action.SERVER_INVALID = 'server';
3440  /**
3441  * Connect to Server Failed
3442  * @const 
3443  */
3444 Roo.form.Action.CONNECT_FAILURE = 'connect';
3445 /**
3446  * Reading Data from Server Failed
3447  * @const 
3448  */
3449 Roo.form.Action.LOAD_FAILURE = 'load';
3450
3451 Roo.form.Action.prototype = {
3452     type : 'default',
3453     failureType : undefined,
3454     response : undefined,
3455     result : undefined,
3456
3457     // interface method
3458     run : function(options){
3459
3460     },
3461
3462     // interface method
3463     success : function(response){
3464
3465     },
3466
3467     // interface method
3468     handleResponse : function(response){
3469
3470     },
3471
3472     // default connection failure
3473     failure : function(response){
3474         
3475         this.response = response;
3476         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3477         this.form.afterAction(this, false);
3478     },
3479
3480     processResponse : function(response){
3481         this.response = response;
3482         if(!response.responseText){
3483             return true;
3484         }
3485         this.result = this.handleResponse(response);
3486         return this.result;
3487     },
3488
3489     // utility functions used internally
3490     getUrl : function(appendParams){
3491         var url = this.options.url || this.form.url || this.form.el.dom.action;
3492         if(appendParams){
3493             var p = this.getParams();
3494             if(p){
3495                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3496             }
3497         }
3498         return url;
3499     },
3500
3501     getMethod : function(){
3502         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3503     },
3504
3505     getParams : function(){
3506         var bp = this.form.baseParams;
3507         var p = this.options.params;
3508         if(p){
3509             if(typeof p == "object"){
3510                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3511             }else if(typeof p == 'string' && bp){
3512                 p += '&' + Roo.urlEncode(bp);
3513             }
3514         }else if(bp){
3515             p = Roo.urlEncode(bp);
3516         }
3517         return p;
3518     },
3519
3520     createCallback : function(){
3521         return {
3522             success: this.success,
3523             failure: this.failure,
3524             scope: this,
3525             timeout: (this.form.timeout*1000),
3526             upload: this.form.fileUpload ? this.success : undefined
3527         };
3528     }
3529 };
3530
3531 Roo.form.Action.Submit = function(form, options){
3532     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3533 };
3534
3535 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3536     type : 'submit',
3537
3538     haveProgress : false,
3539     uploadComplete : false,
3540     
3541     // uploadProgress indicator.
3542     uploadProgress : function()
3543     {
3544         if (!this.form.progressUrl) {
3545             return;
3546         }
3547         
3548         if (!this.haveProgress) {
3549             Roo.MessageBox.progress("Uploading", "Uploading");
3550         }
3551         if (this.uploadComplete) {
3552            Roo.MessageBox.hide();
3553            return;
3554         }
3555         
3556         this.haveProgress = true;
3557    
3558         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3559         
3560         var c = new Roo.data.Connection();
3561         c.request({
3562             url : this.form.progressUrl,
3563             params: {
3564                 id : uid
3565             },
3566             method: 'GET',
3567             success : function(req){
3568                //console.log(data);
3569                 var rdata = false;
3570                 var edata;
3571                 try  {
3572                    rdata = Roo.decode(req.responseText)
3573                 } catch (e) {
3574                     Roo.log("Invalid data from server..");
3575                     Roo.log(edata);
3576                     return;
3577                 }
3578                 if (!rdata || !rdata.success) {
3579                     Roo.log(rdata);
3580                     Roo.MessageBox.alert(Roo.encode(rdata));
3581                     return;
3582                 }
3583                 var data = rdata.data;
3584                 
3585                 if (this.uploadComplete) {
3586                    Roo.MessageBox.hide();
3587                    return;
3588                 }
3589                    
3590                 if (data){
3591                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3592                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3593                     );
3594                 }
3595                 this.uploadProgress.defer(2000,this);
3596             },
3597        
3598             failure: function(data) {
3599                 Roo.log('progress url failed ');
3600                 Roo.log(data);
3601             },
3602             scope : this
3603         });
3604            
3605     },
3606     
3607     
3608     run : function()
3609     {
3610         // run get Values on the form, so it syncs any secondary forms.
3611         this.form.getValues();
3612         
3613         var o = this.options;
3614         var method = this.getMethod();
3615         var isPost = method == 'POST';
3616         if(o.clientValidation === false || this.form.isValid()){
3617             
3618             if (this.form.progressUrl) {
3619                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3620                     (new Date() * 1) + '' + Math.random());
3621                     
3622             } 
3623             
3624             
3625             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3626                 form:this.form.el.dom,
3627                 url:this.getUrl(!isPost),
3628                 method: method,
3629                 params:isPost ? this.getParams() : null,
3630                 isUpload: this.form.fileUpload
3631             }));
3632             
3633             this.uploadProgress();
3634
3635         }else if (o.clientValidation !== false){ // client validation failed
3636             this.failureType = Roo.form.Action.CLIENT_INVALID;
3637             this.form.afterAction(this, false);
3638         }
3639     },
3640
3641     success : function(response)
3642     {
3643         this.uploadComplete= true;
3644         if (this.haveProgress) {
3645             Roo.MessageBox.hide();
3646         }
3647         
3648         
3649         var result = this.processResponse(response);
3650         if(result === true || result.success){
3651             this.form.afterAction(this, true);
3652             return;
3653         }
3654         if(result.errors){
3655             this.form.markInvalid(result.errors);
3656             this.failureType = Roo.form.Action.SERVER_INVALID;
3657         }
3658         this.form.afterAction(this, false);
3659     },
3660     failure : function(response)
3661     {
3662         this.uploadComplete= true;
3663         if (this.haveProgress) {
3664             Roo.MessageBox.hide();
3665         }
3666         
3667         this.response = response;
3668         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3669         this.form.afterAction(this, false);
3670     },
3671     
3672     handleResponse : function(response){
3673         if(this.form.errorReader){
3674             var rs = this.form.errorReader.read(response);
3675             var errors = [];
3676             if(rs.records){
3677                 for(var i = 0, len = rs.records.length; i < len; i++) {
3678                     var r = rs.records[i];
3679                     errors[i] = r.data;
3680                 }
3681             }
3682             if(errors.length < 1){
3683                 errors = null;
3684             }
3685             return {
3686                 success : rs.success,
3687                 errors : errors
3688             };
3689         }
3690         var ret = false;
3691         try {
3692             ret = Roo.decode(response.responseText);
3693         } catch (e) {
3694             ret = {
3695                 success: false,
3696                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3697                 errors : []
3698             };
3699         }
3700         return ret;
3701         
3702     }
3703 });
3704
3705
3706 Roo.form.Action.Load = function(form, options){
3707     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3708     this.reader = this.form.reader;
3709 };
3710
3711 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3712     type : 'load',
3713
3714     run : function(){
3715         
3716         Roo.Ajax.request(Roo.apply(
3717                 this.createCallback(), {
3718                     method:this.getMethod(),
3719                     url:this.getUrl(false),
3720                     params:this.getParams()
3721         }));
3722     },
3723
3724     success : function(response){
3725         
3726         var result = this.processResponse(response);
3727         if(result === true || !result.success || !result.data){
3728             this.failureType = Roo.form.Action.LOAD_FAILURE;
3729             this.form.afterAction(this, false);
3730             return;
3731         }
3732         this.form.clearInvalid();
3733         this.form.setValues(result.data);
3734         this.form.afterAction(this, true);
3735     },
3736
3737     handleResponse : function(response){
3738         if(this.form.reader){
3739             var rs = this.form.reader.read(response);
3740             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3741             return {
3742                 success : rs.success,
3743                 data : data
3744             };
3745         }
3746         return Roo.decode(response.responseText);
3747     }
3748 });
3749
3750 Roo.form.Action.ACTION_TYPES = {
3751     'load' : Roo.form.Action.Load,
3752     'submit' : Roo.form.Action.Submit
3753 };/*
3754  * - LGPL
3755  *
3756  * form
3757  * 
3758  */
3759
3760 /**
3761  * @class Roo.bootstrap.Form
3762  * @extends Roo.bootstrap.Component
3763  * Bootstrap Form class
3764  * @cfg {String} method  GET | POST (default POST)
3765  * @cfg {String} labelAlign top | left (default top)
3766   * @cfg {String} align left  | right - for navbars
3767
3768  * 
3769  * @constructor
3770  * Create a new Form
3771  * @param {Object} config The config object
3772  */
3773
3774
3775 Roo.bootstrap.Form = function(config){
3776     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3777     this.addEvents({
3778         /**
3779          * @event clientvalidation
3780          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3781          * @param {Form} this
3782          * @param {Boolean} valid true if the form has passed client-side validation
3783          */
3784         clientvalidation: true,
3785         /**
3786          * @event beforeaction
3787          * Fires before any action is performed. Return false to cancel the action.
3788          * @param {Form} this
3789          * @param {Action} action The action to be performed
3790          */
3791         beforeaction: true,
3792         /**
3793          * @event actionfailed
3794          * Fires when an action fails.
3795          * @param {Form} this
3796          * @param {Action} action The action that failed
3797          */
3798         actionfailed : true,
3799         /**
3800          * @event actioncomplete
3801          * Fires when an action is completed.
3802          * @param {Form} this
3803          * @param {Action} action The action that completed
3804          */
3805         actioncomplete : true
3806     });
3807     
3808 };
3809
3810 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3811       
3812      /**
3813      * @cfg {String} method
3814      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3815      */
3816     method : 'POST',
3817     /**
3818      * @cfg {String} url
3819      * The URL to use for form actions if one isn't supplied in the action options.
3820      */
3821     /**
3822      * @cfg {Boolean} fileUpload
3823      * Set to true if this form is a file upload.
3824      */
3825      
3826     /**
3827      * @cfg {Object} baseParams
3828      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3829      */
3830       
3831     /**
3832      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3833      */
3834     timeout: 30,
3835     /**
3836      * @cfg {Sting} align (left|right) for navbar forms
3837      */
3838     align : 'left',
3839
3840     // private
3841     activeAction : null,
3842  
3843     /**
3844      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3845      * element by passing it or its id or mask the form itself by passing in true.
3846      * @type Mixed
3847      */
3848     waitMsgTarget : false,
3849     
3850      
3851     
3852     /**
3853      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3854      * element by passing it or its id or mask the form itself by passing in true.
3855      * @type Mixed
3856      */
3857     
3858     getAutoCreate : function(){
3859         
3860         var cfg = {
3861             tag: 'form',
3862             method : this.method || 'POST',
3863             id : this.id || Roo.id(),
3864             cls : ''
3865         }
3866         if (this.parent().xtype.match(/^Nav/)) {
3867             cfg.cls = 'navbar-form navbar-' + this.align;
3868             
3869         }
3870         
3871         if (this.labelAlign == 'left' ) {
3872             cfg.cls += ' form-horizontal';
3873         }
3874         
3875         
3876         return cfg;
3877     },
3878     initEvents : function()
3879     {
3880         this.el.on('submit', this.onSubmit, this);
3881         
3882         
3883     },
3884     // private
3885     onSubmit : function(e){
3886         e.stopEvent();
3887     },
3888     
3889      /**
3890      * Returns true if client-side validation on the form is successful.
3891      * @return Boolean
3892      */
3893     isValid : function(){
3894         var items = this.getItems();
3895         var valid = true;
3896         items.each(function(f){
3897            if(!f.validate()){
3898                valid = false;
3899                
3900            }
3901         });
3902         return valid;
3903     },
3904     /**
3905      * Returns true if any fields in this form have changed since their original load.
3906      * @return Boolean
3907      */
3908     isDirty : function(){
3909         var dirty = false;
3910         var items = this.getItems();
3911         items.each(function(f){
3912            if(f.isDirty()){
3913                dirty = true;
3914                return false;
3915            }
3916            return true;
3917         });
3918         return dirty;
3919     },
3920      /**
3921      * Performs a predefined action (submit or load) or custom actions you define on this form.
3922      * @param {String} actionName The name of the action type
3923      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3924      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3925      * accept other config options):
3926      * <pre>
3927 Property          Type             Description
3928 ----------------  ---------------  ----------------------------------------------------------------------------------
3929 url               String           The url for the action (defaults to the form's url)
3930 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3931 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3932 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3933                                    validate the form on the client (defaults to false)
3934      * </pre>
3935      * @return {BasicForm} this
3936      */
3937     doAction : function(action, options){
3938         if(typeof action == 'string'){
3939             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3940         }
3941         if(this.fireEvent('beforeaction', this, action) !== false){
3942             this.beforeAction(action);
3943             action.run.defer(100, action);
3944         }
3945         return this;
3946     },
3947     
3948     // private
3949     beforeAction : function(action){
3950         var o = action.options;
3951         
3952         // not really supported yet.. ??
3953         
3954         //if(this.waitMsgTarget === true){
3955             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3956         //}else if(this.waitMsgTarget){
3957         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3958         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3959         //}else {
3960         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3961        // }
3962          
3963     },
3964
3965     // private
3966     afterAction : function(action, success){
3967         this.activeAction = null;
3968         var o = action.options;
3969         
3970         //if(this.waitMsgTarget === true){
3971             this.el.unmask();
3972         //}else if(this.waitMsgTarget){
3973         //    this.waitMsgTarget.unmask();
3974         //}else{
3975         //    Roo.MessageBox.updateProgress(1);
3976         //    Roo.MessageBox.hide();
3977        // }
3978         // 
3979         if(success){
3980             if(o.reset){
3981                 this.reset();
3982             }
3983             Roo.callback(o.success, o.scope, [this, action]);
3984             this.fireEvent('actioncomplete', this, action);
3985             
3986         }else{
3987             
3988             // failure condition..
3989             // we have a scenario where updates need confirming.
3990             // eg. if a locking scenario exists..
3991             // we look for { errors : { needs_confirm : true }} in the response.
3992             if (
3993                 (typeof(action.result) != 'undefined')  &&
3994                 (typeof(action.result.errors) != 'undefined')  &&
3995                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3996            ){
3997                 var _t = this;
3998                 Roo.log("not supported yet");
3999                  /*
4000                 
4001                 Roo.MessageBox.confirm(
4002                     "Change requires confirmation",
4003                     action.result.errorMsg,
4004                     function(r) {
4005                         if (r != 'yes') {
4006                             return;
4007                         }
4008                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4009                     }
4010                     
4011                 );
4012                 */
4013                 
4014                 
4015                 return;
4016             }
4017             
4018             Roo.callback(o.failure, o.scope, [this, action]);
4019             // show an error message if no failed handler is set..
4020             if (!this.hasListener('actionfailed')) {
4021                 Roo.log("need to add dialog support");
4022                 /*
4023                 Roo.MessageBox.alert("Error",
4024                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4025                         action.result.errorMsg :
4026                         "Saving Failed, please check your entries or try again"
4027                 );
4028                 */
4029             }
4030             
4031             this.fireEvent('actionfailed', this, action);
4032         }
4033         
4034     },
4035     /**
4036      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4037      * @param {String} id The value to search for
4038      * @return Field
4039      */
4040     findField : function(id){
4041         var items = this.getItems();
4042         var field = items.get(id);
4043         if(!field){
4044              items.each(function(f){
4045                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4046                     field = f;
4047                     return false;
4048                 }
4049                 return true;
4050             });
4051         }
4052         return field || null;
4053     },
4054      /**
4055      * Mark fields in this form invalid in bulk.
4056      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4057      * @return {BasicForm} this
4058      */
4059     markInvalid : function(errors){
4060         if(errors instanceof Array){
4061             for(var i = 0, len = errors.length; i < len; i++){
4062                 var fieldError = errors[i];
4063                 var f = this.findField(fieldError.id);
4064                 if(f){
4065                     f.markInvalid(fieldError.msg);
4066                 }
4067             }
4068         }else{
4069             var field, id;
4070             for(id in errors){
4071                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4072                     field.markInvalid(errors[id]);
4073                 }
4074             }
4075         }
4076         //Roo.each(this.childForms || [], function (f) {
4077         //    f.markInvalid(errors);
4078         //});
4079         
4080         return this;
4081     },
4082
4083     /**
4084      * Set values for fields in this form in bulk.
4085      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4086      * @return {BasicForm} this
4087      */
4088     setValues : function(values){
4089         if(values instanceof Array){ // array of objects
4090             for(var i = 0, len = values.length; i < len; i++){
4091                 var v = values[i];
4092                 var f = this.findField(v.id);
4093                 if(f){
4094                     f.setValue(v.value);
4095                     if(this.trackResetOnLoad){
4096                         f.originalValue = f.getValue();
4097                     }
4098                 }
4099             }
4100         }else{ // object hash
4101             var field, id;
4102             for(id in values){
4103                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4104                     
4105                     if (field.setFromData && 
4106                         field.valueField && 
4107                         field.displayField &&
4108                         // combos' with local stores can 
4109                         // be queried via setValue()
4110                         // to set their value..
4111                         (field.store && !field.store.isLocal)
4112                         ) {
4113                         // it's a combo
4114                         var sd = { };
4115                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4116                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4117                         field.setFromData(sd);
4118                         
4119                     } else {
4120                         field.setValue(values[id]);
4121                     }
4122                     
4123                     
4124                     if(this.trackResetOnLoad){
4125                         field.originalValue = field.getValue();
4126                     }
4127                 }
4128             }
4129         }
4130          
4131         //Roo.each(this.childForms || [], function (f) {
4132         //    f.setValues(values);
4133         //});
4134                 
4135         return this;
4136     },
4137
4138     /**
4139      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4140      * they are returned as an array.
4141      * @param {Boolean} asString
4142      * @return {Object}
4143      */
4144     getValues : function(asString){
4145         //if (this.childForms) {
4146             // copy values from the child forms
4147         //    Roo.each(this.childForms, function (f) {
4148         //        this.setValues(f.getValues());
4149         //    }, this);
4150         //}
4151         
4152         
4153         
4154         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4155         if(asString === true){
4156             return fs;
4157         }
4158         return Roo.urlDecode(fs);
4159     },
4160     
4161     /**
4162      * Returns the fields in this form as an object with key/value pairs. 
4163      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4164      * @return {Object}
4165      */
4166     getFieldValues : function(with_hidden)
4167     {
4168         var items = this.getItems();
4169         var ret = {};
4170         items.each(function(f){
4171             if (!f.getName()) {
4172                 return;
4173             }
4174             var v = f.getValue();
4175             if (f.inputType =='radio') {
4176                 if (typeof(ret[f.getName()]) == 'undefined') {
4177                     ret[f.getName()] = ''; // empty..
4178                 }
4179                 
4180                 if (!f.el.dom.checked) {
4181                     return;
4182                     
4183                 }
4184                 v = f.el.dom.value;
4185                 
4186             }
4187             
4188             // not sure if this supported any more..
4189             if ((typeof(v) == 'object') && f.getRawValue) {
4190                 v = f.getRawValue() ; // dates..
4191             }
4192             // combo boxes where name != hiddenName...
4193             if (f.name != f.getName()) {
4194                 ret[f.name] = f.getRawValue();
4195             }
4196             ret[f.getName()] = v;
4197         });
4198         
4199         return ret;
4200     },
4201
4202     /**
4203      * Clears all invalid messages in this form.
4204      * @return {BasicForm} this
4205      */
4206     clearInvalid : function(){
4207         var items = this.getItems();
4208         
4209         items.each(function(f){
4210            f.clearInvalid();
4211         });
4212         
4213         
4214         
4215         return this;
4216     },
4217
4218     /**
4219      * Resets this form.
4220      * @return {BasicForm} this
4221      */
4222     reset : function(){
4223         var items = this.getItems();
4224         items.each(function(f){
4225             f.reset();
4226         });
4227         
4228         Roo.each(this.childForms || [], function (f) {
4229             f.reset();
4230         });
4231        
4232         
4233         return this;
4234     },
4235     getItems : function()
4236     {
4237         var r=new Roo.util.MixedCollection(false, function(o){
4238             return o.id || (o.id = Roo.id());
4239         });
4240         var iter = function(el) {
4241             if (el.inputEl) {
4242                 r.add(el);
4243             }
4244             if (!el.items) {
4245                 return;
4246             }
4247             Roo.each(el.items,function(e) {
4248                 iter(e);
4249             });
4250             
4251             
4252         };
4253         iter(this);
4254         return r;
4255         
4256         
4257         
4258         
4259     }
4260     
4261 });
4262
4263  
4264 /*
4265  * Based on:
4266  * Ext JS Library 1.1.1
4267  * Copyright(c) 2006-2007, Ext JS, LLC.
4268  *
4269  * Originally Released Under LGPL - original licence link has changed is not relivant.
4270  *
4271  * Fork - LGPL
4272  * <script type="text/javascript">
4273  */
4274 /**
4275  * @class Roo.form.VTypes
4276  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4277  * @singleton
4278  */
4279 Roo.form.VTypes = function(){
4280     // closure these in so they are only created once.
4281     var alpha = /^[a-zA-Z_]+$/;
4282     var alphanum = /^[a-zA-Z0-9_]+$/;
4283     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4284     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4285
4286     // All these messages and functions are configurable
4287     return {
4288         /**
4289          * The function used to validate email addresses
4290          * @param {String} value The email address
4291          */
4292         'email' : function(v){
4293             return email.test(v);
4294         },
4295         /**
4296          * The error text to display when the email validation function returns false
4297          * @type String
4298          */
4299         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4300         /**
4301          * The keystroke filter mask to be applied on email input
4302          * @type RegExp
4303          */
4304         'emailMask' : /[a-z0-9_\.\-@]/i,
4305
4306         /**
4307          * The function used to validate URLs
4308          * @param {String} value The URL
4309          */
4310         'url' : function(v){
4311             return url.test(v);
4312         },
4313         /**
4314          * The error text to display when the url validation function returns false
4315          * @type String
4316          */
4317         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4318         
4319         /**
4320          * The function used to validate alpha values
4321          * @param {String} value The value
4322          */
4323         'alpha' : function(v){
4324             return alpha.test(v);
4325         },
4326         /**
4327          * The error text to display when the alpha validation function returns false
4328          * @type String
4329          */
4330         'alphaText' : 'This field should only contain letters and _',
4331         /**
4332          * The keystroke filter mask to be applied on alpha input
4333          * @type RegExp
4334          */
4335         'alphaMask' : /[a-z_]/i,
4336
4337         /**
4338          * The function used to validate alphanumeric values
4339          * @param {String} value The value
4340          */
4341         'alphanum' : function(v){
4342             return alphanum.test(v);
4343         },
4344         /**
4345          * The error text to display when the alphanumeric validation function returns false
4346          * @type String
4347          */
4348         'alphanumText' : 'This field should only contain letters, numbers and _',
4349         /**
4350          * The keystroke filter mask to be applied on alphanumeric input
4351          * @type RegExp
4352          */
4353         'alphanumMask' : /[a-z0-9_]/i
4354     };
4355 }();/*
4356  * - LGPL
4357  *
4358  * Input
4359  * 
4360  */
4361
4362 /**
4363  * @class Roo.bootstrap.Input
4364  * @extends Roo.bootstrap.Component
4365  * Bootstrap Input class
4366  * @cfg {Boolean} disabled is it disabled
4367  * @cfg {String} fieldLabel - the label associated
4368  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4369  * @cfg {String} name name of the input
4370  * @cfg {string} fieldLabel - the label associated
4371  * @cfg {string}  inputType - input / file submit ...
4372  * @cfg {string} placeholder - placeholder to put in text.
4373  * @cfg {string}  before - input group add on before
4374  * @cfg {string} after - input group add on after
4375  * @cfg {string} size - (lg|sm) or leave empty..
4376  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4377  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4378  * @cfg {Number} md colspan out of 12 for computer-sized screens
4379  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4380  * @cfg {string} value default value of the input
4381  * @cfg {Number} labelWidth set the width of label (0-12)
4382  * @cfg {String} labelAlign (top|left)
4383  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4384  * 
4385  * 
4386  * @constructor
4387  * Create a new Input
4388  * @param {Object} config The config object
4389  */
4390
4391 Roo.bootstrap.Input = function(config){
4392     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4393    
4394         this.addEvents({
4395             /**
4396              * @event focus
4397              * Fires when this field receives input focus.
4398              * @param {Roo.form.Field} this
4399              */
4400             focus : true,
4401             /**
4402              * @event blur
4403              * Fires when this field loses input focus.
4404              * @param {Roo.form.Field} this
4405              */
4406             blur : true,
4407             /**
4408              * @event specialkey
4409              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4410              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4411              * @param {Roo.form.Field} this
4412              * @param {Roo.EventObject} e The event object
4413              */
4414             specialkey : true,
4415             /**
4416              * @event change
4417              * Fires just before the field blurs if the field value has changed.
4418              * @param {Roo.form.Field} this
4419              * @param {Mixed} newValue The new value
4420              * @param {Mixed} oldValue The original value
4421              */
4422             change : true,
4423             /**
4424              * @event invalid
4425              * Fires after the field has been marked as invalid.
4426              * @param {Roo.form.Field} this
4427              * @param {String} msg The validation message
4428              */
4429             invalid : true,
4430             /**
4431              * @event valid
4432              * Fires after the field has been validated with no errors.
4433              * @param {Roo.form.Field} this
4434              */
4435             valid : true,
4436              /**
4437              * @event keyup
4438              * Fires after the key up
4439              * @param {Roo.form.Field} this
4440              * @param {Roo.EventObject}  e The event Object
4441              */
4442             keyup : true
4443         });
4444 };
4445
4446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4447      /**
4448      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4449       automatic validation (defaults to "keyup").
4450      */
4451     validationEvent : "keyup",
4452      /**
4453      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4454      */
4455     validateOnBlur : true,
4456     /**
4457      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4458      */
4459     validationDelay : 250,
4460      /**
4461      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4462      */
4463     focusClass : "x-form-focus",  // not needed???
4464     
4465        
4466     /**
4467      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4468      */
4469     invalidClass : "has-error",
4470     
4471     /**
4472      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4473      */
4474     selectOnFocus : false,
4475     
4476      /**
4477      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4478      */
4479     maskRe : null,
4480        /**
4481      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4482      */
4483     vtype : null,
4484     
4485       /**
4486      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4487      */
4488     disableKeyFilter : false,
4489     
4490        /**
4491      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4492      */
4493     disabled : false,
4494      /**
4495      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4496      */
4497     allowBlank : true,
4498     /**
4499      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4500      */
4501     blankText : "This field is required",
4502     
4503      /**
4504      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4505      */
4506     minLength : 0,
4507     /**
4508      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4509      */
4510     maxLength : Number.MAX_VALUE,
4511     /**
4512      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4513      */
4514     minLengthText : "The minimum length for this field is {0}",
4515     /**
4516      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4517      */
4518     maxLengthText : "The maximum length for this field is {0}",
4519   
4520     
4521     /**
4522      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4523      * If available, this function will be called only after the basic validators all return true, and will be passed the
4524      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4525      */
4526     validator : null,
4527     /**
4528      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4529      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4530      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4531      */
4532     regex : null,
4533     /**
4534      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4535      */
4536     regexText : "",
4537     
4538     
4539     
4540     fieldLabel : '',
4541     inputType : 'text',
4542     
4543     name : false,
4544     placeholder: false,
4545     before : false,
4546     after : false,
4547     size : false,
4548     // private
4549     hasFocus : false,
4550     preventMark: false,
4551     isFormField : true,
4552     value : '',
4553     labelWidth : 2,
4554     labelAlign : false,
4555     readOnly : false,
4556     
4557     parentLabelAlign : function()
4558     {
4559         var parent = this;
4560         while (parent.parent()) {
4561             parent = parent.parent();
4562             if (typeof(parent.labelAlign) !='undefined') {
4563                 return parent.labelAlign;
4564             }
4565         }
4566         return 'left';
4567         
4568     },
4569     
4570     getAutoCreate : function(){
4571         
4572         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4573         
4574         var id = Roo.id();
4575         
4576         var cfg = {};
4577         
4578         if(this.inputType != 'hidden'){
4579             cfg.cls = 'form-group' //input-group
4580         }
4581         
4582         var input =  {
4583             tag: 'input',
4584             id : id,
4585             type : this.inputType,
4586             value : this.value,
4587             cls : 'form-control',
4588             placeholder : this.placeholder || ''
4589             
4590         };
4591         
4592         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4593             input.maxLength = this.maxLength;
4594         }
4595         
4596         if (this.disabled) {
4597             input.disabled=true;
4598         }
4599         
4600         if (this.readOnly) {
4601             input.readonly=true;
4602         }
4603         
4604         if (this.name) {
4605             input.name = this.name;
4606         }
4607         if (this.size) {
4608             input.cls += ' input-' + this.size;
4609         }
4610         var settings=this;
4611         ['xs','sm','md','lg'].map(function(size){
4612             if (settings[size]) {
4613                 cfg.cls += ' col-' + size + '-' + settings[size];
4614             }
4615         });
4616         
4617         var inputblock = input;
4618         
4619         if (this.before || this.after) {
4620             
4621             inputblock = {
4622                 cls : 'input-group',
4623                 cn :  [] 
4624             };
4625             if (this.before) {
4626                 inputblock.cn.push({
4627                     tag :'span',
4628                     cls : 'input-group-addon',
4629                     html : this.before
4630                 });
4631             }
4632             inputblock.cn.push(input);
4633             if (this.after) {
4634                 inputblock.cn.push({
4635                     tag :'span',
4636                     cls : 'input-group-addon',
4637                     html : this.after
4638                 });
4639             }
4640             
4641         };
4642         
4643         if (align ==='left' && this.fieldLabel.length) {
4644                 Roo.log("left and has label");
4645                 cfg.cn = [
4646                     
4647                     {
4648                         tag: 'label',
4649                         'for' :  id,
4650                         cls : 'control-label col-sm-' + this.labelWidth,
4651                         html : this.fieldLabel
4652                         
4653                     },
4654                     {
4655                         cls : "col-sm-" + (12 - this.labelWidth), 
4656                         cn: [
4657                             inputblock
4658                         ]
4659                     }
4660                     
4661                 ];
4662         } else if ( this.fieldLabel.length) {
4663                 Roo.log(" label");
4664                  cfg.cn = [
4665                    
4666                     {
4667                         tag: 'label',
4668                         //cls : 'input-group-addon',
4669                         html : this.fieldLabel
4670                         
4671                     },
4672                     
4673                     inputblock
4674                     
4675                 ];
4676
4677         } else {
4678             
4679                 Roo.log(" no label && no align");
4680                 cfg.cn = [
4681                     
4682                         inputblock
4683                     
4684                 ];
4685                 
4686                 
4687         };
4688         Roo.log('input-parentType: ' + this.parentType);
4689         
4690         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4691            cfg.cls += ' navbar-form';
4692            Roo.log(cfg);
4693         }
4694         
4695         return cfg;
4696         
4697     },
4698     /**
4699      * return the real input element.
4700      */
4701     inputEl: function ()
4702     {
4703         return this.el.select('input.form-control',true).first();
4704     },
4705     setDisabled : function(v)
4706     {
4707         var i  = this.inputEl().dom;
4708         if (!v) {
4709             i.removeAttribute('disabled');
4710             return;
4711             
4712         }
4713         i.setAttribute('disabled','true');
4714     },
4715     initEvents : function()
4716     {
4717         
4718         this.inputEl().on("keydown" , this.fireKey,  this);
4719         this.inputEl().on("focus", this.onFocus,  this);
4720         this.inputEl().on("blur", this.onBlur,  this);
4721         
4722         this.inputEl().relayEvent('keyup', this);
4723
4724         // reference to original value for reset
4725         this.originalValue = this.getValue();
4726         //Roo.form.TextField.superclass.initEvents.call(this);
4727         if(this.validationEvent == 'keyup'){
4728             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4729             this.inputEl().on('keyup', this.filterValidation, this);
4730         }
4731         else if(this.validationEvent !== false){
4732             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4733         }
4734         
4735         if(this.selectOnFocus){
4736             this.on("focus", this.preFocus, this);
4737             
4738         }
4739         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4740             this.inputEl().on("keypress", this.filterKeys, this);
4741         }
4742        /* if(this.grow){
4743             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4744             this.el.on("click", this.autoSize,  this);
4745         }
4746         */
4747         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4748             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4749         }
4750         
4751     },
4752     filterValidation : function(e){
4753         if(!e.isNavKeyPress()){
4754             this.validationTask.delay(this.validationDelay);
4755         }
4756     },
4757      /**
4758      * Validates the field value
4759      * @return {Boolean} True if the value is valid, else false
4760      */
4761     validate : function(){
4762         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4763         if(this.disabled || this.validateValue(this.getRawValue())){
4764             this.clearInvalid();
4765             return true;
4766         }
4767         return false;
4768     },
4769     
4770     
4771     /**
4772      * Validates a value according to the field's validation rules and marks the field as invalid
4773      * if the validation fails
4774      * @param {Mixed} value The value to validate
4775      * @return {Boolean} True if the value is valid, else false
4776      */
4777     validateValue : function(value){
4778         if(value.length < 1)  { // if it's blank
4779              if(this.allowBlank){
4780                 this.clearInvalid();
4781                 return true;
4782              }else{
4783                 this.markInvalid(this.blankText);
4784                 return false;
4785              }
4786         }
4787         if(value.length < this.minLength){
4788             this.markInvalid(String.format(this.minLengthText, this.minLength));
4789             return false;
4790         }
4791         if(value.length > this.maxLength){
4792             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4793             return false;
4794         }
4795         if(this.vtype){
4796             var vt = Roo.form.VTypes;
4797             if(!vt[this.vtype](value, this)){
4798                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4799                 return false;
4800             }
4801         }
4802         if(typeof this.validator == "function"){
4803             var msg = this.validator(value);
4804             if(msg !== true){
4805                 this.markInvalid(msg);
4806                 return false;
4807             }
4808         }
4809         if(this.regex && !this.regex.test(value)){
4810             this.markInvalid(this.regexText);
4811             return false;
4812         }
4813         return true;
4814     },
4815
4816     
4817     
4818      // private
4819     fireKey : function(e){
4820         //Roo.log('field ' + e.getKey());
4821         if(e.isNavKeyPress()){
4822             this.fireEvent("specialkey", this, e);
4823         }
4824     },
4825     focus : function (selectText){
4826         if(this.rendered){
4827             this.inputEl().focus();
4828             if(selectText === true){
4829                 this.inputEl().dom.select();
4830             }
4831         }
4832         return this;
4833     } ,
4834     
4835     onFocus : function(){
4836         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4837            // this.el.addClass(this.focusClass);
4838         }
4839         if(!this.hasFocus){
4840             this.hasFocus = true;
4841             this.startValue = this.getValue();
4842             this.fireEvent("focus", this);
4843         }
4844     },
4845     
4846     beforeBlur : Roo.emptyFn,
4847
4848     
4849     // private
4850     onBlur : function(){
4851         this.beforeBlur();
4852         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4853             //this.el.removeClass(this.focusClass);
4854         }
4855         this.hasFocus = false;
4856         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4857             this.validate();
4858         }
4859         var v = this.getValue();
4860         if(String(v) !== String(this.startValue)){
4861             this.fireEvent('change', this, v, this.startValue);
4862         }
4863         this.fireEvent("blur", this);
4864     },
4865     
4866     /**
4867      * Resets the current field value to the originally loaded value and clears any validation messages
4868      */
4869     reset : function(){
4870         this.setValue(this.originalValue);
4871         this.clearInvalid();
4872     },
4873      /**
4874      * Returns the name of the field
4875      * @return {Mixed} name The name field
4876      */
4877     getName: function(){
4878         return this.name;
4879     },
4880      /**
4881      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4882      * @return {Mixed} value The field value
4883      */
4884     getValue : function(){
4885         return this.inputEl().getValue();
4886     },
4887     /**
4888      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4889      * @return {Mixed} value The field value
4890      */
4891     getRawValue : function(){
4892         var v = this.inputEl().getValue();
4893         
4894         return v;
4895     },
4896     
4897     /**
4898      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4899      * @param {Mixed} value The value to set
4900      */
4901     setRawValue : function(v){
4902         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4903     },
4904     
4905     selectText : function(start, end){
4906         var v = this.getRawValue();
4907         if(v.length > 0){
4908             start = start === undefined ? 0 : start;
4909             end = end === undefined ? v.length : end;
4910             var d = this.inputEl().dom;
4911             if(d.setSelectionRange){
4912                 d.setSelectionRange(start, end);
4913             }else if(d.createTextRange){
4914                 var range = d.createTextRange();
4915                 range.moveStart("character", start);
4916                 range.moveEnd("character", v.length-end);
4917                 range.select();
4918             }
4919         }
4920     },
4921     
4922     /**
4923      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4924      * @param {Mixed} value The value to set
4925      */
4926     setValue : function(v){
4927         this.value = v;
4928         if(this.rendered){
4929             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4930             this.validate();
4931         }
4932     },
4933     
4934     /*
4935     processValue : function(value){
4936         if(this.stripCharsRe){
4937             var newValue = value.replace(this.stripCharsRe, '');
4938             if(newValue !== value){
4939                 this.setRawValue(newValue);
4940                 return newValue;
4941             }
4942         }
4943         return value;
4944     },
4945   */
4946     preFocus : function(){
4947         
4948         if(this.selectOnFocus){
4949             this.inputEl().dom.select();
4950         }
4951     },
4952     filterKeys : function(e){
4953         var k = e.getKey();
4954         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4955             return;
4956         }
4957         var c = e.getCharCode(), cc = String.fromCharCode(c);
4958         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4959             return;
4960         }
4961         if(!this.maskRe.test(cc)){
4962             e.stopEvent();
4963         }
4964     },
4965      /**
4966      * Clear any invalid styles/messages for this field
4967      */
4968     clearInvalid : function(){
4969         
4970         if(!this.el || this.preventMark){ // not rendered
4971             return;
4972         }
4973         this.el.removeClass(this.invalidClass);
4974         /*
4975         switch(this.msgTarget){
4976             case 'qtip':
4977                 this.el.dom.qtip = '';
4978                 break;
4979             case 'title':
4980                 this.el.dom.title = '';
4981                 break;
4982             case 'under':
4983                 if(this.errorEl){
4984                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4985                 }
4986                 break;
4987             case 'side':
4988                 if(this.errorIcon){
4989                     this.errorIcon.dom.qtip = '';
4990                     this.errorIcon.hide();
4991                     this.un('resize', this.alignErrorIcon, this);
4992                 }
4993                 break;
4994             default:
4995                 var t = Roo.getDom(this.msgTarget);
4996                 t.innerHTML = '';
4997                 t.style.display = 'none';
4998                 break;
4999         }
5000         */
5001         this.fireEvent('valid', this);
5002     },
5003      /**
5004      * Mark this field as invalid
5005      * @param {String} msg The validation message
5006      */
5007     markInvalid : function(msg){
5008         if(!this.el  || this.preventMark){ // not rendered
5009             return;
5010         }
5011         this.el.addClass(this.invalidClass);
5012         /*
5013         msg = msg || this.invalidText;
5014         switch(this.msgTarget){
5015             case 'qtip':
5016                 this.el.dom.qtip = msg;
5017                 this.el.dom.qclass = 'x-form-invalid-tip';
5018                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5019                     Roo.QuickTips.enable();
5020                 }
5021                 break;
5022             case 'title':
5023                 this.el.dom.title = msg;
5024                 break;
5025             case 'under':
5026                 if(!this.errorEl){
5027                     var elp = this.el.findParent('.x-form-element', 5, true);
5028                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5029                     this.errorEl.setWidth(elp.getWidth(true)-20);
5030                 }
5031                 this.errorEl.update(msg);
5032                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5033                 break;
5034             case 'side':
5035                 if(!this.errorIcon){
5036                     var elp = this.el.findParent('.x-form-element', 5, true);
5037                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5038                 }
5039                 this.alignErrorIcon();
5040                 this.errorIcon.dom.qtip = msg;
5041                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5042                 this.errorIcon.show();
5043                 this.on('resize', this.alignErrorIcon, this);
5044                 break;
5045             default:
5046                 var t = Roo.getDom(this.msgTarget);
5047                 t.innerHTML = msg;
5048                 t.style.display = this.msgDisplay;
5049                 break;
5050         }
5051         */
5052         this.fireEvent('invalid', this, msg);
5053     },
5054     // private
5055     SafariOnKeyDown : function(event)
5056     {
5057         // this is a workaround for a password hang bug on chrome/ webkit.
5058         
5059         var isSelectAll = false;
5060         
5061         if(this.inputEl().dom.selectionEnd > 0){
5062             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5063         }
5064         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5065             event.preventDefault();
5066             this.setValue('');
5067             return;
5068         }
5069         
5070         if(isSelectAll){ // backspace and delete key
5071             
5072             event.preventDefault();
5073             // this is very hacky as keydown always get's upper case.
5074             //
5075             var cc = String.fromCharCode(event.getCharCode());
5076             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5077             
5078         }
5079     },
5080     adjustWidth : function(tag, w){
5081         tag = tag.toLowerCase();
5082         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5083             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5084                 if(tag == 'input'){
5085                     return w + 2;
5086                 }
5087                 if(tag == 'textarea'){
5088                     return w-2;
5089                 }
5090             }else if(Roo.isOpera){
5091                 if(tag == 'input'){
5092                     return w + 2;
5093                 }
5094                 if(tag == 'textarea'){
5095                     return w-2;
5096                 }
5097             }
5098         }
5099         return w;
5100     }
5101     
5102 });
5103
5104  
5105 /*
5106  * - LGPL
5107  *
5108  * Input
5109  * 
5110  */
5111
5112 /**
5113  * @class Roo.bootstrap.TextArea
5114  * @extends Roo.bootstrap.Input
5115  * Bootstrap TextArea class
5116  * @cfg {Number} cols Specifies the visible width of a text area
5117  * @cfg {Number} rows Specifies the visible number of lines in a text area
5118  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5119  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5120  * @cfg {string} html text
5121  * 
5122  * @constructor
5123  * Create a new TextArea
5124  * @param {Object} config The config object
5125  */
5126
5127 Roo.bootstrap.TextArea = function(config){
5128     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5129    
5130 };
5131
5132 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5133      
5134     cols : false,
5135     rows : 5,
5136     readOnly : false,
5137     warp : 'soft',
5138     resize : false,
5139     value: false,
5140     html: false,
5141     
5142     getAutoCreate : function(){
5143         
5144         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5145         
5146         var id = Roo.id();
5147         
5148         var cfg = {};
5149         
5150         var input =  {
5151             tag: 'textarea',
5152             id : id,
5153             warp : this.warp,
5154             rows : this.rows,
5155             value : this.value || '',
5156             html: this.html || '',
5157             cls : 'form-control',
5158             placeholder : this.placeholder || '' 
5159             
5160         };
5161         
5162         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5163             input.maxLength = this.maxLength;
5164         }
5165         
5166         if(this.resize){
5167             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5168         }
5169         
5170         if(this.cols){
5171             input.cols = this.cols;
5172         }
5173         
5174         if (this.readOnly) {
5175             input.readonly = true;
5176         }
5177         
5178         if (this.name) {
5179             input.name = this.name;
5180         }
5181         
5182         if (this.size) {
5183             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5184         }
5185         
5186         var settings=this;
5187         ['xs','sm','md','lg'].map(function(size){
5188             if (settings[size]) {
5189                 cfg.cls += ' col-' + size + '-' + settings[size];
5190             }
5191         });
5192         
5193         var inputblock = input;
5194         
5195         if (this.before || this.after) {
5196             
5197             inputblock = {
5198                 cls : 'input-group',
5199                 cn :  [] 
5200             };
5201             if (this.before) {
5202                 inputblock.cn.push({
5203                     tag :'span',
5204                     cls : 'input-group-addon',
5205                     html : this.before
5206                 });
5207             }
5208             inputblock.cn.push(input);
5209             if (this.after) {
5210                 inputblock.cn.push({
5211                     tag :'span',
5212                     cls : 'input-group-addon',
5213                     html : this.after
5214                 });
5215             }
5216             
5217         }
5218         
5219         if (align ==='left' && this.fieldLabel.length) {
5220                 Roo.log("left and has label");
5221                 cfg.cn = [
5222                     
5223                     {
5224                         tag: 'label',
5225                         'for' :  id,
5226                         cls : 'control-label col-sm-' + this.labelWidth,
5227                         html : this.fieldLabel
5228                         
5229                     },
5230                     {
5231                         cls : "col-sm-" + (12 - this.labelWidth), 
5232                         cn: [
5233                             inputblock
5234                         ]
5235                     }
5236                     
5237                 ];
5238         } else if ( this.fieldLabel.length) {
5239                 Roo.log(" label");
5240                  cfg.cn = [
5241                    
5242                     {
5243                         tag: 'label',
5244                         //cls : 'input-group-addon',
5245                         html : this.fieldLabel
5246                         
5247                     },
5248                     
5249                     inputblock
5250                     
5251                 ];
5252
5253         } else {
5254             
5255                    Roo.log(" no label && no align");
5256                 cfg.cn = [
5257                     
5258                         inputblock
5259                     
5260                 ];
5261                 
5262                 
5263         }
5264         
5265         if (this.disabled) {
5266             input.disabled=true;
5267         }
5268         
5269         return cfg;
5270         
5271     },
5272     /**
5273      * return the real textarea element.
5274      */
5275     inputEl: function ()
5276     {
5277         return this.el.select('textarea.form-control',true).first();
5278     }
5279 });
5280
5281  
5282 /*
5283  * - LGPL
5284  *
5285  * trigger field - base class for combo..
5286  * 
5287  */
5288  
5289 /**
5290  * @class Roo.bootstrap.TriggerField
5291  * @extends Roo.bootstrap.Input
5292  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5293  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5294  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5295  * for which you can provide a custom implementation.  For example:
5296  * <pre><code>
5297 var trigger = new Roo.bootstrap.TriggerField();
5298 trigger.onTriggerClick = myTriggerFn;
5299 trigger.applyTo('my-field');
5300 </code></pre>
5301  *
5302  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5303  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5304  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5305  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5306  * @constructor
5307  * Create a new TriggerField.
5308  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5309  * to the base TextField)
5310  */
5311 Roo.bootstrap.TriggerField = function(config){
5312     this.mimicing = false;
5313     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5314 };
5315
5316 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5317     /**
5318      * @cfg {String} triggerClass A CSS class to apply to the trigger
5319      */
5320      /**
5321      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5322      */
5323     hideTrigger:false,
5324
5325     /** @cfg {Boolean} grow @hide */
5326     /** @cfg {Number} growMin @hide */
5327     /** @cfg {Number} growMax @hide */
5328
5329     /**
5330      * @hide 
5331      * @method
5332      */
5333     autoSize: Roo.emptyFn,
5334     // private
5335     monitorTab : true,
5336     // private
5337     deferHeight : true,
5338
5339     
5340     actionMode : 'wrap',
5341     
5342     
5343     
5344     getAutoCreate : function(){
5345        
5346         var parent = this.parent();
5347         
5348         var align = this.parentLabelAlign();
5349         
5350         var id = Roo.id();
5351         
5352         var cfg = {
5353             cls: 'form-group' //input-group
5354         };
5355         
5356         
5357         var input =  {
5358             tag: 'input',
5359             id : id,
5360             type : this.inputType,
5361             cls : 'form-control',
5362             autocomplete: 'off',
5363             placeholder : this.placeholder || '' 
5364             
5365         };
5366         if (this.name) {
5367             input.name = this.name;
5368         }
5369         if (this.size) {
5370             input.cls += ' input-' + this.size;
5371         }
5372         
5373         if (this.disabled) {
5374             input.disabled=true;
5375         }
5376         
5377         var inputblock = input;
5378         
5379         if (this.before || this.after) {
5380             
5381             inputblock = {
5382                 cls : 'input-group',
5383                 cn :  [] 
5384             };
5385             if (this.before) {
5386                 inputblock.cn.push({
5387                     tag :'span',
5388                     cls : 'input-group-addon',
5389                     html : this.before
5390                 });
5391             }
5392             inputblock.cn.push(input);
5393             if (this.after) {
5394                 inputblock.cn.push({
5395                     tag :'span',
5396                     cls : 'input-group-addon',
5397                     html : this.after
5398                 });
5399             }
5400             
5401         };
5402         
5403         var box = {
5404             tag: 'div',
5405             cn: [
5406                 {
5407                     tag: 'input',
5408                     type : 'hidden',
5409                     cls: 'form-hidden-field'
5410                 },
5411                 inputblock
5412             ]
5413             
5414         };
5415         
5416         if(this.multiple){
5417             Roo.log('multiple');
5418             
5419             box = {
5420                 tag: 'div',
5421                 cn: [
5422                     {
5423                         tag: 'input',
5424                         type : 'hidden',
5425                         cls: 'form-hidden-field'
5426                     },
5427                     {
5428                         tag: 'ul',
5429                         cls: 'select2-choices',
5430                         cn:[
5431                             {
5432                                 tag: 'li',
5433                                 cls: 'select2-search-field',
5434                                 cn: [
5435
5436                                     inputblock
5437                                 ]
5438                             }
5439                         ]
5440                     }
5441                 ]
5442             }
5443         };
5444         
5445         var combobox = {
5446             cls: 'select2-container input-group',
5447             cn: [
5448                 box,
5449                 {
5450                     tag: 'ul',
5451                     cls: 'typeahead typeahead-long dropdown-menu',
5452                     style: 'display:none'
5453                 }
5454             ]
5455         };
5456         
5457         if(!this.multiple){
5458             combobox.cn.push({
5459                 tag :'span',
5460                 cls : 'input-group-addon btn dropdown-toggle',
5461                 cn : [
5462                     {
5463                         tag: 'span',
5464                         cls: 'caret'
5465                     },
5466                     {
5467                         tag: 'span',
5468                         cls: 'combobox-clear',
5469                         cn  : [
5470                             {
5471                                 tag : 'i',
5472                                 cls: 'icon-remove'
5473                             }
5474                         ]
5475                     }
5476                 ]
5477
5478             })
5479         }
5480         
5481         if(this.multiple){
5482             combobox.cls += ' select2-container-multi';
5483         }
5484         
5485         if (align ==='left' && this.fieldLabel.length) {
5486             
5487                 Roo.log("left and has label");
5488                 cfg.cn = [
5489                     
5490                     {
5491                         tag: 'label',
5492                         'for' :  id,
5493                         cls : 'control-label col-sm-' + this.labelWidth,
5494                         html : this.fieldLabel
5495                         
5496                     },
5497                     {
5498                         cls : "col-sm-" + (12 - this.labelWidth), 
5499                         cn: [
5500                             combobox
5501                         ]
5502                     }
5503                     
5504                 ];
5505         } else if ( this.fieldLabel.length) {
5506                 Roo.log(" label");
5507                  cfg.cn = [
5508                    
5509                     {
5510                         tag: 'label',
5511                         //cls : 'input-group-addon',
5512                         html : this.fieldLabel
5513                         
5514                     },
5515                     
5516                     combobox
5517                     
5518                 ];
5519
5520         } else {
5521             
5522                 Roo.log(" no label && no align");
5523                 cfg = combobox
5524                      
5525                 
5526         }
5527          
5528         var settings=this;
5529         ['xs','sm','md','lg'].map(function(size){
5530             if (settings[size]) {
5531                 cfg.cls += ' col-' + size + '-' + settings[size];
5532             }
5533         });
5534         
5535         return cfg;
5536         
5537     },
5538     
5539     
5540     
5541     // private
5542     onResize : function(w, h){
5543 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5544 //        if(typeof w == 'number'){
5545 //            var x = w - this.trigger.getWidth();
5546 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5547 //            this.trigger.setStyle('left', x+'px');
5548 //        }
5549     },
5550
5551     // private
5552     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5553
5554     // private
5555     getResizeEl : function(){
5556         return this.inputEl();
5557     },
5558
5559     // private
5560     getPositionEl : function(){
5561         return this.inputEl();
5562     },
5563
5564     // private
5565     alignErrorIcon : function(){
5566         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5567     },
5568
5569     // private
5570     initEvents : function(){
5571         
5572         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5573         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5574         if(!this.multiple){
5575             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5576             if(this.hideTrigger){
5577                 this.trigger.setDisplayed(false);
5578             }
5579             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5580         }
5581         
5582         if(this.multiple){
5583             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5584         }
5585         
5586         //this.trigger.addClassOnOver('x-form-trigger-over');
5587         //this.trigger.addClassOnClick('x-form-trigger-click');
5588         
5589         //if(!this.width){
5590         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5591         //}
5592     },
5593
5594     // private
5595     initTrigger : function(){
5596        
5597     },
5598
5599     // private
5600     onDestroy : function(){
5601         if(this.trigger){
5602             this.trigger.removeAllListeners();
5603           //  this.trigger.remove();
5604         }
5605         //if(this.wrap){
5606         //    this.wrap.remove();
5607         //}
5608         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5609     },
5610
5611     // private
5612     onFocus : function(){
5613         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5614         /*
5615         if(!this.mimicing){
5616             this.wrap.addClass('x-trigger-wrap-focus');
5617             this.mimicing = true;
5618             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5619             if(this.monitorTab){
5620                 this.el.on("keydown", this.checkTab, this);
5621             }
5622         }
5623         */
5624     },
5625
5626     // private
5627     checkTab : function(e){
5628         if(e.getKey() == e.TAB){
5629             this.triggerBlur();
5630         }
5631     },
5632
5633     // private
5634     onBlur : function(){
5635         // do nothing
5636     },
5637
5638     // private
5639     mimicBlur : function(e, t){
5640         /*
5641         if(!this.wrap.contains(t) && this.validateBlur()){
5642             this.triggerBlur();
5643         }
5644         */
5645     },
5646
5647     // private
5648     triggerBlur : function(){
5649         this.mimicing = false;
5650         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5651         if(this.monitorTab){
5652             this.el.un("keydown", this.checkTab, this);
5653         }
5654         //this.wrap.removeClass('x-trigger-wrap-focus');
5655         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5656     },
5657
5658     // private
5659     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5660     validateBlur : function(e, t){
5661         return true;
5662     },
5663
5664     // private
5665     onDisable : function(){
5666         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5667         //if(this.wrap){
5668         //    this.wrap.addClass('x-item-disabled');
5669         //}
5670     },
5671
5672     // private
5673     onEnable : function(){
5674         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5675         //if(this.wrap){
5676         //    this.el.removeClass('x-item-disabled');
5677         //}
5678     },
5679
5680     // private
5681     onShow : function(){
5682         var ae = this.getActionEl();
5683         
5684         if(ae){
5685             ae.dom.style.display = '';
5686             ae.dom.style.visibility = 'visible';
5687         }
5688     },
5689
5690     // private
5691     
5692     onHide : function(){
5693         var ae = this.getActionEl();
5694         ae.dom.style.display = 'none';
5695     },
5696
5697     /**
5698      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5699      * by an implementing function.
5700      * @method
5701      * @param {EventObject} e
5702      */
5703     onTriggerClick : Roo.emptyFn
5704 });
5705  /*
5706  * Based on:
5707  * Ext JS Library 1.1.1
5708  * Copyright(c) 2006-2007, Ext JS, LLC.
5709  *
5710  * Originally Released Under LGPL - original licence link has changed is not relivant.
5711  *
5712  * Fork - LGPL
5713  * <script type="text/javascript">
5714  */
5715
5716
5717 /**
5718  * @class Roo.data.SortTypes
5719  * @singleton
5720  * Defines the default sorting (casting?) comparison functions used when sorting data.
5721  */
5722 Roo.data.SortTypes = {
5723     /**
5724      * Default sort that does nothing
5725      * @param {Mixed} s The value being converted
5726      * @return {Mixed} The comparison value
5727      */
5728     none : function(s){
5729         return s;
5730     },
5731     
5732     /**
5733      * The regular expression used to strip tags
5734      * @type {RegExp}
5735      * @property
5736      */
5737     stripTagsRE : /<\/?[^>]+>/gi,
5738     
5739     /**
5740      * Strips all HTML tags to sort on text only
5741      * @param {Mixed} s The value being converted
5742      * @return {String} The comparison value
5743      */
5744     asText : function(s){
5745         return String(s).replace(this.stripTagsRE, "");
5746     },
5747     
5748     /**
5749      * Strips all HTML tags to sort on text only - Case insensitive
5750      * @param {Mixed} s The value being converted
5751      * @return {String} The comparison value
5752      */
5753     asUCText : function(s){
5754         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5755     },
5756     
5757     /**
5758      * Case insensitive string
5759      * @param {Mixed} s The value being converted
5760      * @return {String} The comparison value
5761      */
5762     asUCString : function(s) {
5763         return String(s).toUpperCase();
5764     },
5765     
5766     /**
5767      * Date sorting
5768      * @param {Mixed} s The value being converted
5769      * @return {Number} The comparison value
5770      */
5771     asDate : function(s) {
5772         if(!s){
5773             return 0;
5774         }
5775         if(s instanceof Date){
5776             return s.getTime();
5777         }
5778         return Date.parse(String(s));
5779     },
5780     
5781     /**
5782      * Float sorting
5783      * @param {Mixed} s The value being converted
5784      * @return {Float} The comparison value
5785      */
5786     asFloat : function(s) {
5787         var val = parseFloat(String(s).replace(/,/g, ""));
5788         if(isNaN(val)) val = 0;
5789         return val;
5790     },
5791     
5792     /**
5793      * Integer sorting
5794      * @param {Mixed} s The value being converted
5795      * @return {Number} The comparison value
5796      */
5797     asInt : function(s) {
5798         var val = parseInt(String(s).replace(/,/g, ""));
5799         if(isNaN(val)) val = 0;
5800         return val;
5801     }
5802 };/*
5803  * Based on:
5804  * Ext JS Library 1.1.1
5805  * Copyright(c) 2006-2007, Ext JS, LLC.
5806  *
5807  * Originally Released Under LGPL - original licence link has changed is not relivant.
5808  *
5809  * Fork - LGPL
5810  * <script type="text/javascript">
5811  */
5812
5813 /**
5814 * @class Roo.data.Record
5815  * Instances of this class encapsulate both record <em>definition</em> information, and record
5816  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5817  * to access Records cached in an {@link Roo.data.Store} object.<br>
5818  * <p>
5819  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5820  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5821  * objects.<br>
5822  * <p>
5823  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5824  * @constructor
5825  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5826  * {@link #create}. The parameters are the same.
5827  * @param {Array} data An associative Array of data values keyed by the field name.
5828  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5829  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5830  * not specified an integer id is generated.
5831  */
5832 Roo.data.Record = function(data, id){
5833     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5834     this.data = data;
5835 };
5836
5837 /**
5838  * Generate a constructor for a specific record layout.
5839  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5840  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5841  * Each field definition object may contain the following properties: <ul>
5842  * <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,
5843  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5844  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5845  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5846  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5847  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5848  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5849  * this may be omitted.</p></li>
5850  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5851  * <ul><li>auto (Default, implies no conversion)</li>
5852  * <li>string</li>
5853  * <li>int</li>
5854  * <li>float</li>
5855  * <li>boolean</li>
5856  * <li>date</li></ul></p></li>
5857  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5858  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5859  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5860  * by the Reader into an object that will be stored in the Record. It is passed the
5861  * following parameters:<ul>
5862  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5863  * </ul></p></li>
5864  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5865  * </ul>
5866  * <br>usage:<br><pre><code>
5867 var TopicRecord = Roo.data.Record.create(
5868     {name: 'title', mapping: 'topic_title'},
5869     {name: 'author', mapping: 'username'},
5870     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5871     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5872     {name: 'lastPoster', mapping: 'user2'},
5873     {name: 'excerpt', mapping: 'post_text'}
5874 );
5875
5876 var myNewRecord = new TopicRecord({
5877     title: 'Do my job please',
5878     author: 'noobie',
5879     totalPosts: 1,
5880     lastPost: new Date(),
5881     lastPoster: 'Animal',
5882     excerpt: 'No way dude!'
5883 });
5884 myStore.add(myNewRecord);
5885 </code></pre>
5886  * @method create
5887  * @static
5888  */
5889 Roo.data.Record.create = function(o){
5890     var f = function(){
5891         f.superclass.constructor.apply(this, arguments);
5892     };
5893     Roo.extend(f, Roo.data.Record);
5894     var p = f.prototype;
5895     p.fields = new Roo.util.MixedCollection(false, function(field){
5896         return field.name;
5897     });
5898     for(var i = 0, len = o.length; i < len; i++){
5899         p.fields.add(new Roo.data.Field(o[i]));
5900     }
5901     f.getField = function(name){
5902         return p.fields.get(name);  
5903     };
5904     return f;
5905 };
5906
5907 Roo.data.Record.AUTO_ID = 1000;
5908 Roo.data.Record.EDIT = 'edit';
5909 Roo.data.Record.REJECT = 'reject';
5910 Roo.data.Record.COMMIT = 'commit';
5911
5912 Roo.data.Record.prototype = {
5913     /**
5914      * Readonly flag - true if this record has been modified.
5915      * @type Boolean
5916      */
5917     dirty : false,
5918     editing : false,
5919     error: null,
5920     modified: null,
5921
5922     // private
5923     join : function(store){
5924         this.store = store;
5925     },
5926
5927     /**
5928      * Set the named field to the specified value.
5929      * @param {String} name The name of the field to set.
5930      * @param {Object} value The value to set the field to.
5931      */
5932     set : function(name, value){
5933         if(this.data[name] == value){
5934             return;
5935         }
5936         this.dirty = true;
5937         if(!this.modified){
5938             this.modified = {};
5939         }
5940         if(typeof this.modified[name] == 'undefined'){
5941             this.modified[name] = this.data[name];
5942         }
5943         this.data[name] = value;
5944         if(!this.editing && this.store){
5945             this.store.afterEdit(this);
5946         }       
5947     },
5948
5949     /**
5950      * Get the value of the named field.
5951      * @param {String} name The name of the field to get the value of.
5952      * @return {Object} The value of the field.
5953      */
5954     get : function(name){
5955         return this.data[name]; 
5956     },
5957
5958     // private
5959     beginEdit : function(){
5960         this.editing = true;
5961         this.modified = {}; 
5962     },
5963
5964     // private
5965     cancelEdit : function(){
5966         this.editing = false;
5967         delete this.modified;
5968     },
5969
5970     // private
5971     endEdit : function(){
5972         this.editing = false;
5973         if(this.dirty && this.store){
5974             this.store.afterEdit(this);
5975         }
5976     },
5977
5978     /**
5979      * Usually called by the {@link Roo.data.Store} which owns the Record.
5980      * Rejects all changes made to the Record since either creation, or the last commit operation.
5981      * Modified fields are reverted to their original values.
5982      * <p>
5983      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5984      * of reject operations.
5985      */
5986     reject : function(){
5987         var m = this.modified;
5988         for(var n in m){
5989             if(typeof m[n] != "function"){
5990                 this.data[n] = m[n];
5991             }
5992         }
5993         this.dirty = false;
5994         delete this.modified;
5995         this.editing = false;
5996         if(this.store){
5997             this.store.afterReject(this);
5998         }
5999     },
6000
6001     /**
6002      * Usually called by the {@link Roo.data.Store} which owns the Record.
6003      * Commits all changes made to the Record since either creation, or the last commit operation.
6004      * <p>
6005      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6006      * of commit operations.
6007      */
6008     commit : function(){
6009         this.dirty = false;
6010         delete this.modified;
6011         this.editing = false;
6012         if(this.store){
6013             this.store.afterCommit(this);
6014         }
6015     },
6016
6017     // private
6018     hasError : function(){
6019         return this.error != null;
6020     },
6021
6022     // private
6023     clearError : function(){
6024         this.error = null;
6025     },
6026
6027     /**
6028      * Creates a copy of this record.
6029      * @param {String} id (optional) A new record id if you don't want to use this record's id
6030      * @return {Record}
6031      */
6032     copy : function(newId) {
6033         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6034     }
6035 };/*
6036  * Based on:
6037  * Ext JS Library 1.1.1
6038  * Copyright(c) 2006-2007, Ext JS, LLC.
6039  *
6040  * Originally Released Under LGPL - original licence link has changed is not relivant.
6041  *
6042  * Fork - LGPL
6043  * <script type="text/javascript">
6044  */
6045
6046
6047
6048 /**
6049  * @class Roo.data.Store
6050  * @extends Roo.util.Observable
6051  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6052  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6053  * <p>
6054  * 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
6055  * has no knowledge of the format of the data returned by the Proxy.<br>
6056  * <p>
6057  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6058  * instances from the data object. These records are cached and made available through accessor functions.
6059  * @constructor
6060  * Creates a new Store.
6061  * @param {Object} config A config object containing the objects needed for the Store to access data,
6062  * and read the data into Records.
6063  */
6064 Roo.data.Store = function(config){
6065     this.data = new Roo.util.MixedCollection(false);
6066     this.data.getKey = function(o){
6067         return o.id;
6068     };
6069     this.baseParams = {};
6070     // private
6071     this.paramNames = {
6072         "start" : "start",
6073         "limit" : "limit",
6074         "sort" : "sort",
6075         "dir" : "dir",
6076         "multisort" : "_multisort"
6077     };
6078
6079     if(config && config.data){
6080         this.inlineData = config.data;
6081         delete config.data;
6082     }
6083
6084     Roo.apply(this, config);
6085     
6086     if(this.reader){ // reader passed
6087         this.reader = Roo.factory(this.reader, Roo.data);
6088         this.reader.xmodule = this.xmodule || false;
6089         if(!this.recordType){
6090             this.recordType = this.reader.recordType;
6091         }
6092         if(this.reader.onMetaChange){
6093             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6094         }
6095     }
6096
6097     if(this.recordType){
6098         this.fields = this.recordType.prototype.fields;
6099     }
6100     this.modified = [];
6101
6102     this.addEvents({
6103         /**
6104          * @event datachanged
6105          * Fires when the data cache has changed, and a widget which is using this Store
6106          * as a Record cache should refresh its view.
6107          * @param {Store} this
6108          */
6109         datachanged : true,
6110         /**
6111          * @event metachange
6112          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6113          * @param {Store} this
6114          * @param {Object} meta The JSON metadata
6115          */
6116         metachange : true,
6117         /**
6118          * @event add
6119          * Fires when Records have been added to the Store
6120          * @param {Store} this
6121          * @param {Roo.data.Record[]} records The array of Records added
6122          * @param {Number} index The index at which the record(s) were added
6123          */
6124         add : true,
6125         /**
6126          * @event remove
6127          * Fires when a Record has been removed from the Store
6128          * @param {Store} this
6129          * @param {Roo.data.Record} record The Record that was removed
6130          * @param {Number} index The index at which the record was removed
6131          */
6132         remove : true,
6133         /**
6134          * @event update
6135          * Fires when a Record has been updated
6136          * @param {Store} this
6137          * @param {Roo.data.Record} record The Record that was updated
6138          * @param {String} operation The update operation being performed.  Value may be one of:
6139          * <pre><code>
6140  Roo.data.Record.EDIT
6141  Roo.data.Record.REJECT
6142  Roo.data.Record.COMMIT
6143          * </code></pre>
6144          */
6145         update : true,
6146         /**
6147          * @event clear
6148          * Fires when the data cache has been cleared.
6149          * @param {Store} this
6150          */
6151         clear : true,
6152         /**
6153          * @event beforeload
6154          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6155          * the load action will be canceled.
6156          * @param {Store} this
6157          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6158          */
6159         beforeload : true,
6160         /**
6161          * @event beforeloadadd
6162          * Fires after a new set of Records has been loaded.
6163          * @param {Store} this
6164          * @param {Roo.data.Record[]} records The Records that were loaded
6165          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6166          */
6167         beforeloadadd : true,
6168         /**
6169          * @event load
6170          * Fires after a new set of Records has been loaded, before they are added to the store.
6171          * @param {Store} this
6172          * @param {Roo.data.Record[]} records The Records that were loaded
6173          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6174          * @params {Object} return from reader
6175          */
6176         load : true,
6177         /**
6178          * @event loadexception
6179          * Fires if an exception occurs in the Proxy during loading.
6180          * Called with the signature of the Proxy's "loadexception" event.
6181          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6182          * 
6183          * @param {Proxy} 
6184          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6185          * @param {Object} load options 
6186          * @param {Object} jsonData from your request (normally this contains the Exception)
6187          */
6188         loadexception : true
6189     });
6190     
6191     if(this.proxy){
6192         this.proxy = Roo.factory(this.proxy, Roo.data);
6193         this.proxy.xmodule = this.xmodule || false;
6194         this.relayEvents(this.proxy,  ["loadexception"]);
6195     }
6196     this.sortToggle = {};
6197     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6198
6199     Roo.data.Store.superclass.constructor.call(this);
6200
6201     if(this.inlineData){
6202         this.loadData(this.inlineData);
6203         delete this.inlineData;
6204     }
6205 };
6206
6207 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6208      /**
6209     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6210     * without a remote query - used by combo/forms at present.
6211     */
6212     
6213     /**
6214     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6215     */
6216     /**
6217     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6218     */
6219     /**
6220     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6221     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6222     */
6223     /**
6224     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6225     * on any HTTP request
6226     */
6227     /**
6228     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6229     */
6230     /**
6231     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6232     */
6233     multiSort: false,
6234     /**
6235     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6236     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6237     */
6238     remoteSort : false,
6239
6240     /**
6241     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6242      * loaded or when a record is removed. (defaults to false).
6243     */
6244     pruneModifiedRecords : false,
6245
6246     // private
6247     lastOptions : null,
6248
6249     /**
6250      * Add Records to the Store and fires the add event.
6251      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6252      */
6253     add : function(records){
6254         records = [].concat(records);
6255         for(var i = 0, len = records.length; i < len; i++){
6256             records[i].join(this);
6257         }
6258         var index = this.data.length;
6259         this.data.addAll(records);
6260         this.fireEvent("add", this, records, index);
6261     },
6262
6263     /**
6264      * Remove a Record from the Store and fires the remove event.
6265      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6266      */
6267     remove : function(record){
6268         var index = this.data.indexOf(record);
6269         this.data.removeAt(index);
6270         if(this.pruneModifiedRecords){
6271             this.modified.remove(record);
6272         }
6273         this.fireEvent("remove", this, record, index);
6274     },
6275
6276     /**
6277      * Remove all Records from the Store and fires the clear event.
6278      */
6279     removeAll : function(){
6280         this.data.clear();
6281         if(this.pruneModifiedRecords){
6282             this.modified = [];
6283         }
6284         this.fireEvent("clear", this);
6285     },
6286
6287     /**
6288      * Inserts Records to the Store at the given index and fires the add event.
6289      * @param {Number} index The start index at which to insert the passed Records.
6290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6291      */
6292     insert : function(index, records){
6293         records = [].concat(records);
6294         for(var i = 0, len = records.length; i < len; i++){
6295             this.data.insert(index, records[i]);
6296             records[i].join(this);
6297         }
6298         this.fireEvent("add", this, records, index);
6299     },
6300
6301     /**
6302      * Get the index within the cache of the passed Record.
6303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6304      * @return {Number} The index of the passed Record. Returns -1 if not found.
6305      */
6306     indexOf : function(record){
6307         return this.data.indexOf(record);
6308     },
6309
6310     /**
6311      * Get the index within the cache of the Record with the passed id.
6312      * @param {String} id The id of the Record to find.
6313      * @return {Number} The index of the Record. Returns -1 if not found.
6314      */
6315     indexOfId : function(id){
6316         return this.data.indexOfKey(id);
6317     },
6318
6319     /**
6320      * Get the Record with the specified id.
6321      * @param {String} id The id of the Record to find.
6322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6323      */
6324     getById : function(id){
6325         return this.data.key(id);
6326     },
6327
6328     /**
6329      * Get the Record at the specified index.
6330      * @param {Number} index The index of the Record to find.
6331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6332      */
6333     getAt : function(index){
6334         return this.data.itemAt(index);
6335     },
6336
6337     /**
6338      * Returns a range of Records between specified indices.
6339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6341      * @return {Roo.data.Record[]} An array of Records
6342      */
6343     getRange : function(start, end){
6344         return this.data.getRange(start, end);
6345     },
6346
6347     // private
6348     storeOptions : function(o){
6349         o = Roo.apply({}, o);
6350         delete o.callback;
6351         delete o.scope;
6352         this.lastOptions = o;
6353     },
6354
6355     /**
6356      * Loads the Record cache from the configured Proxy using the configured Reader.
6357      * <p>
6358      * If using remote paging, then the first load call must specify the <em>start</em>
6359      * and <em>limit</em> properties in the options.params property to establish the initial
6360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6361      * <p>
6362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6363      * and this call will return before the new data has been loaded. Perform any post-processing
6364      * in a callback function, or in a "load" event handler.</strong>
6365      * <p>
6366      * @param {Object} options An object containing properties which control loading options:<ul>
6367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6368      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6369      * passed the following arguments:<ul>
6370      * <li>r : Roo.data.Record[]</li>
6371      * <li>options: Options object from the load call</li>
6372      * <li>success: Boolean success indicator</li></ul></li>
6373      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6374      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6375      * </ul>
6376      */
6377     load : function(options){
6378         options = options || {};
6379         if(this.fireEvent("beforeload", this, options) !== false){
6380             this.storeOptions(options);
6381             var p = Roo.apply(options.params || {}, this.baseParams);
6382             // if meta was not loaded from remote source.. try requesting it.
6383             if (!this.reader.metaFromRemote) {
6384                 p._requestMeta = 1;
6385             }
6386             if(this.sortInfo && this.remoteSort){
6387                 var pn = this.paramNames;
6388                 p[pn["sort"]] = this.sortInfo.field;
6389                 p[pn["dir"]] = this.sortInfo.direction;
6390             }
6391             if (this.multiSort) {
6392                 var pn = this.paramNames;
6393                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6394             }
6395             
6396             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6397         }
6398     },
6399
6400     /**
6401      * Reloads the Record cache from the configured Proxy using the configured Reader and
6402      * the options from the last load operation performed.
6403      * @param {Object} options (optional) An object containing properties which may override the options
6404      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6405      * the most recently used options are reused).
6406      */
6407     reload : function(options){
6408         this.load(Roo.applyIf(options||{}, this.lastOptions));
6409     },
6410
6411     // private
6412     // Called as a callback by the Reader during a load operation.
6413     loadRecords : function(o, options, success){
6414         if(!o || success === false){
6415             if(success !== false){
6416                 this.fireEvent("load", this, [], options, o);
6417             }
6418             if(options.callback){
6419                 options.callback.call(options.scope || this, [], options, false);
6420             }
6421             return;
6422         }
6423         // if data returned failure - throw an exception.
6424         if (o.success === false) {
6425             // show a message if no listener is registered.
6426             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6427                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6428             }
6429             // loadmask wil be hooked into this..
6430             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6431             return;
6432         }
6433         var r = o.records, t = o.totalRecords || r.length;
6434         
6435         this.fireEvent("beforeloadadd", this, r, options, o);
6436         
6437         if(!options || options.add !== true){
6438             if(this.pruneModifiedRecords){
6439                 this.modified = [];
6440             }
6441             for(var i = 0, len = r.length; i < len; i++){
6442                 r[i].join(this);
6443             }
6444             if(this.snapshot){
6445                 this.data = this.snapshot;
6446                 delete this.snapshot;
6447             }
6448             this.data.clear();
6449             this.data.addAll(r);
6450             this.totalLength = t;
6451             this.applySort();
6452             this.fireEvent("datachanged", this);
6453         }else{
6454             this.totalLength = Math.max(t, this.data.length+r.length);
6455             this.add(r);
6456         }
6457         this.fireEvent("load", this, r, options, o);
6458         if(options.callback){
6459             options.callback.call(options.scope || this, r, options, true);
6460         }
6461     },
6462
6463
6464     /**
6465      * Loads data from a passed data block. A Reader which understands the format of the data
6466      * must have been configured in the constructor.
6467      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6468      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6469      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6470      */
6471     loadData : function(o, append){
6472         var r = this.reader.readRecords(o);
6473         this.loadRecords(r, {add: append}, true);
6474     },
6475
6476     /**
6477      * Gets the number of cached records.
6478      * <p>
6479      * <em>If using paging, this may not be the total size of the dataset. If the data object
6480      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6481      * the data set size</em>
6482      */
6483     getCount : function(){
6484         return this.data.length || 0;
6485     },
6486
6487     /**
6488      * Gets the total number of records in the dataset as returned by the server.
6489      * <p>
6490      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6491      * the dataset size</em>
6492      */
6493     getTotalCount : function(){
6494         return this.totalLength || 0;
6495     },
6496
6497     /**
6498      * Returns the sort state of the Store as an object with two properties:
6499      * <pre><code>
6500  field {String} The name of the field by which the Records are sorted
6501  direction {String} The sort order, "ASC" or "DESC"
6502      * </code></pre>
6503      */
6504     getSortState : function(){
6505         return this.sortInfo;
6506     },
6507
6508     // private
6509     applySort : function(){
6510         if(this.sortInfo && !this.remoteSort){
6511             var s = this.sortInfo, f = s.field;
6512             var st = this.fields.get(f).sortType;
6513             var fn = function(r1, r2){
6514                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6515                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6516             };
6517             this.data.sort(s.direction, fn);
6518             if(this.snapshot && this.snapshot != this.data){
6519                 this.snapshot.sort(s.direction, fn);
6520             }
6521         }
6522     },
6523
6524     /**
6525      * Sets the default sort column and order to be used by the next load operation.
6526      * @param {String} fieldName The name of the field to sort by.
6527      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6528      */
6529     setDefaultSort : function(field, dir){
6530         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6531     },
6532
6533     /**
6534      * Sort the Records.
6535      * If remote sorting is used, the sort is performed on the server, and the cache is
6536      * reloaded. If local sorting is used, the cache is sorted internally.
6537      * @param {String} fieldName The name of the field to sort by.
6538      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6539      */
6540     sort : function(fieldName, dir){
6541         var f = this.fields.get(fieldName);
6542         if(!dir){
6543             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6544             
6545             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6546                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6547             }else{
6548                 dir = f.sortDir;
6549             }
6550         }
6551         this.sortToggle[f.name] = dir;
6552         this.sortInfo = {field: f.name, direction: dir};
6553         if(!this.remoteSort){
6554             this.applySort();
6555             this.fireEvent("datachanged", this);
6556         }else{
6557             this.load(this.lastOptions);
6558         }
6559     },
6560
6561     /**
6562      * Calls the specified function for each of the Records in the cache.
6563      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6564      * Returning <em>false</em> aborts and exits the iteration.
6565      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6566      */
6567     each : function(fn, scope){
6568         this.data.each(fn, scope);
6569     },
6570
6571     /**
6572      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6573      * (e.g., during paging).
6574      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6575      */
6576     getModifiedRecords : function(){
6577         return this.modified;
6578     },
6579
6580     // private
6581     createFilterFn : function(property, value, anyMatch){
6582         if(!value.exec){ // not a regex
6583             value = String(value);
6584             if(value.length == 0){
6585                 return false;
6586             }
6587             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6588         }
6589         return function(r){
6590             return value.test(r.data[property]);
6591         };
6592     },
6593
6594     /**
6595      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6596      * @param {String} property A field on your records
6597      * @param {Number} start The record index to start at (defaults to 0)
6598      * @param {Number} end The last record index to include (defaults to length - 1)
6599      * @return {Number} The sum
6600      */
6601     sum : function(property, start, end){
6602         var rs = this.data.items, v = 0;
6603         start = start || 0;
6604         end = (end || end === 0) ? end : rs.length-1;
6605
6606         for(var i = start; i <= end; i++){
6607             v += (rs[i].data[property] || 0);
6608         }
6609         return v;
6610     },
6611
6612     /**
6613      * Filter the records by a specified property.
6614      * @param {String} field A field on your records
6615      * @param {String/RegExp} value Either a string that the field
6616      * should start with or a RegExp to test against the field
6617      * @param {Boolean} anyMatch True to match any part not just the beginning
6618      */
6619     filter : function(property, value, anyMatch){
6620         var fn = this.createFilterFn(property, value, anyMatch);
6621         return fn ? this.filterBy(fn) : this.clearFilter();
6622     },
6623
6624     /**
6625      * Filter by a function. The specified function will be called with each
6626      * record in this data source. If the function returns true the record is included,
6627      * otherwise it is filtered.
6628      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6629      * @param {Object} scope (optional) The scope of the function (defaults to this)
6630      */
6631     filterBy : function(fn, scope){
6632         this.snapshot = this.snapshot || this.data;
6633         this.data = this.queryBy(fn, scope||this);
6634         this.fireEvent("datachanged", this);
6635     },
6636
6637     /**
6638      * Query the records by a specified property.
6639      * @param {String} field A field on your records
6640      * @param {String/RegExp} value Either a string that the field
6641      * should start with or a RegExp to test against the field
6642      * @param {Boolean} anyMatch True to match any part not just the beginning
6643      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6644      */
6645     query : function(property, value, anyMatch){
6646         var fn = this.createFilterFn(property, value, anyMatch);
6647         return fn ? this.queryBy(fn) : this.data.clone();
6648     },
6649
6650     /**
6651      * Query by a function. The specified function will be called with each
6652      * record in this data source. If the function returns true the record is included
6653      * in the results.
6654      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6655      * @param {Object} scope (optional) The scope of the function (defaults to this)
6656       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6657      **/
6658     queryBy : function(fn, scope){
6659         var data = this.snapshot || this.data;
6660         return data.filterBy(fn, scope||this);
6661     },
6662
6663     /**
6664      * Collects unique values for a particular dataIndex from this store.
6665      * @param {String} dataIndex The property to collect
6666      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6667      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6668      * @return {Array} An array of the unique values
6669      **/
6670     collect : function(dataIndex, allowNull, bypassFilter){
6671         var d = (bypassFilter === true && this.snapshot) ?
6672                 this.snapshot.items : this.data.items;
6673         var v, sv, r = [], l = {};
6674         for(var i = 0, len = d.length; i < len; i++){
6675             v = d[i].data[dataIndex];
6676             sv = String(v);
6677             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6678                 l[sv] = true;
6679                 r[r.length] = v;
6680             }
6681         }
6682         return r;
6683     },
6684
6685     /**
6686      * Revert to a view of the Record cache with no filtering applied.
6687      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6688      */
6689     clearFilter : function(suppressEvent){
6690         if(this.snapshot && this.snapshot != this.data){
6691             this.data = this.snapshot;
6692             delete this.snapshot;
6693             if(suppressEvent !== true){
6694                 this.fireEvent("datachanged", this);
6695             }
6696         }
6697     },
6698
6699     // private
6700     afterEdit : function(record){
6701         if(this.modified.indexOf(record) == -1){
6702             this.modified.push(record);
6703         }
6704         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6705     },
6706     
6707     // private
6708     afterReject : function(record){
6709         this.modified.remove(record);
6710         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6711     },
6712
6713     // private
6714     afterCommit : function(record){
6715         this.modified.remove(record);
6716         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6717     },
6718
6719     /**
6720      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6721      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6722      */
6723     commitChanges : function(){
6724         var m = this.modified.slice(0);
6725         this.modified = [];
6726         for(var i = 0, len = m.length; i < len; i++){
6727             m[i].commit();
6728         }
6729     },
6730
6731     /**
6732      * Cancel outstanding changes on all changed records.
6733      */
6734     rejectChanges : function(){
6735         var m = this.modified.slice(0);
6736         this.modified = [];
6737         for(var i = 0, len = m.length; i < len; i++){
6738             m[i].reject();
6739         }
6740     },
6741
6742     onMetaChange : function(meta, rtype, o){
6743         this.recordType = rtype;
6744         this.fields = rtype.prototype.fields;
6745         delete this.snapshot;
6746         this.sortInfo = meta.sortInfo || this.sortInfo;
6747         this.modified = [];
6748         this.fireEvent('metachange', this, this.reader.meta);
6749     },
6750     
6751     moveIndex : function(data, type)
6752     {
6753         var index = this.indexOf(data);
6754         
6755         var newIndex = index + type;
6756         
6757         this.remove(data);
6758         
6759         this.insert(newIndex, data);
6760         
6761     }
6762 });/*
6763  * Based on:
6764  * Ext JS Library 1.1.1
6765  * Copyright(c) 2006-2007, Ext JS, LLC.
6766  *
6767  * Originally Released Under LGPL - original licence link has changed is not relivant.
6768  *
6769  * Fork - LGPL
6770  * <script type="text/javascript">
6771  */
6772
6773 /**
6774  * @class Roo.data.SimpleStore
6775  * @extends Roo.data.Store
6776  * Small helper class to make creating Stores from Array data easier.
6777  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6778  * @cfg {Array} fields An array of field definition objects, or field name strings.
6779  * @cfg {Array} data The multi-dimensional array of data
6780  * @constructor
6781  * @param {Object} config
6782  */
6783 Roo.data.SimpleStore = function(config){
6784     Roo.data.SimpleStore.superclass.constructor.call(this, {
6785         isLocal : true,
6786         reader: new Roo.data.ArrayReader({
6787                 id: config.id
6788             },
6789             Roo.data.Record.create(config.fields)
6790         ),
6791         proxy : new Roo.data.MemoryProxy(config.data)
6792     });
6793     this.load();
6794 };
6795 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6796  * Based on:
6797  * Ext JS Library 1.1.1
6798  * Copyright(c) 2006-2007, Ext JS, LLC.
6799  *
6800  * Originally Released Under LGPL - original licence link has changed is not relivant.
6801  *
6802  * Fork - LGPL
6803  * <script type="text/javascript">
6804  */
6805
6806 /**
6807 /**
6808  * @extends Roo.data.Store
6809  * @class Roo.data.JsonStore
6810  * Small helper class to make creating Stores for JSON data easier. <br/>
6811 <pre><code>
6812 var store = new Roo.data.JsonStore({
6813     url: 'get-images.php',
6814     root: 'images',
6815     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6816 });
6817 </code></pre>
6818  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6819  * JsonReader and HttpProxy (unless inline data is provided).</b>
6820  * @cfg {Array} fields An array of field definition objects, or field name strings.
6821  * @constructor
6822  * @param {Object} config
6823  */
6824 Roo.data.JsonStore = function(c){
6825     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6826         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6827         reader: new Roo.data.JsonReader(c, c.fields)
6828     }));
6829 };
6830 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6831  * Based on:
6832  * Ext JS Library 1.1.1
6833  * Copyright(c) 2006-2007, Ext JS, LLC.
6834  *
6835  * Originally Released Under LGPL - original licence link has changed is not relivant.
6836  *
6837  * Fork - LGPL
6838  * <script type="text/javascript">
6839  */
6840
6841  
6842 Roo.data.Field = function(config){
6843     if(typeof config == "string"){
6844         config = {name: config};
6845     }
6846     Roo.apply(this, config);
6847     
6848     if(!this.type){
6849         this.type = "auto";
6850     }
6851     
6852     var st = Roo.data.SortTypes;
6853     // named sortTypes are supported, here we look them up
6854     if(typeof this.sortType == "string"){
6855         this.sortType = st[this.sortType];
6856     }
6857     
6858     // set default sortType for strings and dates
6859     if(!this.sortType){
6860         switch(this.type){
6861             case "string":
6862                 this.sortType = st.asUCString;
6863                 break;
6864             case "date":
6865                 this.sortType = st.asDate;
6866                 break;
6867             default:
6868                 this.sortType = st.none;
6869         }
6870     }
6871
6872     // define once
6873     var stripRe = /[\$,%]/g;
6874
6875     // prebuilt conversion function for this field, instead of
6876     // switching every time we're reading a value
6877     if(!this.convert){
6878         var cv, dateFormat = this.dateFormat;
6879         switch(this.type){
6880             case "":
6881             case "auto":
6882             case undefined:
6883                 cv = function(v){ return v; };
6884                 break;
6885             case "string":
6886                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6887                 break;
6888             case "int":
6889                 cv = function(v){
6890                     return v !== undefined && v !== null && v !== '' ?
6891                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6892                     };
6893                 break;
6894             case "float":
6895                 cv = function(v){
6896                     return v !== undefined && v !== null && v !== '' ?
6897                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6898                     };
6899                 break;
6900             case "bool":
6901             case "boolean":
6902                 cv = function(v){ return v === true || v === "true" || v == 1; };
6903                 break;
6904             case "date":
6905                 cv = function(v){
6906                     if(!v){
6907                         return '';
6908                     }
6909                     if(v instanceof Date){
6910                         return v;
6911                     }
6912                     if(dateFormat){
6913                         if(dateFormat == "timestamp"){
6914                             return new Date(v*1000);
6915                         }
6916                         return Date.parseDate(v, dateFormat);
6917                     }
6918                     var parsed = Date.parse(v);
6919                     return parsed ? new Date(parsed) : null;
6920                 };
6921              break;
6922             
6923         }
6924         this.convert = cv;
6925     }
6926 };
6927
6928 Roo.data.Field.prototype = {
6929     dateFormat: null,
6930     defaultValue: "",
6931     mapping: null,
6932     sortType : null,
6933     sortDir : "ASC"
6934 };/*
6935  * Based on:
6936  * Ext JS Library 1.1.1
6937  * Copyright(c) 2006-2007, Ext JS, LLC.
6938  *
6939  * Originally Released Under LGPL - original licence link has changed is not relivant.
6940  *
6941  * Fork - LGPL
6942  * <script type="text/javascript">
6943  */
6944  
6945 // Base class for reading structured data from a data source.  This class is intended to be
6946 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6947
6948 /**
6949  * @class Roo.data.DataReader
6950  * Base class for reading structured data from a data source.  This class is intended to be
6951  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6952  */
6953
6954 Roo.data.DataReader = function(meta, recordType){
6955     
6956     this.meta = meta;
6957     
6958     this.recordType = recordType instanceof Array ? 
6959         Roo.data.Record.create(recordType) : recordType;
6960 };
6961
6962 Roo.data.DataReader.prototype = {
6963      /**
6964      * Create an empty record
6965      * @param {Object} data (optional) - overlay some values
6966      * @return {Roo.data.Record} record created.
6967      */
6968     newRow :  function(d) {
6969         var da =  {};
6970         this.recordType.prototype.fields.each(function(c) {
6971             switch( c.type) {
6972                 case 'int' : da[c.name] = 0; break;
6973                 case 'date' : da[c.name] = new Date(); break;
6974                 case 'float' : da[c.name] = 0.0; break;
6975                 case 'boolean' : da[c.name] = false; break;
6976                 default : da[c.name] = ""; break;
6977             }
6978             
6979         });
6980         return new this.recordType(Roo.apply(da, d));
6981     }
6982     
6983 };/*
6984  * Based on:
6985  * Ext JS Library 1.1.1
6986  * Copyright(c) 2006-2007, Ext JS, LLC.
6987  *
6988  * Originally Released Under LGPL - original licence link has changed is not relivant.
6989  *
6990  * Fork - LGPL
6991  * <script type="text/javascript">
6992  */
6993
6994 /**
6995  * @class Roo.data.DataProxy
6996  * @extends Roo.data.Observable
6997  * This class is an abstract base class for implementations which provide retrieval of
6998  * unformatted data objects.<br>
6999  * <p>
7000  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7001  * (of the appropriate type which knows how to parse the data object) to provide a block of
7002  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7003  * <p>
7004  * Custom implementations must implement the load method as described in
7005  * {@link Roo.data.HttpProxy#load}.
7006  */
7007 Roo.data.DataProxy = function(){
7008     this.addEvents({
7009         /**
7010          * @event beforeload
7011          * Fires before a network request is made to retrieve a data object.
7012          * @param {Object} This DataProxy object.
7013          * @param {Object} params The params parameter to the load function.
7014          */
7015         beforeload : true,
7016         /**
7017          * @event load
7018          * Fires before the load method's callback is called.
7019          * @param {Object} This DataProxy object.
7020          * @param {Object} o The data object.
7021          * @param {Object} arg The callback argument object passed to the load function.
7022          */
7023         load : true,
7024         /**
7025          * @event loadexception
7026          * Fires if an Exception occurs during data retrieval.
7027          * @param {Object} This DataProxy object.
7028          * @param {Object} o The data object.
7029          * @param {Object} arg The callback argument object passed to the load function.
7030          * @param {Object} e The Exception.
7031          */
7032         loadexception : true
7033     });
7034     Roo.data.DataProxy.superclass.constructor.call(this);
7035 };
7036
7037 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7038
7039     /**
7040      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7041      */
7042 /*
7043  * Based on:
7044  * Ext JS Library 1.1.1
7045  * Copyright(c) 2006-2007, Ext JS, LLC.
7046  *
7047  * Originally Released Under LGPL - original licence link has changed is not relivant.
7048  *
7049  * Fork - LGPL
7050  * <script type="text/javascript">
7051  */
7052 /**
7053  * @class Roo.data.MemoryProxy
7054  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7055  * to the Reader when its load method is called.
7056  * @constructor
7057  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7058  */
7059 Roo.data.MemoryProxy = function(data){
7060     if (data.data) {
7061         data = data.data;
7062     }
7063     Roo.data.MemoryProxy.superclass.constructor.call(this);
7064     this.data = data;
7065 };
7066
7067 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7068     /**
7069      * Load data from the requested source (in this case an in-memory
7070      * data object passed to the constructor), read the data object into
7071      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7072      * process that block using the passed callback.
7073      * @param {Object} params This parameter is not used by the MemoryProxy class.
7074      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7075      * object into a block of Roo.data.Records.
7076      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7077      * The function must be passed <ul>
7078      * <li>The Record block object</li>
7079      * <li>The "arg" argument from the load function</li>
7080      * <li>A boolean success indicator</li>
7081      * </ul>
7082      * @param {Object} scope The scope in which to call the callback
7083      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7084      */
7085     load : function(params, reader, callback, scope, arg){
7086         params = params || {};
7087         var result;
7088         try {
7089             result = reader.readRecords(this.data);
7090         }catch(e){
7091             this.fireEvent("loadexception", this, arg, null, e);
7092             callback.call(scope, null, arg, false);
7093             return;
7094         }
7095         callback.call(scope, result, arg, true);
7096     },
7097     
7098     // private
7099     update : function(params, records){
7100         
7101     }
7102 });/*
7103  * Based on:
7104  * Ext JS Library 1.1.1
7105  * Copyright(c) 2006-2007, Ext JS, LLC.
7106  *
7107  * Originally Released Under LGPL - original licence link has changed is not relivant.
7108  *
7109  * Fork - LGPL
7110  * <script type="text/javascript">
7111  */
7112 /**
7113  * @class Roo.data.HttpProxy
7114  * @extends Roo.data.DataProxy
7115  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7116  * configured to reference a certain URL.<br><br>
7117  * <p>
7118  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7119  * from which the running page was served.<br><br>
7120  * <p>
7121  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7122  * <p>
7123  * Be aware that to enable the browser to parse an XML document, the server must set
7124  * the Content-Type header in the HTTP response to "text/xml".
7125  * @constructor
7126  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7127  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7128  * will be used to make the request.
7129  */
7130 Roo.data.HttpProxy = function(conn){
7131     Roo.data.HttpProxy.superclass.constructor.call(this);
7132     // is conn a conn config or a real conn?
7133     this.conn = conn;
7134     this.useAjax = !conn || !conn.events;
7135   
7136 };
7137
7138 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7139     // thse are take from connection...
7140     
7141     /**
7142      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7143      */
7144     /**
7145      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7146      * extra parameters to each request made by this object. (defaults to undefined)
7147      */
7148     /**
7149      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7150      *  to each request made by this object. (defaults to undefined)
7151      */
7152     /**
7153      * @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)
7154      */
7155     /**
7156      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7157      */
7158      /**
7159      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7160      * @type Boolean
7161      */
7162   
7163
7164     /**
7165      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7166      * @type Boolean
7167      */
7168     /**
7169      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7170      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7171      * a finer-grained basis than the DataProxy events.
7172      */
7173     getConnection : function(){
7174         return this.useAjax ? Roo.Ajax : this.conn;
7175     },
7176
7177     /**
7178      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7179      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7180      * process that block using the passed callback.
7181      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7182      * for the request to the remote server.
7183      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7184      * object into a block of Roo.data.Records.
7185      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7186      * The function must be passed <ul>
7187      * <li>The Record block object</li>
7188      * <li>The "arg" argument from the load function</li>
7189      * <li>A boolean success indicator</li>
7190      * </ul>
7191      * @param {Object} scope The scope in which to call the callback
7192      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7193      */
7194     load : function(params, reader, callback, scope, arg){
7195         if(this.fireEvent("beforeload", this, params) !== false){
7196             var  o = {
7197                 params : params || {},
7198                 request: {
7199                     callback : callback,
7200                     scope : scope,
7201                     arg : arg
7202                 },
7203                 reader: reader,
7204                 callback : this.loadResponse,
7205                 scope: this
7206             };
7207             if(this.useAjax){
7208                 Roo.applyIf(o, this.conn);
7209                 if(this.activeRequest){
7210                     Roo.Ajax.abort(this.activeRequest);
7211                 }
7212                 this.activeRequest = Roo.Ajax.request(o);
7213             }else{
7214                 this.conn.request(o);
7215             }
7216         }else{
7217             callback.call(scope||this, null, arg, false);
7218         }
7219     },
7220
7221     // private
7222     loadResponse : function(o, success, response){
7223         delete this.activeRequest;
7224         if(!success){
7225             this.fireEvent("loadexception", this, o, response);
7226             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7227             return;
7228         }
7229         var result;
7230         try {
7231             result = o.reader.read(response);
7232         }catch(e){
7233             this.fireEvent("loadexception", this, o, response, e);
7234             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7235             return;
7236         }
7237         
7238         this.fireEvent("load", this, o, o.request.arg);
7239         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7240     },
7241
7242     // private
7243     update : function(dataSet){
7244
7245     },
7246
7247     // private
7248     updateResponse : function(dataSet){
7249
7250     }
7251 });/*
7252  * Based on:
7253  * Ext JS Library 1.1.1
7254  * Copyright(c) 2006-2007, Ext JS, LLC.
7255  *
7256  * Originally Released Under LGPL - original licence link has changed is not relivant.
7257  *
7258  * Fork - LGPL
7259  * <script type="text/javascript">
7260  */
7261
7262 /**
7263  * @class Roo.data.ScriptTagProxy
7264  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7265  * other than the originating domain of the running page.<br><br>
7266  * <p>
7267  * <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
7268  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7269  * <p>
7270  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7271  * source code that is used as the source inside a &lt;script> tag.<br><br>
7272  * <p>
7273  * In order for the browser to process the returned data, the server must wrap the data object
7274  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7275  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7276  * depending on whether the callback name was passed:
7277  * <p>
7278  * <pre><code>
7279 boolean scriptTag = false;
7280 String cb = request.getParameter("callback");
7281 if (cb != null) {
7282     scriptTag = true;
7283     response.setContentType("text/javascript");
7284 } else {
7285     response.setContentType("application/x-json");
7286 }
7287 Writer out = response.getWriter();
7288 if (scriptTag) {
7289     out.write(cb + "(");
7290 }
7291 out.print(dataBlock.toJsonString());
7292 if (scriptTag) {
7293     out.write(");");
7294 }
7295 </pre></code>
7296  *
7297  * @constructor
7298  * @param {Object} config A configuration object.
7299  */
7300 Roo.data.ScriptTagProxy = function(config){
7301     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7302     Roo.apply(this, config);
7303     this.head = document.getElementsByTagName("head")[0];
7304 };
7305
7306 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7307
7308 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7309     /**
7310      * @cfg {String} url The URL from which to request the data object.
7311      */
7312     /**
7313      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7314      */
7315     timeout : 30000,
7316     /**
7317      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7318      * the server the name of the callback function set up by the load call to process the returned data object.
7319      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7320      * javascript output which calls this named function passing the data object as its only parameter.
7321      */
7322     callbackParam : "callback",
7323     /**
7324      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7325      * name to the request.
7326      */
7327     nocache : true,
7328
7329     /**
7330      * Load data from the configured URL, read the data object into
7331      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7332      * process that block using the passed callback.
7333      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7334      * for the request to the remote server.
7335      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7336      * object into a block of Roo.data.Records.
7337      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7338      * The function must be passed <ul>
7339      * <li>The Record block object</li>
7340      * <li>The "arg" argument from the load function</li>
7341      * <li>A boolean success indicator</li>
7342      * </ul>
7343      * @param {Object} scope The scope in which to call the callback
7344      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7345      */
7346     load : function(params, reader, callback, scope, arg){
7347         if(this.fireEvent("beforeload", this, params) !== false){
7348
7349             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7350
7351             var url = this.url;
7352             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7353             if(this.nocache){
7354                 url += "&_dc=" + (new Date().getTime());
7355             }
7356             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7357             var trans = {
7358                 id : transId,
7359                 cb : "stcCallback"+transId,
7360                 scriptId : "stcScript"+transId,
7361                 params : params,
7362                 arg : arg,
7363                 url : url,
7364                 callback : callback,
7365                 scope : scope,
7366                 reader : reader
7367             };
7368             var conn = this;
7369
7370             window[trans.cb] = function(o){
7371                 conn.handleResponse(o, trans);
7372             };
7373
7374             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7375
7376             if(this.autoAbort !== false){
7377                 this.abort();
7378             }
7379
7380             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7381
7382             var script = document.createElement("script");
7383             script.setAttribute("src", url);
7384             script.setAttribute("type", "text/javascript");
7385             script.setAttribute("id", trans.scriptId);
7386             this.head.appendChild(script);
7387
7388             this.trans = trans;
7389         }else{
7390             callback.call(scope||this, null, arg, false);
7391         }
7392     },
7393
7394     // private
7395     isLoading : function(){
7396         return this.trans ? true : false;
7397     },
7398
7399     /**
7400      * Abort the current server request.
7401      */
7402     abort : function(){
7403         if(this.isLoading()){
7404             this.destroyTrans(this.trans);
7405         }
7406     },
7407
7408     // private
7409     destroyTrans : function(trans, isLoaded){
7410         this.head.removeChild(document.getElementById(trans.scriptId));
7411         clearTimeout(trans.timeoutId);
7412         if(isLoaded){
7413             window[trans.cb] = undefined;
7414             try{
7415                 delete window[trans.cb];
7416             }catch(e){}
7417         }else{
7418             // if hasn't been loaded, wait for load to remove it to prevent script error
7419             window[trans.cb] = function(){
7420                 window[trans.cb] = undefined;
7421                 try{
7422                     delete window[trans.cb];
7423                 }catch(e){}
7424             };
7425         }
7426     },
7427
7428     // private
7429     handleResponse : function(o, trans){
7430         this.trans = false;
7431         this.destroyTrans(trans, true);
7432         var result;
7433         try {
7434             result = trans.reader.readRecords(o);
7435         }catch(e){
7436             this.fireEvent("loadexception", this, o, trans.arg, e);
7437             trans.callback.call(trans.scope||window, null, trans.arg, false);
7438             return;
7439         }
7440         this.fireEvent("load", this, o, trans.arg);
7441         trans.callback.call(trans.scope||window, result, trans.arg, true);
7442     },
7443
7444     // private
7445     handleFailure : function(trans){
7446         this.trans = false;
7447         this.destroyTrans(trans, false);
7448         this.fireEvent("loadexception", this, null, trans.arg);
7449         trans.callback.call(trans.scope||window, null, trans.arg, false);
7450     }
7451 });/*
7452  * Based on:
7453  * Ext JS Library 1.1.1
7454  * Copyright(c) 2006-2007, Ext JS, LLC.
7455  *
7456  * Originally Released Under LGPL - original licence link has changed is not relivant.
7457  *
7458  * Fork - LGPL
7459  * <script type="text/javascript">
7460  */
7461
7462 /**
7463  * @class Roo.data.JsonReader
7464  * @extends Roo.data.DataReader
7465  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7466  * based on mappings in a provided Roo.data.Record constructor.
7467  * 
7468  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7469  * in the reply previously. 
7470  * 
7471  * <p>
7472  * Example code:
7473  * <pre><code>
7474 var RecordDef = Roo.data.Record.create([
7475     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7476     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7477 ]);
7478 var myReader = new Roo.data.JsonReader({
7479     totalProperty: "results",    // The property which contains the total dataset size (optional)
7480     root: "rows",                // The property which contains an Array of row objects
7481     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7482 }, RecordDef);
7483 </code></pre>
7484  * <p>
7485  * This would consume a JSON file like this:
7486  * <pre><code>
7487 { 'results': 2, 'rows': [
7488     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7489     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7490 }
7491 </code></pre>
7492  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7493  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7494  * paged from the remote server.
7495  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7496  * @cfg {String} root name of the property which contains the Array of row objects.
7497  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7498  * @constructor
7499  * Create a new JsonReader
7500  * @param {Object} meta Metadata configuration options
7501  * @param {Object} recordType Either an Array of field definition objects,
7502  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7503  */
7504 Roo.data.JsonReader = function(meta, recordType){
7505     
7506     meta = meta || {};
7507     // set some defaults:
7508     Roo.applyIf(meta, {
7509         totalProperty: 'total',
7510         successProperty : 'success',
7511         root : 'data',
7512         id : 'id'
7513     });
7514     
7515     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7516 };
7517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7518     
7519     /**
7520      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7521      * Used by Store query builder to append _requestMeta to params.
7522      * 
7523      */
7524     metaFromRemote : false,
7525     /**
7526      * This method is only used by a DataProxy which has retrieved data from a remote server.
7527      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7528      * @return {Object} data A data block which is used by an Roo.data.Store object as
7529      * a cache of Roo.data.Records.
7530      */
7531     read : function(response){
7532         var json = response.responseText;
7533        
7534         var o = /* eval:var:o */ eval("("+json+")");
7535         if(!o) {
7536             throw {message: "JsonReader.read: Json object not found"};
7537         }
7538         
7539         if(o.metaData){
7540             
7541             delete this.ef;
7542             this.metaFromRemote = true;
7543             this.meta = o.metaData;
7544             this.recordType = Roo.data.Record.create(o.metaData.fields);
7545             this.onMetaChange(this.meta, this.recordType, o);
7546         }
7547         return this.readRecords(o);
7548     },
7549
7550     // private function a store will implement
7551     onMetaChange : function(meta, recordType, o){
7552
7553     },
7554
7555     /**
7556          * @ignore
7557          */
7558     simpleAccess: function(obj, subsc) {
7559         return obj[subsc];
7560     },
7561
7562         /**
7563          * @ignore
7564          */
7565     getJsonAccessor: function(){
7566         var re = /[\[\.]/;
7567         return function(expr) {
7568             try {
7569                 return(re.test(expr))
7570                     ? new Function("obj", "return obj." + expr)
7571                     : function(obj){
7572                         return obj[expr];
7573                     };
7574             } catch(e){}
7575             return Roo.emptyFn;
7576         };
7577     }(),
7578
7579     /**
7580      * Create a data block containing Roo.data.Records from an XML document.
7581      * @param {Object} o An object which contains an Array of row objects in the property specified
7582      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7583      * which contains the total size of the dataset.
7584      * @return {Object} data A data block which is used by an Roo.data.Store object as
7585      * a cache of Roo.data.Records.
7586      */
7587     readRecords : function(o){
7588         /**
7589          * After any data loads, the raw JSON data is available for further custom processing.
7590          * @type Object
7591          */
7592         this.o = o;
7593         var s = this.meta, Record = this.recordType,
7594             f = Record.prototype.fields, fi = f.items, fl = f.length;
7595
7596 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7597         if (!this.ef) {
7598             if(s.totalProperty) {
7599                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7600                 }
7601                 if(s.successProperty) {
7602                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7603                 }
7604                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7605                 if (s.id) {
7606                         var g = this.getJsonAccessor(s.id);
7607                         this.getId = function(rec) {
7608                                 var r = g(rec);
7609                                 return (r === undefined || r === "") ? null : r;
7610                         };
7611                 } else {
7612                         this.getId = function(){return null;};
7613                 }
7614             this.ef = [];
7615             for(var jj = 0; jj < fl; jj++){
7616                 f = fi[jj];
7617                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7618                 this.ef[jj] = this.getJsonAccessor(map);
7619             }
7620         }
7621
7622         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7623         if(s.totalProperty){
7624             var vt = parseInt(this.getTotal(o), 10);
7625             if(!isNaN(vt)){
7626                 totalRecords = vt;
7627             }
7628         }
7629         if(s.successProperty){
7630             var vs = this.getSuccess(o);
7631             if(vs === false || vs === 'false'){
7632                 success = false;
7633             }
7634         }
7635         var records = [];
7636             for(var i = 0; i < c; i++){
7637                     var n = root[i];
7638                 var values = {};
7639                 var id = this.getId(n);
7640                 for(var j = 0; j < fl; j++){
7641                     f = fi[j];
7642                 var v = this.ef[j](n);
7643                 if (!f.convert) {
7644                     Roo.log('missing convert for ' + f.name);
7645                     Roo.log(f);
7646                     continue;
7647                 }
7648                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7649                 }
7650                 var record = new Record(values, id);
7651                 record.json = n;
7652                 records[i] = record;
7653             }
7654             return {
7655             raw : o,
7656                 success : success,
7657                 records : records,
7658                 totalRecords : totalRecords
7659             };
7660     }
7661 });/*
7662  * Based on:
7663  * Ext JS Library 1.1.1
7664  * Copyright(c) 2006-2007, Ext JS, LLC.
7665  *
7666  * Originally Released Under LGPL - original licence link has changed is not relivant.
7667  *
7668  * Fork - LGPL
7669  * <script type="text/javascript">
7670  */
7671
7672 /**
7673  * @class Roo.data.ArrayReader
7674  * @extends Roo.data.DataReader
7675  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7676  * Each element of that Array represents a row of data fields. The
7677  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7678  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7679  * <p>
7680  * Example code:.
7681  * <pre><code>
7682 var RecordDef = Roo.data.Record.create([
7683     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7684     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7685 ]);
7686 var myReader = new Roo.data.ArrayReader({
7687     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7688 }, RecordDef);
7689 </code></pre>
7690  * <p>
7691  * This would consume an Array like this:
7692  * <pre><code>
7693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7694   </code></pre>
7695  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7696  * @constructor
7697  * Create a new JsonReader
7698  * @param {Object} meta Metadata configuration options.
7699  * @param {Object} recordType Either an Array of field definition objects
7700  * as specified to {@link Roo.data.Record#create},
7701  * or an {@link Roo.data.Record} object
7702  * created using {@link Roo.data.Record#create}.
7703  */
7704 Roo.data.ArrayReader = function(meta, recordType){
7705     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7706 };
7707
7708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7709     /**
7710      * Create a data block containing Roo.data.Records from an XML document.
7711      * @param {Object} o An Array of row objects which represents the dataset.
7712      * @return {Object} data A data block which is used by an Roo.data.Store object as
7713      * a cache of Roo.data.Records.
7714      */
7715     readRecords : function(o){
7716         var sid = this.meta ? this.meta.id : null;
7717         var recordType = this.recordType, fields = recordType.prototype.fields;
7718         var records = [];
7719         var root = o;
7720             for(var i = 0; i < root.length; i++){
7721                     var n = root[i];
7722                 var values = {};
7723                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7724                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7725                 var f = fields.items[j];
7726                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7727                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7728                 v = f.convert(v);
7729                 values[f.name] = v;
7730             }
7731                 var record = new recordType(values, id);
7732                 record.json = n;
7733                 records[records.length] = record;
7734             }
7735             return {
7736                 records : records,
7737                 totalRecords : records.length
7738             };
7739     }
7740 });/*
7741  * - LGPL
7742  * * 
7743  */
7744
7745 /**
7746  * @class Roo.bootstrap.ComboBox
7747  * @extends Roo.bootstrap.TriggerField
7748  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7749  * @cfg {Boolean} append (true|false) default false
7750  * @constructor
7751  * Create a new ComboBox.
7752  * @param {Object} config Configuration options
7753  */
7754 Roo.bootstrap.ComboBox = function(config){
7755     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7756     this.addEvents({
7757         /**
7758          * @event expand
7759          * Fires when the dropdown list is expanded
7760              * @param {Roo.bootstrap.ComboBox} combo This combo box
7761              */
7762         'expand' : true,
7763         /**
7764          * @event collapse
7765          * Fires when the dropdown list is collapsed
7766              * @param {Roo.bootstrap.ComboBox} combo This combo box
7767              */
7768         'collapse' : true,
7769         /**
7770          * @event beforeselect
7771          * Fires before a list item is selected. Return false to cancel the selection.
7772              * @param {Roo.bootstrap.ComboBox} combo This combo box
7773              * @param {Roo.data.Record} record The data record returned from the underlying store
7774              * @param {Number} index The index of the selected item in the dropdown list
7775              */
7776         'beforeselect' : true,
7777         /**
7778          * @event select
7779          * Fires when a list item is selected
7780              * @param {Roo.bootstrap.ComboBox} combo This combo box
7781              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7782              * @param {Number} index The index of the selected item in the dropdown list
7783              */
7784         'select' : true,
7785         /**
7786          * @event beforequery
7787          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7788          * The event object passed has these properties:
7789              * @param {Roo.bootstrap.ComboBox} combo This combo box
7790              * @param {String} query The query
7791              * @param {Boolean} forceAll true to force "all" query
7792              * @param {Boolean} cancel true to cancel the query
7793              * @param {Object} e The query event object
7794              */
7795         'beforequery': true,
7796          /**
7797          * @event add
7798          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7799              * @param {Roo.bootstrap.ComboBox} combo This combo box
7800              */
7801         'add' : true,
7802         /**
7803          * @event edit
7804          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7805              * @param {Roo.bootstrap.ComboBox} combo This combo box
7806              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7807              */
7808         'edit' : true,
7809         /**
7810          * @event remove
7811          * Fires when the remove value from the combobox array
7812              * @param {Roo.bootstrap.ComboBox} combo This combo box
7813              */
7814         'remove' : true
7815         
7816     });
7817     
7818     
7819     this.selectedIndex = -1;
7820     if(this.mode == 'local'){
7821         if(config.queryDelay === undefined){
7822             this.queryDelay = 10;
7823         }
7824         if(config.minChars === undefined){
7825             this.minChars = 0;
7826         }
7827     }
7828 };
7829
7830 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7831      
7832     /**
7833      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7834      * rendering into an Roo.Editor, defaults to false)
7835      */
7836     /**
7837      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7838      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7839      */
7840     /**
7841      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7842      */
7843     /**
7844      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7845      * the dropdown list (defaults to undefined, with no header element)
7846      */
7847
7848      /**
7849      * @cfg {String/Roo.Template} tpl The template to use to render the output
7850      */
7851      
7852      /**
7853      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7854      */
7855     listWidth: undefined,
7856     /**
7857      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7858      * mode = 'remote' or 'text' if mode = 'local')
7859      */
7860     displayField: undefined,
7861     /**
7862      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7863      * mode = 'remote' or 'value' if mode = 'local'). 
7864      * Note: use of a valueField requires the user make a selection
7865      * in order for a value to be mapped.
7866      */
7867     valueField: undefined,
7868     
7869     
7870     /**
7871      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7872      * field's data value (defaults to the underlying DOM element's name)
7873      */
7874     hiddenName: undefined,
7875     /**
7876      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7877      */
7878     listClass: '',
7879     /**
7880      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7881      */
7882     selectedClass: 'active',
7883     
7884     /**
7885      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7886      */
7887     shadow:'sides',
7888     /**
7889      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7890      * anchor positions (defaults to 'tl-bl')
7891      */
7892     listAlign: 'tl-bl?',
7893     /**
7894      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7895      */
7896     maxHeight: 300,
7897     /**
7898      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7899      * query specified by the allQuery config option (defaults to 'query')
7900      */
7901     triggerAction: 'query',
7902     /**
7903      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7904      * (defaults to 4, does not apply if editable = false)
7905      */
7906     minChars : 4,
7907     /**
7908      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7909      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7910      */
7911     typeAhead: false,
7912     /**
7913      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7914      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7915      */
7916     queryDelay: 500,
7917     /**
7918      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7919      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7920      */
7921     pageSize: 0,
7922     /**
7923      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7924      * when editable = true (defaults to false)
7925      */
7926     selectOnFocus:false,
7927     /**
7928      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7929      */
7930     queryParam: 'query',
7931     /**
7932      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7933      * when mode = 'remote' (defaults to 'Loading...')
7934      */
7935     loadingText: 'Loading...',
7936     /**
7937      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7938      */
7939     resizable: false,
7940     /**
7941      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7942      */
7943     handleHeight : 8,
7944     /**
7945      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7946      * traditional select (defaults to true)
7947      */
7948     editable: true,
7949     /**
7950      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7951      */
7952     allQuery: '',
7953     /**
7954      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7955      */
7956     mode: 'remote',
7957     /**
7958      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7959      * listWidth has a higher value)
7960      */
7961     minListWidth : 70,
7962     /**
7963      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7964      * allow the user to set arbitrary text into the field (defaults to false)
7965      */
7966     forceSelection:false,
7967     /**
7968      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7969      * if typeAhead = true (defaults to 250)
7970      */
7971     typeAheadDelay : 250,
7972     /**
7973      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7974      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7975      */
7976     valueNotFoundText : undefined,
7977     /**
7978      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7979      */
7980     blockFocus : false,
7981     
7982     /**
7983      * @cfg {Boolean} disableClear Disable showing of clear button.
7984      */
7985     disableClear : false,
7986     /**
7987      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7988      */
7989     alwaysQuery : false,
7990     
7991     /**
7992      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7993      */
7994     multiple : false,
7995     
7996     //private
7997     addicon : false,
7998     editicon: false,
7999     
8000     page: 0,
8001     hasQuery: false,
8002     append: false,
8003     loadNext: false,
8004     item: [],
8005     
8006     // element that contains real text value.. (when hidden is used..)
8007      
8008     // private
8009     initEvents: function(){
8010         
8011         if (!this.store) {
8012             throw "can not find store for combo";
8013         }
8014         this.store = Roo.factory(this.store, Roo.data);
8015         
8016         
8017         
8018         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8019         
8020         
8021         if(this.hiddenName){
8022             
8023             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8024             
8025             this.hiddenField.dom.value =
8026                 this.hiddenValue !== undefined ? this.hiddenValue :
8027                 this.value !== undefined ? this.value : '';
8028
8029             // prevent input submission
8030             this.el.dom.removeAttribute('name');
8031             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8032              
8033              
8034         }
8035         //if(Roo.isGecko){
8036         //    this.el.dom.setAttribute('autocomplete', 'off');
8037         //}
8038
8039         var cls = 'x-combo-list';
8040         this.list = this.el.select('ul.dropdown-menu',true).first();
8041
8042         //this.list = new Roo.Layer({
8043         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8044         //});
8045         
8046         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8047         this.list.setWidth(lw);
8048         
8049         this.list.on('mouseover', this.onViewOver, this);
8050         this.list.on('mousemove', this.onViewMove, this);
8051         
8052         this.list.on('scroll', this.onViewScroll, this);
8053         
8054         /*
8055         this.list.swallowEvent('mousewheel');
8056         this.assetHeight = 0;
8057
8058         if(this.title){
8059             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8060             this.assetHeight += this.header.getHeight();
8061         }
8062
8063         this.innerList = this.list.createChild({cls:cls+'-inner'});
8064         this.innerList.on('mouseover', this.onViewOver, this);
8065         this.innerList.on('mousemove', this.onViewMove, this);
8066         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8067         
8068         if(this.allowBlank && !this.pageSize && !this.disableClear){
8069             this.footer = this.list.createChild({cls:cls+'-ft'});
8070             this.pageTb = new Roo.Toolbar(this.footer);
8071            
8072         }
8073         if(this.pageSize){
8074             this.footer = this.list.createChild({cls:cls+'-ft'});
8075             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8076                     {pageSize: this.pageSize});
8077             
8078         }
8079         
8080         if (this.pageTb && this.allowBlank && !this.disableClear) {
8081             var _this = this;
8082             this.pageTb.add(new Roo.Toolbar.Fill(), {
8083                 cls: 'x-btn-icon x-btn-clear',
8084                 text: '&#160;',
8085                 handler: function()
8086                 {
8087                     _this.collapse();
8088                     _this.clearValue();
8089                     _this.onSelect(false, -1);
8090                 }
8091             });
8092         }
8093         if (this.footer) {
8094             this.assetHeight += this.footer.getHeight();
8095         }
8096         */
8097             
8098         if(!this.tpl){
8099             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8100         }
8101
8102         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8103             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8104         });
8105         //this.view.wrapEl.setDisplayed(false);
8106         this.view.on('click', this.onViewClick, this);
8107         
8108         
8109         
8110         this.store.on('beforeload', this.onBeforeLoad, this);
8111         this.store.on('load', this.onLoad, this);
8112         this.store.on('loadexception', this.onLoadException, this);
8113         /*
8114         if(this.resizable){
8115             this.resizer = new Roo.Resizable(this.list,  {
8116                pinned:true, handles:'se'
8117             });
8118             this.resizer.on('resize', function(r, w, h){
8119                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8120                 this.listWidth = w;
8121                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8122                 this.restrictHeight();
8123             }, this);
8124             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8125         }
8126         */
8127         if(!this.editable){
8128             this.editable = true;
8129             this.setEditable(false);
8130         }
8131         
8132         /*
8133         
8134         if (typeof(this.events.add.listeners) != 'undefined') {
8135             
8136             this.addicon = this.wrap.createChild(
8137                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8138        
8139             this.addicon.on('click', function(e) {
8140                 this.fireEvent('add', this);
8141             }, this);
8142         }
8143         if (typeof(this.events.edit.listeners) != 'undefined') {
8144             
8145             this.editicon = this.wrap.createChild(
8146                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8147             if (this.addicon) {
8148                 this.editicon.setStyle('margin-left', '40px');
8149             }
8150             this.editicon.on('click', function(e) {
8151                 
8152                 // we fire even  if inothing is selected..
8153                 this.fireEvent('edit', this, this.lastData );
8154                 
8155             }, this);
8156         }
8157         */
8158         
8159         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8160             "up" : function(e){
8161                 this.inKeyMode = true;
8162                 this.selectPrev();
8163             },
8164
8165             "down" : function(e){
8166                 if(!this.isExpanded()){
8167                     this.onTriggerClick();
8168                 }else{
8169                     this.inKeyMode = true;
8170                     this.selectNext();
8171                 }
8172             },
8173
8174             "enter" : function(e){
8175                 this.onViewClick();
8176                 //return true;
8177             },
8178
8179             "esc" : function(e){
8180                 this.collapse();
8181             },
8182
8183             "tab" : function(e){
8184                 this.collapse();
8185                 
8186                 if(this.fireEvent("specialkey", this, e)){
8187                     this.onViewClick(false);
8188                 }
8189                 
8190                 return true;
8191             },
8192
8193             scope : this,
8194
8195             doRelay : function(foo, bar, hname){
8196                 if(hname == 'down' || this.scope.isExpanded()){
8197                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8198                 }
8199                 return true;
8200             },
8201
8202             forceKeyDown: true
8203         });
8204         
8205         
8206         this.queryDelay = Math.max(this.queryDelay || 10,
8207                 this.mode == 'local' ? 10 : 250);
8208         
8209         
8210         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8211         
8212         if(this.typeAhead){
8213             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8214         }
8215         if(this.editable !== false){
8216             this.inputEl().on("keyup", this.onKeyUp, this);
8217         }
8218         if(this.forceSelection){
8219             this.on('blur', this.doForce, this);
8220         }
8221         
8222         if(this.multiple){
8223             this.choices = this.el.select('ul.select2-choices', true).first();
8224             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8225         }
8226     },
8227
8228     onDestroy : function(){
8229         if(this.view){
8230             this.view.setStore(null);
8231             this.view.el.removeAllListeners();
8232             this.view.el.remove();
8233             this.view.purgeListeners();
8234         }
8235         if(this.list){
8236             this.list.dom.innerHTML  = '';
8237         }
8238         if(this.store){
8239             this.store.un('beforeload', this.onBeforeLoad, this);
8240             this.store.un('load', this.onLoad, this);
8241             this.store.un('loadexception', this.onLoadException, this);
8242         }
8243         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8244     },
8245
8246     // private
8247     fireKey : function(e){
8248         if(e.isNavKeyPress() && !this.list.isVisible()){
8249             this.fireEvent("specialkey", this, e);
8250         }
8251     },
8252
8253     // private
8254     onResize: function(w, h){
8255 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8256 //        
8257 //        if(typeof w != 'number'){
8258 //            // we do not handle it!?!?
8259 //            return;
8260 //        }
8261 //        var tw = this.trigger.getWidth();
8262 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8263 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8264 //        var x = w - tw;
8265 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8266 //            
8267 //        //this.trigger.setStyle('left', x+'px');
8268 //        
8269 //        if(this.list && this.listWidth === undefined){
8270 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8271 //            this.list.setWidth(lw);
8272 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8273 //        }
8274         
8275     
8276         
8277     },
8278
8279     /**
8280      * Allow or prevent the user from directly editing the field text.  If false is passed,
8281      * the user will only be able to select from the items defined in the dropdown list.  This method
8282      * is the runtime equivalent of setting the 'editable' config option at config time.
8283      * @param {Boolean} value True to allow the user to directly edit the field text
8284      */
8285     setEditable : function(value){
8286         if(value == this.editable){
8287             return;
8288         }
8289         this.editable = value;
8290         if(!value){
8291             this.inputEl().dom.setAttribute('readOnly', true);
8292             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8293             this.inputEl().addClass('x-combo-noedit');
8294         }else{
8295             this.inputEl().dom.setAttribute('readOnly', false);
8296             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8297             this.inputEl().removeClass('x-combo-noedit');
8298         }
8299     },
8300
8301     // private
8302     
8303     onBeforeLoad : function(combo,opts){
8304         if(!this.hasFocus){
8305             return;
8306         }
8307          if (!opts.add) {
8308             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8309          }
8310         this.restrictHeight();
8311         this.selectedIndex = -1;
8312     },
8313
8314     // private
8315     onLoad : function(){
8316         
8317         this.hasQuery = false;
8318         
8319         if(!this.hasFocus){
8320             return;
8321         }
8322         
8323         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8324             this.loading.hide();
8325         }
8326         
8327         if(this.store.getCount() > 0){
8328             this.expand();
8329             this.restrictHeight();
8330             if(this.lastQuery == this.allQuery){
8331                 if(this.editable){
8332                     this.inputEl().dom.select();
8333                 }
8334                 if(!this.selectByValue(this.value, true)){
8335                     this.select(0, true);
8336                 }
8337             }else{
8338                 this.selectNext();
8339                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8340                     this.taTask.delay(this.typeAheadDelay);
8341                 }
8342             }
8343         }else{
8344             this.onEmptyResults();
8345         }
8346         
8347         //this.el.focus();
8348     },
8349     // private
8350     onLoadException : function()
8351     {
8352         this.hasQuery = false;
8353         
8354         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8355             this.loading.hide();
8356         }
8357         
8358         this.collapse();
8359         Roo.log(this.store.reader.jsonData);
8360         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8361             // fixme
8362             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8363         }
8364         
8365         
8366     },
8367     // private
8368     onTypeAhead : function(){
8369         if(this.store.getCount() > 0){
8370             var r = this.store.getAt(0);
8371             var newValue = r.data[this.displayField];
8372             var len = newValue.length;
8373             var selStart = this.getRawValue().length;
8374             
8375             if(selStart != len){
8376                 this.setRawValue(newValue);
8377                 this.selectText(selStart, newValue.length);
8378             }
8379         }
8380     },
8381
8382     // private
8383     onSelect : function(record, index){
8384         
8385         if(this.fireEvent('beforeselect', this, record, index) !== false){
8386         
8387             this.setFromData(index > -1 ? record.data : false);
8388             
8389             this.collapse();
8390             this.fireEvent('select', this, record, index);
8391         }
8392     },
8393
8394     /**
8395      * Returns the currently selected field value or empty string if no value is set.
8396      * @return {String} value The selected value
8397      */
8398     getValue : function(){
8399         
8400         if(this.multiple){
8401             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8402         }
8403         
8404         if(this.valueField){
8405             return typeof this.value != 'undefined' ? this.value : '';
8406         }else{
8407             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8408         }
8409     },
8410
8411     /**
8412      * Clears any text/value currently set in the field
8413      */
8414     clearValue : function(){
8415         if(this.hiddenField){
8416             this.hiddenField.dom.value = '';
8417         }
8418         this.value = '';
8419         this.setRawValue('');
8420         this.lastSelectionText = '';
8421         
8422     },
8423
8424     /**
8425      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8426      * will be displayed in the field.  If the value does not match the data value of an existing item,
8427      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8428      * Otherwise the field will be blank (although the value will still be set).
8429      * @param {String} value The value to match
8430      */
8431     setValue : function(v){
8432         if(this.multiple){
8433             this.syncValue();
8434             return;
8435         }
8436         
8437         var text = v;
8438         if(this.valueField){
8439             var r = this.findRecord(this.valueField, v);
8440             if(r){
8441                 text = r.data[this.displayField];
8442             }else if(this.valueNotFoundText !== undefined){
8443                 text = this.valueNotFoundText;
8444             }
8445         }
8446         this.lastSelectionText = text;
8447         if(this.hiddenField){
8448             this.hiddenField.dom.value = v;
8449         }
8450         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8451         this.value = v;
8452     },
8453     /**
8454      * @property {Object} the last set data for the element
8455      */
8456     
8457     lastData : false,
8458     /**
8459      * Sets the value of the field based on a object which is related to the record format for the store.
8460      * @param {Object} value the value to set as. or false on reset?
8461      */
8462     setFromData : function(o){
8463         
8464         if(this.multiple){
8465             this.addItem(o);
8466             return;
8467         }
8468             
8469         var dv = ''; // display value
8470         var vv = ''; // value value..
8471         this.lastData = o;
8472         if (this.displayField) {
8473             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8474         } else {
8475             // this is an error condition!!!
8476             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8477         }
8478         
8479         if(this.valueField){
8480             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8481         }
8482         
8483         if(this.hiddenField){
8484             this.hiddenField.dom.value = vv;
8485             
8486             this.lastSelectionText = dv;
8487             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8488             this.value = vv;
8489             return;
8490         }
8491         // no hidden field.. - we store the value in 'value', but still display
8492         // display field!!!!
8493         this.lastSelectionText = dv;
8494         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8495         this.value = vv;
8496         
8497         
8498     },
8499     // private
8500     reset : function(){
8501         // overridden so that last data is reset..
8502         this.setValue(this.originalValue);
8503         this.clearInvalid();
8504         this.lastData = false;
8505         if (this.view) {
8506             this.view.clearSelections();
8507         }
8508     },
8509     // private
8510     findRecord : function(prop, value){
8511         var record;
8512         if(this.store.getCount() > 0){
8513             this.store.each(function(r){
8514                 if(r.data[prop] == value){
8515                     record = r;
8516                     return false;
8517                 }
8518                 return true;
8519             });
8520         }
8521         return record;
8522     },
8523     
8524     getName: function()
8525     {
8526         // returns hidden if it's set..
8527         if (!this.rendered) {return ''};
8528         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8529         
8530     },
8531     // private
8532     onViewMove : function(e, t){
8533         this.inKeyMode = false;
8534     },
8535
8536     // private
8537     onViewOver : function(e, t){
8538         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8539             return;
8540         }
8541         var item = this.view.findItemFromChild(t);
8542         if(item){
8543             var index = this.view.indexOf(item);
8544             this.select(index, false);
8545         }
8546     },
8547
8548     // private
8549     onViewClick : function(doFocus)
8550     {
8551         var index = this.view.getSelectedIndexes()[0];
8552         var r = this.store.getAt(index);
8553         if(r){
8554             this.onSelect(r, index);
8555         }
8556         if(doFocus !== false && !this.blockFocus){
8557             this.inputEl().focus();
8558         }
8559     },
8560
8561     // private
8562     restrictHeight : function(){
8563         //this.innerList.dom.style.height = '';
8564         //var inner = this.innerList.dom;
8565         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8566         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8567         //this.list.beginUpdate();
8568         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8569         this.list.alignTo(this.inputEl(), this.listAlign);
8570         //this.list.endUpdate();
8571     },
8572
8573     // private
8574     onEmptyResults : function(){
8575         this.collapse();
8576     },
8577
8578     /**
8579      * Returns true if the dropdown list is expanded, else false.
8580      */
8581     isExpanded : function(){
8582         return this.list.isVisible();
8583     },
8584
8585     /**
8586      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8587      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8588      * @param {String} value The data value of the item to select
8589      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8590      * selected item if it is not currently in view (defaults to true)
8591      * @return {Boolean} True if the value matched an item in the list, else false
8592      */
8593     selectByValue : function(v, scrollIntoView){
8594         if(v !== undefined && v !== null){
8595             var r = this.findRecord(this.valueField || this.displayField, v);
8596             if(r){
8597                 this.select(this.store.indexOf(r), scrollIntoView);
8598                 return true;
8599             }
8600         }
8601         return false;
8602     },
8603
8604     /**
8605      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8606      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8607      * @param {Number} index The zero-based index of the list item to select
8608      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8609      * selected item if it is not currently in view (defaults to true)
8610      */
8611     select : function(index, scrollIntoView){
8612         this.selectedIndex = index;
8613         this.view.select(index);
8614         if(scrollIntoView !== false){
8615             var el = this.view.getNode(index);
8616             if(el){
8617                 //this.innerList.scrollChildIntoView(el, false);
8618                 
8619             }
8620         }
8621     },
8622
8623     // private
8624     selectNext : function(){
8625         var ct = this.store.getCount();
8626         if(ct > 0){
8627             if(this.selectedIndex == -1){
8628                 this.select(0);
8629             }else if(this.selectedIndex < ct-1){
8630                 this.select(this.selectedIndex+1);
8631             }
8632         }
8633     },
8634
8635     // private
8636     selectPrev : function(){
8637         var ct = this.store.getCount();
8638         if(ct > 0){
8639             if(this.selectedIndex == -1){
8640                 this.select(0);
8641             }else if(this.selectedIndex != 0){
8642                 this.select(this.selectedIndex-1);
8643             }
8644         }
8645     },
8646
8647     // private
8648     onKeyUp : function(e){
8649         if(this.editable !== false && !e.isSpecialKey()){
8650             this.lastKey = e.getKey();
8651             this.dqTask.delay(this.queryDelay);
8652         }
8653     },
8654
8655     // private
8656     validateBlur : function(){
8657         return !this.list || !this.list.isVisible();   
8658     },
8659
8660     // private
8661     initQuery : function(){
8662         this.doQuery(this.getRawValue());
8663     },
8664
8665     // private
8666     doForce : function(){
8667         if(this.el.dom.value.length > 0){
8668             this.el.dom.value =
8669                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8670              
8671         }
8672     },
8673
8674     /**
8675      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8676      * query allowing the query action to be canceled if needed.
8677      * @param {String} query The SQL query to execute
8678      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8679      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8680      * saved in the current store (defaults to false)
8681      */
8682     doQuery : function(q, forceAll){
8683         
8684         if(q === undefined || q === null){
8685             q = '';
8686         }
8687         var qe = {
8688             query: q,
8689             forceAll: forceAll,
8690             combo: this,
8691             cancel:false
8692         };
8693         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8694             return false;
8695         }
8696         q = qe.query;
8697         
8698         forceAll = qe.forceAll;
8699         if(forceAll === true || (q.length >= this.minChars)){
8700             
8701             this.hasQuery = true;
8702             
8703             if(this.lastQuery != q || this.alwaysQuery){
8704                 this.lastQuery = q;
8705                 if(this.mode == 'local'){
8706                     this.selectedIndex = -1;
8707                     if(forceAll){
8708                         this.store.clearFilter();
8709                     }else{
8710                         this.store.filter(this.displayField, q);
8711                     }
8712                     this.onLoad();
8713                 }else{
8714                     this.store.baseParams[this.queryParam] = q;
8715                     
8716                     var options = {params : this.getParams(q)};
8717                     
8718                     if(this.loadNext){
8719                         options.add = true;
8720                         options.params.start = this.page * this.pageSize;
8721                     }
8722                     
8723                     this.store.load(options);
8724                     this.expand();
8725                 }
8726             }else{
8727                 this.selectedIndex = -1;
8728                 this.onLoad();   
8729             }
8730         }
8731         
8732         this.loadNext = false;
8733     },
8734
8735     // private
8736     getParams : function(q){
8737         var p = {};
8738         //p[this.queryParam] = q;
8739         
8740         if(this.pageSize){
8741             p.start = 0;
8742             p.limit = this.pageSize;
8743         }
8744         return p;
8745     },
8746
8747     /**
8748      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8749      */
8750     collapse : function(){
8751         if(!this.isExpanded()){
8752             return;
8753         }
8754         
8755         this.list.hide();
8756         Roo.get(document).un('mousedown', this.collapseIf, this);
8757         Roo.get(document).un('mousewheel', this.collapseIf, this);
8758         if (!this.editable) {
8759             Roo.get(document).un('keydown', this.listKeyPress, this);
8760         }
8761         this.fireEvent('collapse', this);
8762     },
8763
8764     // private
8765     collapseIf : function(e){
8766         var in_combo  = e.within(this.el);
8767         var in_list =  e.within(this.list);
8768         
8769         if (in_combo || in_list) {
8770             //e.stopPropagation();
8771             return;
8772         }
8773
8774         this.collapse();
8775         
8776     },
8777
8778     /**
8779      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8780      */
8781     expand : function(){
8782        
8783         if(this.isExpanded() || !this.hasFocus){
8784             return;
8785         }
8786          Roo.log('expand');
8787         this.list.alignTo(this.inputEl(), this.listAlign);
8788         this.list.show();
8789         Roo.get(document).on('mousedown', this.collapseIf, this);
8790         Roo.get(document).on('mousewheel', this.collapseIf, this);
8791         if (!this.editable) {
8792             Roo.get(document).on('keydown', this.listKeyPress, this);
8793         }
8794         
8795         this.fireEvent('expand', this);
8796     },
8797
8798     // private
8799     // Implements the default empty TriggerField.onTriggerClick function
8800     onTriggerClick : function()
8801     {
8802         Roo.log('trigger click');
8803         
8804         if(this.disabled){
8805             return;
8806         }
8807         
8808         this.page = 0;
8809         this.loadNext = false;
8810         
8811         if(this.isExpanded()){
8812             this.collapse();
8813             if (!this.blockFocus) {
8814                 this.inputEl().focus();
8815             }
8816             
8817         }else {
8818             this.hasFocus = true;
8819             if(this.triggerAction == 'all') {
8820                 this.doQuery(this.allQuery, true);
8821             } else {
8822                 this.doQuery(this.getRawValue());
8823             }
8824             if (!this.blockFocus) {
8825                 this.inputEl().focus();
8826             }
8827         }
8828     },
8829     listKeyPress : function(e)
8830     {
8831         //Roo.log('listkeypress');
8832         // scroll to first matching element based on key pres..
8833         if (e.isSpecialKey()) {
8834             return false;
8835         }
8836         var k = String.fromCharCode(e.getKey()).toUpperCase();
8837         //Roo.log(k);
8838         var match  = false;
8839         var csel = this.view.getSelectedNodes();
8840         var cselitem = false;
8841         if (csel.length) {
8842             var ix = this.view.indexOf(csel[0]);
8843             cselitem  = this.store.getAt(ix);
8844             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8845                 cselitem = false;
8846             }
8847             
8848         }
8849         
8850         this.store.each(function(v) { 
8851             if (cselitem) {
8852                 // start at existing selection.
8853                 if (cselitem.id == v.id) {
8854                     cselitem = false;
8855                 }
8856                 return true;
8857             }
8858                 
8859             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8860                 match = this.store.indexOf(v);
8861                 return false;
8862             }
8863             return true;
8864         }, this);
8865         
8866         if (match === false) {
8867             return true; // no more action?
8868         }
8869         // scroll to?
8870         this.view.select(match);
8871         var sn = Roo.get(this.view.getSelectedNodes()[0])
8872         //sn.scrollIntoView(sn.dom.parentNode, false);
8873     },
8874     
8875     onViewScroll : function(e, t){
8876         
8877         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8878             return;
8879         }
8880         
8881         this.hasQuery = true;
8882         
8883         this.loading = this.list.select('.loading', true).first();
8884         
8885         if(this.loading === null){
8886             this.list.createChild({
8887                 tag: 'div',
8888                 cls: 'loading select2-more-results select2-active',
8889                 html: 'Loading more results...'
8890             })
8891             
8892             this.loading = this.list.select('.loading', true).first();
8893             
8894             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8895             
8896             this.loading.hide();
8897         }
8898         
8899         this.loading.show();
8900         
8901         var _combo = this;
8902         
8903         this.page++;
8904         this.loadNext = true;
8905         
8906         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8907         
8908         return;
8909     },
8910     
8911     addItem : function(o)
8912     {   
8913         var dv = ''; // display value
8914         
8915         if (this.displayField) {
8916             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8917         } else {
8918             // this is an error condition!!!
8919             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8920         }
8921         
8922         if(!dv.length){
8923             return;
8924         }
8925         
8926         var choice = this.choices.createChild({
8927             tag: 'li',
8928             cls: 'select2-search-choice',
8929             cn: [
8930                 {
8931                     tag: 'div',
8932                     html: dv
8933                 },
8934                 {
8935                     tag: 'a',
8936                     href: '#',
8937                     cls: 'select2-search-choice-close',
8938                     tabindex: '-1'
8939                 }
8940             ]
8941             
8942         }, this.searchField);
8943         
8944         var close = choice.select('a.select2-search-choice-close', true).first()
8945         
8946         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8947         
8948         this.item.push(o);
8949         this.lastData = o;
8950         
8951         this.syncValue();
8952         
8953         this.inputEl().dom.value = '';
8954         
8955     },
8956     
8957     onRemoveItem : function(e, _self, o)
8958     {
8959         Roo.log('remove item');
8960         var index = this.item.indexOf(o.data) * 1;
8961         
8962         if( index < 0){
8963             Roo.log('not this item?!');
8964             return;
8965         }
8966         
8967         this.item.splice(index, 1);
8968         o.item.remove();
8969         
8970         this.syncValue();
8971         
8972         this.fireEvent('remove', this);
8973         
8974     },
8975     
8976     syncValue : function()
8977     {
8978         if(!this.item.length){
8979             this.clearValue();
8980             return;
8981         }
8982             
8983         var value = [];
8984         var _this = this;
8985         Roo.each(this.item, function(i){
8986             if(_this.valueField){
8987                 value.push(i[_this.valueField]);
8988                 return;
8989             }
8990
8991             value.push(i);
8992         });
8993
8994         this.value = value.join(',');
8995
8996         if(this.hiddenField){
8997             this.hiddenField.dom.value = this.value;
8998         }
8999     },
9000     
9001     clearItem : function()
9002     {
9003         if(!this.multiple){
9004             return;
9005         }
9006         
9007         this.item = [];
9008         
9009         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9010            c.remove();
9011         });
9012         
9013         this.syncValue();
9014     }
9015     
9016     
9017
9018     /** 
9019     * @cfg {Boolean} grow 
9020     * @hide 
9021     */
9022     /** 
9023     * @cfg {Number} growMin 
9024     * @hide 
9025     */
9026     /** 
9027     * @cfg {Number} growMax 
9028     * @hide 
9029     */
9030     /**
9031      * @hide
9032      * @method autoSize
9033      */
9034 });
9035 /*
9036  * Based on:
9037  * Ext JS Library 1.1.1
9038  * Copyright(c) 2006-2007, Ext JS, LLC.
9039  *
9040  * Originally Released Under LGPL - original licence link has changed is not relivant.
9041  *
9042  * Fork - LGPL
9043  * <script type="text/javascript">
9044  */
9045
9046 /**
9047  * @class Roo.View
9048  * @extends Roo.util.Observable
9049  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9050  * This class also supports single and multi selection modes. <br>
9051  * Create a data model bound view:
9052  <pre><code>
9053  var store = new Roo.data.Store(...);
9054
9055  var view = new Roo.View({
9056     el : "my-element",
9057     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9058  
9059     singleSelect: true,
9060     selectedClass: "ydataview-selected",
9061     store: store
9062  });
9063
9064  // listen for node click?
9065  view.on("click", function(vw, index, node, e){
9066  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9067  });
9068
9069  // load XML data
9070  dataModel.load("foobar.xml");
9071  </code></pre>
9072  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9073  * <br><br>
9074  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9075  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9076  * 
9077  * Note: old style constructor is still suported (container, template, config)
9078  * 
9079  * @constructor
9080  * Create a new View
9081  * @param {Object} config The config object
9082  * 
9083  */
9084 Roo.View = function(config, depreciated_tpl, depreciated_config){
9085     
9086     if (typeof(depreciated_tpl) == 'undefined') {
9087         // new way.. - universal constructor.
9088         Roo.apply(this, config);
9089         this.el  = Roo.get(this.el);
9090     } else {
9091         // old format..
9092         this.el  = Roo.get(config);
9093         this.tpl = depreciated_tpl;
9094         Roo.apply(this, depreciated_config);
9095     }
9096     this.wrapEl  = this.el.wrap().wrap();
9097     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9098     
9099     
9100     if(typeof(this.tpl) == "string"){
9101         this.tpl = new Roo.Template(this.tpl);
9102     } else {
9103         // support xtype ctors..
9104         this.tpl = new Roo.factory(this.tpl, Roo);
9105     }
9106     
9107     
9108     this.tpl.compile();
9109    
9110   
9111     
9112      
9113     /** @private */
9114     this.addEvents({
9115         /**
9116          * @event beforeclick
9117          * Fires before a click is processed. Returns false to cancel the default action.
9118          * @param {Roo.View} this
9119          * @param {Number} index The index of the target node
9120          * @param {HTMLElement} node The target node
9121          * @param {Roo.EventObject} e The raw event object
9122          */
9123             "beforeclick" : true,
9124         /**
9125          * @event click
9126          * Fires when a template node is clicked.
9127          * @param {Roo.View} this
9128          * @param {Number} index The index of the target node
9129          * @param {HTMLElement} node The target node
9130          * @param {Roo.EventObject} e The raw event object
9131          */
9132             "click" : true,
9133         /**
9134          * @event dblclick
9135          * Fires when a template node is double clicked.
9136          * @param {Roo.View} this
9137          * @param {Number} index The index of the target node
9138          * @param {HTMLElement} node The target node
9139          * @param {Roo.EventObject} e The raw event object
9140          */
9141             "dblclick" : true,
9142         /**
9143          * @event contextmenu
9144          * Fires when a template node is right clicked.
9145          * @param {Roo.View} this
9146          * @param {Number} index The index of the target node
9147          * @param {HTMLElement} node The target node
9148          * @param {Roo.EventObject} e The raw event object
9149          */
9150             "contextmenu" : true,
9151         /**
9152          * @event selectionchange
9153          * Fires when the selected nodes change.
9154          * @param {Roo.View} this
9155          * @param {Array} selections Array of the selected nodes
9156          */
9157             "selectionchange" : true,
9158     
9159         /**
9160          * @event beforeselect
9161          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9162          * @param {Roo.View} this
9163          * @param {HTMLElement} node The node to be selected
9164          * @param {Array} selections Array of currently selected nodes
9165          */
9166             "beforeselect" : true,
9167         /**
9168          * @event preparedata
9169          * Fires on every row to render, to allow you to change the data.
9170          * @param {Roo.View} this
9171          * @param {Object} data to be rendered (change this)
9172          */
9173           "preparedata" : true
9174           
9175           
9176         });
9177
9178
9179
9180     this.el.on({
9181         "click": this.onClick,
9182         "dblclick": this.onDblClick,
9183         "contextmenu": this.onContextMenu,
9184         scope:this
9185     });
9186
9187     this.selections = [];
9188     this.nodes = [];
9189     this.cmp = new Roo.CompositeElementLite([]);
9190     if(this.store){
9191         this.store = Roo.factory(this.store, Roo.data);
9192         this.setStore(this.store, true);
9193     }
9194     
9195     if ( this.footer && this.footer.xtype) {
9196            
9197          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9198         
9199         this.footer.dataSource = this.store
9200         this.footer.container = fctr;
9201         this.footer = Roo.factory(this.footer, Roo);
9202         fctr.insertFirst(this.el);
9203         
9204         // this is a bit insane - as the paging toolbar seems to detach the el..
9205 //        dom.parentNode.parentNode.parentNode
9206          // they get detached?
9207     }
9208     
9209     
9210     Roo.View.superclass.constructor.call(this);
9211     
9212     
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     /**
9232      * @cfg {String} dataName the named area of the template to use as the data area
9233      *                          Works with domtemplates roo-name="name"
9234      */
9235     dataName: false,
9236     /**
9237      * @cfg {String} selectedClass The css class to add to selected nodes
9238      */
9239     selectedClass : "x-view-selected",
9240      /**
9241      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9242      */
9243     emptyText : "",
9244     
9245     /**
9246      * @cfg {String} text to display on mask (default Loading)
9247      */
9248     mask : false,
9249     /**
9250      * @cfg {Boolean} multiSelect Allow multiple selection
9251      */
9252     multiSelect : false,
9253     /**
9254      * @cfg {Boolean} singleSelect Allow single selection
9255      */
9256     singleSelect:  false,
9257     
9258     /**
9259      * @cfg {Boolean} toggleSelect - selecting 
9260      */
9261     toggleSelect : false,
9262     
9263     /**
9264      * Returns the element this view is bound to.
9265      * @return {Roo.Element}
9266      */
9267     getEl : function(){
9268         return this.wrapEl;
9269     },
9270     
9271     
9272
9273     /**
9274      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9275      */
9276     refresh : function(){
9277         Roo.log('refresh');
9278         var t = this.tpl;
9279         
9280         // if we are using something like 'domtemplate', then
9281         // the what gets used is:
9282         // t.applySubtemplate(NAME, data, wrapping data..)
9283         // the outer template then get' applied with
9284         //     the store 'extra data'
9285         // and the body get's added to the
9286         //      roo-name="data" node?
9287         //      <span class='roo-tpl-{name}'></span> ?????
9288         
9289         
9290         
9291         this.clearSelections();
9292         this.el.update("");
9293         var html = [];
9294         var records = this.store.getRange();
9295         if(records.length < 1) {
9296             
9297             // is this valid??  = should it render a template??
9298             
9299             this.el.update(this.emptyText);
9300             return;
9301         }
9302         var el = this.el;
9303         if (this.dataName) {
9304             this.el.update(t.apply(this.store.meta)); //????
9305             el = this.el.child('.roo-tpl-' + this.dataName);
9306         }
9307         
9308         for(var i = 0, len = records.length; i < len; i++){
9309             var data = this.prepareData(records[i].data, i, records[i]);
9310             this.fireEvent("preparedata", this, data, i, records[i]);
9311             html[html.length] = Roo.util.Format.trim(
9312                 this.dataName ?
9313                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9314                     t.apply(data)
9315             );
9316         }
9317         
9318         
9319         
9320         el.update(html.join(""));
9321         this.nodes = el.dom.childNodes;
9322         this.updateIndexes(0);
9323     },
9324     
9325
9326     /**
9327      * Function to override to reformat the data that is sent to
9328      * the template for each node.
9329      * DEPRICATED - use the preparedata event handler.
9330      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9331      * a JSON object for an UpdateManager bound view).
9332      */
9333     prepareData : function(data, index, record)
9334     {
9335         this.fireEvent("preparedata", this, data, index, record);
9336         return data;
9337     },
9338
9339     onUpdate : function(ds, record){
9340          Roo.log('on update');   
9341         this.clearSelections();
9342         var index = this.store.indexOf(record);
9343         var n = this.nodes[index];
9344         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9345         n.parentNode.removeChild(n);
9346         this.updateIndexes(index, index);
9347     },
9348
9349     
9350     
9351 // --------- FIXME     
9352     onAdd : function(ds, records, index)
9353     {
9354         Roo.log(['on Add', ds, records, index] );        
9355         this.clearSelections();
9356         if(this.nodes.length == 0){
9357             this.refresh();
9358             return;
9359         }
9360         var n = this.nodes[index];
9361         for(var i = 0, len = records.length; i < len; i++){
9362             var d = this.prepareData(records[i].data, i, records[i]);
9363             if(n){
9364                 this.tpl.insertBefore(n, d);
9365             }else{
9366                 
9367                 this.tpl.append(this.el, d);
9368             }
9369         }
9370         this.updateIndexes(index);
9371     },
9372
9373     onRemove : function(ds, record, index){
9374         Roo.log('onRemove');
9375         this.clearSelections();
9376         var el = this.dataName  ?
9377             this.el.child('.roo-tpl-' + this.dataName) :
9378             this.el; 
9379         
9380         el.dom.removeChild(this.nodes[index]);
9381         this.updateIndexes(index);
9382     },
9383
9384     /**
9385      * Refresh an individual node.
9386      * @param {Number} index
9387      */
9388     refreshNode : function(index){
9389         this.onUpdate(this.store, this.store.getAt(index));
9390     },
9391
9392     updateIndexes : function(startIndex, endIndex){
9393         var ns = this.nodes;
9394         startIndex = startIndex || 0;
9395         endIndex = endIndex || ns.length - 1;
9396         for(var i = startIndex; i <= endIndex; i++){
9397             ns[i].nodeIndex = i;
9398         }
9399     },
9400
9401     /**
9402      * Changes the data store this view uses and refresh the view.
9403      * @param {Store} store
9404      */
9405     setStore : function(store, initial){
9406         if(!initial && this.store){
9407             this.store.un("datachanged", this.refresh);
9408             this.store.un("add", this.onAdd);
9409             this.store.un("remove", this.onRemove);
9410             this.store.un("update", this.onUpdate);
9411             this.store.un("clear", this.refresh);
9412             this.store.un("beforeload", this.onBeforeLoad);
9413             this.store.un("load", this.onLoad);
9414             this.store.un("loadexception", this.onLoad);
9415         }
9416         if(store){
9417           
9418             store.on("datachanged", this.refresh, this);
9419             store.on("add", this.onAdd, this);
9420             store.on("remove", this.onRemove, this);
9421             store.on("update", this.onUpdate, this);
9422             store.on("clear", this.refresh, this);
9423             store.on("beforeload", this.onBeforeLoad, this);
9424             store.on("load", this.onLoad, this);
9425             store.on("loadexception", this.onLoad, this);
9426         }
9427         
9428         if(store){
9429             this.refresh();
9430         }
9431     },
9432     /**
9433      * onbeforeLoad - masks the loading area.
9434      *
9435      */
9436     onBeforeLoad : function(store,opts)
9437     {
9438          Roo.log('onBeforeLoad');   
9439         if (!opts.add) {
9440             this.el.update("");
9441         }
9442         this.el.mask(this.mask ? this.mask : "Loading" ); 
9443     },
9444     onLoad : function ()
9445     {
9446         this.el.unmask();
9447     },
9448     
9449
9450     /**
9451      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9452      * @param {HTMLElement} node
9453      * @return {HTMLElement} The template node
9454      */
9455     findItemFromChild : function(node){
9456         var el = this.dataName  ?
9457             this.el.child('.roo-tpl-' + this.dataName,true) :
9458             this.el.dom; 
9459         
9460         if(!node || node.parentNode == el){
9461                     return node;
9462             }
9463             var p = node.parentNode;
9464             while(p && p != el){
9465             if(p.parentNode == el){
9466                 return p;
9467             }
9468             p = p.parentNode;
9469         }
9470             return null;
9471     },
9472
9473     /** @ignore */
9474     onClick : function(e){
9475         var item = this.findItemFromChild(e.getTarget());
9476         if(item){
9477             var index = this.indexOf(item);
9478             if(this.onItemClick(item, index, e) !== false){
9479                 this.fireEvent("click", this, index, item, e);
9480             }
9481         }else{
9482             this.clearSelections();
9483         }
9484     },
9485
9486     /** @ignore */
9487     onContextMenu : function(e){
9488         var item = this.findItemFromChild(e.getTarget());
9489         if(item){
9490             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9491         }
9492     },
9493
9494     /** @ignore */
9495     onDblClick : function(e){
9496         var item = this.findItemFromChild(e.getTarget());
9497         if(item){
9498             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9499         }
9500     },
9501
9502     onItemClick : function(item, index, e)
9503     {
9504         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9505             return false;
9506         }
9507         if (this.toggleSelect) {
9508             var m = this.isSelected(item) ? 'unselect' : 'select';
9509             Roo.log(m);
9510             var _t = this;
9511             _t[m](item, true, false);
9512             return true;
9513         }
9514         if(this.multiSelect || this.singleSelect){
9515             if(this.multiSelect && e.shiftKey && this.lastSelection){
9516                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9517             }else{
9518                 this.select(item, this.multiSelect && e.ctrlKey);
9519                 this.lastSelection = item;
9520             }
9521             e.preventDefault();
9522         }
9523         return true;
9524     },
9525
9526     /**
9527      * Get the number of selected nodes.
9528      * @return {Number}
9529      */
9530     getSelectionCount : function(){
9531         return this.selections.length;
9532     },
9533
9534     /**
9535      * Get the currently selected nodes.
9536      * @return {Array} An array of HTMLElements
9537      */
9538     getSelectedNodes : function(){
9539         return this.selections;
9540     },
9541
9542     /**
9543      * Get the indexes of the selected nodes.
9544      * @return {Array}
9545      */
9546     getSelectedIndexes : function(){
9547         var indexes = [], s = this.selections;
9548         for(var i = 0, len = s.length; i < len; i++){
9549             indexes.push(s[i].nodeIndex);
9550         }
9551         return indexes;
9552     },
9553
9554     /**
9555      * Clear all selections
9556      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9557      */
9558     clearSelections : function(suppressEvent){
9559         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9560             this.cmp.elements = this.selections;
9561             this.cmp.removeClass(this.selectedClass);
9562             this.selections = [];
9563             if(!suppressEvent){
9564                 this.fireEvent("selectionchange", this, this.selections);
9565             }
9566         }
9567     },
9568
9569     /**
9570      * Returns true if the passed node is selected
9571      * @param {HTMLElement/Number} node The node or node index
9572      * @return {Boolean}
9573      */
9574     isSelected : function(node){
9575         var s = this.selections;
9576         if(s.length < 1){
9577             return false;
9578         }
9579         node = this.getNode(node);
9580         return s.indexOf(node) !== -1;
9581     },
9582
9583     /**
9584      * Selects nodes.
9585      * @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
9586      * @param {Boolean} keepExisting (optional) true to keep existing selections
9587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9588      */
9589     select : function(nodeInfo, keepExisting, suppressEvent){
9590         if(nodeInfo instanceof Array){
9591             if(!keepExisting){
9592                 this.clearSelections(true);
9593             }
9594             for(var i = 0, len = nodeInfo.length; i < len; i++){
9595                 this.select(nodeInfo[i], true, true);
9596             }
9597             return;
9598         } 
9599         var node = this.getNode(nodeInfo);
9600         if(!node || this.isSelected(node)){
9601             return; // already selected.
9602         }
9603         if(!keepExisting){
9604             this.clearSelections(true);
9605         }
9606         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9607             Roo.fly(node).addClass(this.selectedClass);
9608             this.selections.push(node);
9609             if(!suppressEvent){
9610                 this.fireEvent("selectionchange", this, this.selections);
9611             }
9612         }
9613         
9614         
9615     },
9616       /**
9617      * Unselects nodes.
9618      * @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
9619      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9620      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9621      */
9622     unselect : function(nodeInfo, keepExisting, suppressEvent)
9623     {
9624         if(nodeInfo instanceof Array){
9625             Roo.each(this.selections, function(s) {
9626                 this.unselect(s, nodeInfo);
9627             }, this);
9628             return;
9629         }
9630         var node = this.getNode(nodeInfo);
9631         if(!node || !this.isSelected(node)){
9632             Roo.log("not selected");
9633             return; // not selected.
9634         }
9635         // fireevent???
9636         var ns = [];
9637         Roo.each(this.selections, function(s) {
9638             if (s == node ) {
9639                 Roo.fly(node).removeClass(this.selectedClass);
9640
9641                 return;
9642             }
9643             ns.push(s);
9644         },this);
9645         
9646         this.selections= ns;
9647         this.fireEvent("selectionchange", this, this.selections);
9648     },
9649
9650     /**
9651      * Gets a template node.
9652      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9653      * @return {HTMLElement} The node or null if it wasn't found
9654      */
9655     getNode : function(nodeInfo){
9656         if(typeof nodeInfo == "string"){
9657             return document.getElementById(nodeInfo);
9658         }else if(typeof nodeInfo == "number"){
9659             return this.nodes[nodeInfo];
9660         }
9661         return nodeInfo;
9662     },
9663
9664     /**
9665      * Gets a range template nodes.
9666      * @param {Number} startIndex
9667      * @param {Number} endIndex
9668      * @return {Array} An array of nodes
9669      */
9670     getNodes : function(start, end){
9671         var ns = this.nodes;
9672         start = start || 0;
9673         end = typeof end == "undefined" ? ns.length - 1 : end;
9674         var nodes = [];
9675         if(start <= end){
9676             for(var i = start; i <= end; i++){
9677                 nodes.push(ns[i]);
9678             }
9679         } else{
9680             for(var i = start; i >= end; i--){
9681                 nodes.push(ns[i]);
9682             }
9683         }
9684         return nodes;
9685     },
9686
9687     /**
9688      * Finds the index of the passed node
9689      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9690      * @return {Number} The index of the node or -1
9691      */
9692     indexOf : function(node){
9693         node = this.getNode(node);
9694         if(typeof node.nodeIndex == "number"){
9695             return node.nodeIndex;
9696         }
9697         var ns = this.nodes;
9698         for(var i = 0, len = ns.length; i < len; i++){
9699             if(ns[i] == node){
9700                 return i;
9701             }
9702         }
9703         return -1;
9704     }
9705 });
9706 /*
9707  * - LGPL
9708  *
9709  * based on jquery fullcalendar
9710  * 
9711  */
9712
9713 Roo.bootstrap = Roo.bootstrap || {};
9714 /**
9715  * @class Roo.bootstrap.Calendar
9716  * @extends Roo.bootstrap.Component
9717  * Bootstrap Calendar class
9718  * @cfg {Boolean} loadMask (true|false) default false
9719     
9720  * @constructor
9721  * Create a new Container
9722  * @param {Object} config The config object
9723  */
9724
9725
9726
9727 Roo.bootstrap.Calendar = function(config){
9728     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9729      this.addEvents({
9730         /**
9731              * @event select
9732              * Fires when a date is selected
9733              * @param {DatePicker} this
9734              * @param {Date} date The selected date
9735              */
9736         'select': true,
9737         /**
9738              * @event monthchange
9739              * Fires when the displayed month changes 
9740              * @param {DatePicker} this
9741              * @param {Date} date The selected month
9742              */
9743         'monthchange': true,
9744         /**
9745              * @event evententer
9746              * Fires when mouse over an event
9747              * @param {Calendar} this
9748              * @param {event} Event
9749              */
9750         'evententer': true,
9751         /**
9752              * @event eventleave
9753              * Fires when the mouse leaves an
9754              * @param {Calendar} this
9755              * @param {event}
9756              */
9757         'eventleave': true,
9758         /**
9759              * @event eventclick
9760              * Fires when the mouse click an
9761              * @param {Calendar} this
9762              * @param {event}
9763              */
9764         'eventclick': true
9765         
9766     });
9767
9768 };
9769
9770 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9771     
9772      /**
9773      * @cfg {Number} startDay
9774      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9775      */
9776     startDay : 0,
9777     
9778     loadMask : false,
9779       
9780     getAutoCreate : function(){
9781         
9782         
9783         var fc_button = function(name, corner, style, content ) {
9784             return Roo.apply({},{
9785                 tag : 'span',
9786                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9787                          (corner.length ?
9788                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9789                             ''
9790                         ),
9791                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9792                 unselectable: 'on'
9793             });
9794         };
9795         
9796         var header = {
9797             tag : 'table',
9798             cls : 'fc-header',
9799             style : 'width:100%',
9800             cn : [
9801                 {
9802                     tag: 'tr',
9803                     cn : [
9804                         {
9805                             tag : 'td',
9806                             cls : 'fc-header-left',
9807                             cn : [
9808                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9809                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9810                                 { tag: 'span', cls: 'fc-header-space' },
9811                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9812                                 
9813                                 
9814                             ]
9815                         },
9816                         
9817                         {
9818                             tag : 'td',
9819                             cls : 'fc-header-center',
9820                             cn : [
9821                                 {
9822                                     tag: 'span',
9823                                     cls: 'fc-header-title',
9824                                     cn : {
9825                                         tag: 'H2',
9826                                         html : 'month / year'
9827                                     }
9828                                 }
9829                                 
9830                             ]
9831                         },
9832                         {
9833                             tag : 'td',
9834                             cls : 'fc-header-right',
9835                             cn : [
9836                           /*      fc_button('month', 'left', '', 'month' ),
9837                                 fc_button('week', '', '', 'week' ),
9838                                 fc_button('day', 'right', '', 'day' )
9839                             */    
9840                                 
9841                             ]
9842                         }
9843                         
9844                     ]
9845                 }
9846             ]
9847         };
9848         
9849        
9850         var cal_heads = function() {
9851             var ret = [];
9852             // fixme - handle this.
9853             
9854             for (var i =0; i < Date.dayNames.length; i++) {
9855                 var d = Date.dayNames[i];
9856                 ret.push({
9857                     tag: 'th',
9858                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9859                     html : d.substring(0,3)
9860                 });
9861                 
9862             }
9863             ret[0].cls += ' fc-first';
9864             ret[6].cls += ' fc-last';
9865             return ret;
9866         };
9867         var cal_cell = function(n) {
9868             return  {
9869                 tag: 'td',
9870                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9871                 cn : [
9872                     {
9873                         cn : [
9874                             {
9875                                 cls: 'fc-day-number',
9876                                 html: 'D'
9877                             },
9878                             {
9879                                 cls: 'fc-day-content',
9880                              
9881                                 cn : [
9882                                      {
9883                                         style: 'position: relative;' // height: 17px;
9884                                     }
9885                                 ]
9886                             }
9887                             
9888                             
9889                         ]
9890                     }
9891                 ]
9892                 
9893             }
9894         };
9895         var cal_rows = function() {
9896             
9897             var ret = []
9898             for (var r = 0; r < 6; r++) {
9899                 var row= {
9900                     tag : 'tr',
9901                     cls : 'fc-week',
9902                     cn : []
9903                 };
9904                 
9905                 for (var i =0; i < Date.dayNames.length; i++) {
9906                     var d = Date.dayNames[i];
9907                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9908
9909                 }
9910                 row.cn[0].cls+=' fc-first';
9911                 row.cn[0].cn[0].style = 'min-height:90px';
9912                 row.cn[6].cls+=' fc-last';
9913                 ret.push(row);
9914                 
9915             }
9916             ret[0].cls += ' fc-first';
9917             ret[4].cls += ' fc-prev-last';
9918             ret[5].cls += ' fc-last';
9919             return ret;
9920             
9921         };
9922         
9923         var cal_table = {
9924             tag: 'table',
9925             cls: 'fc-border-separate',
9926             style : 'width:100%',
9927             cellspacing  : 0,
9928             cn : [
9929                 { 
9930                     tag: 'thead',
9931                     cn : [
9932                         { 
9933                             tag: 'tr',
9934                             cls : 'fc-first fc-last',
9935                             cn : cal_heads()
9936                         }
9937                     ]
9938                 },
9939                 { 
9940                     tag: 'tbody',
9941                     cn : cal_rows()
9942                 }
9943                   
9944             ]
9945         };
9946          
9947          var cfg = {
9948             cls : 'fc fc-ltr',
9949             cn : [
9950                 header,
9951                 {
9952                     cls : 'fc-content',
9953                     style : "position: relative;",
9954                     cn : [
9955                         {
9956                             cls : 'fc-view fc-view-month fc-grid',
9957                             style : 'position: relative',
9958                             unselectable : 'on',
9959                             cn : [
9960                                 {
9961                                     cls : 'fc-event-container',
9962                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9963                                 },
9964                                 cal_table
9965                             ]
9966                         }
9967                     ]
9968     
9969                 }
9970            ] 
9971             
9972         };
9973         
9974          
9975         
9976         return cfg;
9977     },
9978     
9979     
9980     initEvents : function()
9981     {
9982         if(!this.store){
9983             throw "can not find store for calendar";
9984         }
9985         
9986         var mark = {
9987             tag: "div",
9988             cls:"x-dlg-mask",
9989             style: "text-align:center",
9990             cn: [
9991                 {
9992                     tag: "div",
9993                     style: "background-color:white;width:50%;margin:250 auto",
9994                     cn: [
9995                         {
9996                             tag: "img",
9997                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9998                         },
9999                         {
10000                             tag: "span",
10001                             html: "Loading"
10002                         }
10003                         
10004                     ]
10005                 }
10006             ]
10007         }
10008         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10009         
10010         var size = this.el.select('.fc-content', true).first().getSize();
10011         this.maskEl.setSize(size.width, size.height);
10012         this.maskEl.enableDisplayMode("block");
10013         if(!this.loadMask){
10014             this.maskEl.hide();
10015         }
10016         
10017         this.store = Roo.factory(this.store, Roo.data);
10018         this.store.on('load', this.onLoad, this);
10019         this.store.on('beforeload', this.onBeforeLoad, this);
10020         
10021         this.resize();
10022         
10023         this.cells = this.el.select('.fc-day',true);
10024         //Roo.log(this.cells);
10025         this.textNodes = this.el.query('.fc-day-number');
10026         this.cells.addClassOnOver('fc-state-hover');
10027         
10028         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10029         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10030         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10031         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10032         
10033         this.on('monthchange', this.onMonthChange, this);
10034         
10035         this.update(new Date().clearTime());
10036     },
10037     
10038     resize : function() {
10039         var sz  = this.el.getSize();
10040         
10041         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10042         this.el.select('.fc-day-content div',true).setHeight(34);
10043     },
10044     
10045     
10046     // private
10047     showPrevMonth : function(e){
10048         this.update(this.activeDate.add("mo", -1));
10049     },
10050     showToday : function(e){
10051         this.update(new Date().clearTime());
10052     },
10053     // private
10054     showNextMonth : function(e){
10055         this.update(this.activeDate.add("mo", 1));
10056     },
10057
10058     // private
10059     showPrevYear : function(){
10060         this.update(this.activeDate.add("y", -1));
10061     },
10062
10063     // private
10064     showNextYear : function(){
10065         this.update(this.activeDate.add("y", 1));
10066     },
10067
10068     
10069    // private
10070     update : function(date)
10071     {
10072         var vd = this.activeDate;
10073         this.activeDate = date;
10074 //        if(vd && this.el){
10075 //            var t = date.getTime();
10076 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10077 //                Roo.log('using add remove');
10078 //                
10079 //                this.fireEvent('monthchange', this, date);
10080 //                
10081 //                this.cells.removeClass("fc-state-highlight");
10082 //                this.cells.each(function(c){
10083 //                   if(c.dateValue == t){
10084 //                       c.addClass("fc-state-highlight");
10085 //                       setTimeout(function(){
10086 //                            try{c.dom.firstChild.focus();}catch(e){}
10087 //                       }, 50);
10088 //                       return false;
10089 //                   }
10090 //                   return true;
10091 //                });
10092 //                return;
10093 //            }
10094 //        }
10095         
10096         var days = date.getDaysInMonth();
10097         
10098         var firstOfMonth = date.getFirstDateOfMonth();
10099         var startingPos = firstOfMonth.getDay()-this.startDay;
10100         
10101         if(startingPos < this.startDay){
10102             startingPos += 7;
10103         }
10104         
10105         var pm = date.add(Date.MONTH, -1);
10106         var prevStart = pm.getDaysInMonth()-startingPos;
10107 //        
10108         this.cells = this.el.select('.fc-day',true);
10109         this.textNodes = this.el.query('.fc-day-number');
10110         this.cells.addClassOnOver('fc-state-hover');
10111         
10112         var cells = this.cells.elements;
10113         var textEls = this.textNodes;
10114         
10115         Roo.each(cells, function(cell){
10116             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10117         });
10118         
10119         days += startingPos;
10120
10121         // convert everything to numbers so it's fast
10122         var day = 86400000;
10123         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10124         //Roo.log(d);
10125         //Roo.log(pm);
10126         //Roo.log(prevStart);
10127         
10128         var today = new Date().clearTime().getTime();
10129         var sel = date.clearTime().getTime();
10130         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10131         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10132         var ddMatch = this.disabledDatesRE;
10133         var ddText = this.disabledDatesText;
10134         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10135         var ddaysText = this.disabledDaysText;
10136         var format = this.format;
10137         
10138         var setCellClass = function(cal, cell){
10139             
10140             //Roo.log('set Cell Class');
10141             cell.title = "";
10142             var t = d.getTime();
10143             
10144             //Roo.log(d);
10145             
10146             cell.dateValue = t;
10147             if(t == today){
10148                 cell.className += " fc-today";
10149                 cell.className += " fc-state-highlight";
10150                 cell.title = cal.todayText;
10151             }
10152             if(t == sel){
10153                 // disable highlight in other month..
10154                 //cell.className += " fc-state-highlight";
10155                 
10156             }
10157             // disabling
10158             if(t < min) {
10159                 cell.className = " fc-state-disabled";
10160                 cell.title = cal.minText;
10161                 return;
10162             }
10163             if(t > max) {
10164                 cell.className = " fc-state-disabled";
10165                 cell.title = cal.maxText;
10166                 return;
10167             }
10168             if(ddays){
10169                 if(ddays.indexOf(d.getDay()) != -1){
10170                     cell.title = ddaysText;
10171                     cell.className = " fc-state-disabled";
10172                 }
10173             }
10174             if(ddMatch && format){
10175                 var fvalue = d.dateFormat(format);
10176                 if(ddMatch.test(fvalue)){
10177                     cell.title = ddText.replace("%0", fvalue);
10178                     cell.className = " fc-state-disabled";
10179                 }
10180             }
10181             
10182             if (!cell.initialClassName) {
10183                 cell.initialClassName = cell.dom.className;
10184             }
10185             
10186             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10187         };
10188
10189         var i = 0;
10190         
10191         for(; i < startingPos; i++) {
10192             textEls[i].innerHTML = (++prevStart);
10193             d.setDate(d.getDate()+1);
10194             
10195             cells[i].className = "fc-past fc-other-month";
10196             setCellClass(this, cells[i]);
10197         }
10198         
10199         var intDay = 0;
10200         
10201         for(; i < days; i++){
10202             intDay = i - startingPos + 1;
10203             textEls[i].innerHTML = (intDay);
10204             d.setDate(d.getDate()+1);
10205             
10206             cells[i].className = ''; // "x-date-active";
10207             setCellClass(this, cells[i]);
10208         }
10209         var extraDays = 0;
10210         
10211         for(; i < 42; i++) {
10212             textEls[i].innerHTML = (++extraDays);
10213             d.setDate(d.getDate()+1);
10214             
10215             cells[i].className = "fc-future fc-other-month";
10216             setCellClass(this, cells[i]);
10217         }
10218         
10219         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10220         
10221         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10222         
10223         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10224         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10225         
10226         if(totalRows != 6){
10227             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10228             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10229         }
10230         
10231         this.fireEvent('monthchange', this, date);
10232         
10233         
10234         /*
10235         if(!this.internalRender){
10236             var main = this.el.dom.firstChild;
10237             var w = main.offsetWidth;
10238             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10239             Roo.fly(main).setWidth(w);
10240             this.internalRender = true;
10241             // opera does not respect the auto grow header center column
10242             // then, after it gets a width opera refuses to recalculate
10243             // without a second pass
10244             if(Roo.isOpera && !this.secondPass){
10245                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10246                 this.secondPass = true;
10247                 this.update.defer(10, this, [date]);
10248             }
10249         }
10250         */
10251         
10252     },
10253     
10254     findCell : function(dt) {
10255         dt = dt.clearTime().getTime();
10256         var ret = false;
10257         this.cells.each(function(c){
10258             //Roo.log("check " +c.dateValue + '?=' + dt);
10259             if(c.dateValue == dt){
10260                 ret = c;
10261                 return false;
10262             }
10263             return true;
10264         });
10265         
10266         return ret;
10267     },
10268     
10269     findCells : function(ev) {
10270         var s = ev.start.clone().clearTime().getTime();
10271        // Roo.log(s);
10272         var e= ev.end.clone().clearTime().getTime();
10273        // Roo.log(e);
10274         var ret = [];
10275         this.cells.each(function(c){
10276              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10277             
10278             if(c.dateValue > e){
10279                 return ;
10280             }
10281             if(c.dateValue < s){
10282                 return ;
10283             }
10284             ret.push(c);
10285         });
10286         
10287         return ret;    
10288     },
10289     
10290     findBestRow: function(cells)
10291     {
10292         var ret = 0;
10293         
10294         for (var i =0 ; i < cells.length;i++) {
10295             ret  = Math.max(cells[i].rows || 0,ret);
10296         }
10297         return ret;
10298         
10299     },
10300     
10301     
10302     addItem : function(ev)
10303     {
10304         // look for vertical location slot in
10305         var cells = this.findCells(ev);
10306         
10307         ev.row = this.findBestRow(cells);
10308         
10309         // work out the location.
10310         
10311         var crow = false;
10312         var rows = [];
10313         for(var i =0; i < cells.length; i++) {
10314             if (!crow) {
10315                 crow = {
10316                     start : cells[i],
10317                     end :  cells[i]
10318                 };
10319                 continue;
10320             }
10321             if (crow.start.getY() == cells[i].getY()) {
10322                 // on same row.
10323                 crow.end = cells[i];
10324                 continue;
10325             }
10326             // different row.
10327             rows.push(crow);
10328             crow = {
10329                 start: cells[i],
10330                 end : cells[i]
10331             };
10332             
10333         }
10334         
10335         rows.push(crow);
10336         ev.els = [];
10337         ev.rows = rows;
10338         ev.cells = cells;
10339         for (var i = 0; i < cells.length;i++) {
10340             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10341             
10342         }
10343         
10344         this.calevents.push(ev);
10345     },
10346     
10347     clearEvents: function() {
10348         
10349         if(!this.calevents){
10350             return;
10351         }
10352         
10353         Roo.each(this.cells.elements, function(c){
10354             c.rows = 0;
10355         });
10356         
10357         Roo.each(this.calevents, function(e) {
10358             Roo.each(e.els, function(el) {
10359                 el.un('mouseenter' ,this.onEventEnter, this);
10360                 el.un('mouseleave' ,this.onEventLeave, this);
10361                 el.remove();
10362             },this);
10363         },this);
10364         
10365     },
10366     
10367     renderEvents: function()
10368     {   
10369         // first make sure there is enough space..
10370         
10371         this.cells.each(function(c) {
10372         
10373             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10374         });
10375         
10376         for (var e = 0; e < this.calevents.length; e++) {
10377             var ev = this.calevents[e];
10378             var cells = ev.cells;
10379             var rows = ev.rows;
10380             
10381             for(var i =0; i < rows.length; i++) {
10382                 
10383                  
10384                 // how many rows should it span..
10385                 
10386                 var  cfg = {
10387                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10388                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10389                     
10390                     unselectable : "on",
10391                     cn : [
10392                         {
10393                             cls: 'fc-event-inner',
10394                             cn : [
10395 //                                {
10396 //                                  tag:'span',
10397 //                                  cls: 'fc-event-time',
10398 //                                  html : cells.length > 1 ? '' : ev.time
10399 //                                },
10400                                 {
10401                                   tag:'span',
10402                                   cls: 'fc-event-title',
10403                                   html : String.format('{0}', ev.title)
10404                                 }
10405                                 
10406                                 
10407                             ]
10408                         },
10409                         {
10410                             cls: 'ui-resizable-handle ui-resizable-e',
10411                             html : '&nbsp;&nbsp;&nbsp'
10412                         }
10413                         
10414                     ]
10415                 };
10416                 if (i == 0) {
10417                     cfg.cls += ' fc-event-start';
10418                 }
10419                 if ((i+1) == rows.length) {
10420                     cfg.cls += ' fc-event-end';
10421                 }
10422                 
10423                 var ctr = this.el.select('.fc-event-container',true).first();
10424                 var cg = ctr.createChild(cfg);
10425                 
10426                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10427                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10428                 cg.on('click', this.onEventClick, this, ev);
10429                 
10430                 ev.els.push(cg);
10431                 
10432                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10433                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10434                 //Roo.log(cg);
10435                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10436                 cg.setWidth(ebox.right - sbox.x -2);
10437             }
10438             
10439             
10440         }
10441         
10442     },
10443     
10444     onEventEnter: function (e, el,event,d) {
10445         this.fireEvent('evententer', this, el, event);
10446     },
10447     
10448     onEventLeave: function (e, el,event,d) {
10449         this.fireEvent('eventleave', this, el, event);
10450     },
10451     
10452     onEventClick: function (e, el,event,d) {
10453         this.fireEvent('eventclick', this, el, event);
10454     },
10455     
10456     onMonthChange: function () {
10457         this.store.load();
10458     },
10459     
10460     onLoad: function () 
10461     {   
10462         this.calevents = [];
10463         var cal = this;
10464         
10465         if(this.store.getCount() > 0){
10466             this.store.data.each(function(d){
10467                cal.addItem({
10468                     id : d.data.id,
10469                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10470                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10471                     time : d.data.start_time,
10472                     title : d.data.title,
10473                     description : d.data.description,
10474                     venue : d.data.venue
10475                 });
10476             });
10477         }
10478         
10479         this.renderEvents();
10480         
10481         if(this.loadMask){
10482             this.maskEl.hide();
10483         }
10484     },
10485     
10486     onBeforeLoad: function()
10487     {
10488         this.clearEvents();
10489         
10490         if(this.loadMask){
10491             this.maskEl.show();
10492         }
10493     }
10494 });
10495
10496  
10497  /*
10498  * - LGPL
10499  *
10500  * element
10501  * 
10502  */
10503
10504 /**
10505  * @class Roo.bootstrap.Popover
10506  * @extends Roo.bootstrap.Component
10507  * Bootstrap Popover class
10508  * @cfg {String} html contents of the popover   (or false to use children..)
10509  * @cfg {String} title of popover (or false to hide)
10510  * @cfg {String} placement how it is placed
10511  * @cfg {String} trigger click || hover (or false to trigger manually)
10512  * @cfg {String} over what (parent or false to trigger manually.)
10513  * 
10514  * @constructor
10515  * Create a new Popover
10516  * @param {Object} config The config object
10517  */
10518
10519 Roo.bootstrap.Popover = function(config){
10520     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10521 };
10522
10523 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10524     
10525     title: 'Fill in a title',
10526     html: false,
10527     
10528     placement : 'right',
10529     trigger : 'hover', // hover
10530     
10531     over: 'parent',
10532     
10533     can_build_overlaid : false,
10534     
10535     getChildContainer : function()
10536     {
10537         return this.el.select('.popover-content',true).first();
10538     },
10539     
10540     getAutoCreate : function(){
10541          Roo.log('make popover?');
10542         var cfg = {
10543            cls : 'popover roo-dynamic',
10544            style: 'display:block',
10545            cn : [
10546                 {
10547                     cls : 'arrow'
10548                 },
10549                 {
10550                     cls : 'popover-inner',
10551                     cn : [
10552                         {
10553                             tag: 'h3',
10554                             cls: 'popover-title',
10555                             html : this.title
10556                         },
10557                         {
10558                             cls : 'popover-content',
10559                             html : this.html
10560                         }
10561                     ]
10562                     
10563                 }
10564            ]
10565         };
10566         
10567         return cfg;
10568     },
10569     setTitle: function(str)
10570     {
10571         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10572     },
10573     setContent: function(str)
10574     {
10575         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10576     },
10577     // as it get's added to the bottom of the page.
10578     onRender : function(ct, position)
10579     {
10580         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10581         if(!this.el){
10582             var cfg = Roo.apply({},  this.getAutoCreate());
10583             cfg.id = Roo.id();
10584             
10585             if (this.cls) {
10586                 cfg.cls += ' ' + this.cls;
10587             }
10588             if (this.style) {
10589                 cfg.style = this.style;
10590             }
10591             Roo.log("adding to ")
10592             this.el = Roo.get(document.body).createChild(cfg, position);
10593             Roo.log(this.el);
10594         }
10595         this.initEvents();
10596     },
10597     
10598     initEvents : function()
10599     {
10600         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10601         this.el.enableDisplayMode('block');
10602         this.el.hide();
10603         if (this.over === false) {
10604             return; 
10605         }
10606         if (this.triggers === false) {
10607             return;
10608         }
10609         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10610         var triggers = this.trigger ? this.trigger.split(' ') : [];
10611         Roo.each(triggers, function(trigger) {
10612         
10613             if (trigger == 'click') {
10614                 on_el.on('click', this.toggle, this);
10615             } else if (trigger != 'manual') {
10616                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10617                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10618       
10619                 on_el.on(eventIn  ,this.enter, this);
10620                 on_el.on(eventOut, this.leave, this);
10621             }
10622         }, this);
10623         
10624     },
10625     
10626     
10627     // private
10628     timeout : null,
10629     hoverState : null,
10630     
10631     toggle : function () {
10632         this.hoverState == 'in' ? this.leave() : this.enter();
10633     },
10634     
10635     enter : function () {
10636        
10637     
10638         clearTimeout(this.timeout);
10639     
10640         this.hoverState = 'in'
10641     
10642         if (!this.delay || !this.delay.show) {
10643             this.show();
10644             return 
10645         }
10646         var _t = this;
10647         this.timeout = setTimeout(function () {
10648             if (_t.hoverState == 'in') {
10649                 _t.show();
10650             }
10651         }, this.delay.show)
10652     },
10653     leave : function() {
10654         clearTimeout(this.timeout);
10655     
10656         this.hoverState = 'out'
10657     
10658         if (!this.delay || !this.delay.hide) {
10659             this.hide();
10660             return 
10661         }
10662         var _t = this;
10663         this.timeout = setTimeout(function () {
10664             if (_t.hoverState == 'out') {
10665                 _t.hide();
10666             }
10667         }, this.delay.hide)
10668     },
10669     
10670     show : function (on_el)
10671     {
10672         if (!on_el) {
10673             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10674         }
10675         // set content.
10676         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10677         if (this.html !== false) {
10678             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10679         }
10680         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10681         if (!this.title.length) {
10682             this.el.select('.popover-title',true).hide();
10683         }
10684         
10685         var placement = typeof this.placement == 'function' ?
10686             this.placement.call(this, this.el, on_el) :
10687             this.placement;
10688             
10689         var autoToken = /\s?auto?\s?/i;
10690         var autoPlace = autoToken.test(placement);
10691         if (autoPlace) {
10692             placement = placement.replace(autoToken, '') || 'top';
10693         }
10694         
10695         //this.el.detach()
10696         //this.el.setXY([0,0]);
10697         this.el.show();
10698         this.el.dom.style.display='block';
10699         this.el.addClass(placement);
10700         
10701         //this.el.appendTo(on_el);
10702         
10703         var p = this.getPosition();
10704         var box = this.el.getBox();
10705         
10706         if (autoPlace) {
10707             // fixme..
10708         }
10709         var align = Roo.bootstrap.Popover.alignment[placement]
10710         this.el.alignTo(on_el, align[0],align[1]);
10711         //var arrow = this.el.select('.arrow',true).first();
10712         //arrow.set(align[2], 
10713         
10714         this.el.addClass('in');
10715         this.hoverState = null;
10716         
10717         if (this.el.hasClass('fade')) {
10718             // fade it?
10719         }
10720         
10721     },
10722     hide : function()
10723     {
10724         this.el.setXY([0,0]);
10725         this.el.removeClass('in');
10726         this.el.hide();
10727         
10728     }
10729     
10730 });
10731
10732 Roo.bootstrap.Popover.alignment = {
10733     'left' : ['r-l', [-10,0], 'right'],
10734     'right' : ['l-r', [10,0], 'left'],
10735     'bottom' : ['t-b', [0,10], 'top'],
10736     'top' : [ 'b-t', [0,-10], 'bottom']
10737 };
10738
10739  /*
10740  * - LGPL
10741  *
10742  * Progress
10743  * 
10744  */
10745
10746 /**
10747  * @class Roo.bootstrap.Progress
10748  * @extends Roo.bootstrap.Component
10749  * Bootstrap Progress class
10750  * @cfg {Boolean} striped striped of the progress bar
10751  * @cfg {Boolean} active animated of the progress bar
10752  * 
10753  * 
10754  * @constructor
10755  * Create a new Progress
10756  * @param {Object} config The config object
10757  */
10758
10759 Roo.bootstrap.Progress = function(config){
10760     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10761 };
10762
10763 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10764     
10765     striped : false,
10766     active: false,
10767     
10768     getAutoCreate : function(){
10769         var cfg = {
10770             tag: 'div',
10771             cls: 'progress'
10772         };
10773         
10774         
10775         if(this.striped){
10776             cfg.cls += ' progress-striped';
10777         }
10778       
10779         if(this.active){
10780             cfg.cls += ' active';
10781         }
10782         
10783         
10784         return cfg;
10785     }
10786    
10787 });
10788
10789  
10790
10791  /*
10792  * - LGPL
10793  *
10794  * ProgressBar
10795  * 
10796  */
10797
10798 /**
10799  * @class Roo.bootstrap.ProgressBar
10800  * @extends Roo.bootstrap.Component
10801  * Bootstrap ProgressBar class
10802  * @cfg {Number} aria_valuenow aria-value now
10803  * @cfg {Number} aria_valuemin aria-value min
10804  * @cfg {Number} aria_valuemax aria-value max
10805  * @cfg {String} label label for the progress bar
10806  * @cfg {String} panel (success | info | warning | danger )
10807  * @cfg {String} role role of the progress bar
10808  * @cfg {String} sr_only text
10809  * 
10810  * 
10811  * @constructor
10812  * Create a new ProgressBar
10813  * @param {Object} config The config object
10814  */
10815
10816 Roo.bootstrap.ProgressBar = function(config){
10817     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10818 };
10819
10820 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10821     
10822     aria_valuenow : 0,
10823     aria_valuemin : 0,
10824     aria_valuemax : 100,
10825     label : false,
10826     panel : false,
10827     role : false,
10828     sr_only: false,
10829     
10830     getAutoCreate : function()
10831     {
10832         
10833         var cfg = {
10834             tag: 'div',
10835             cls: 'progress-bar',
10836             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10837         };
10838         
10839         if(this.sr_only){
10840             cfg.cn = {
10841                 tag: 'span',
10842                 cls: 'sr-only',
10843                 html: this.sr_only
10844             }
10845         }
10846         
10847         if(this.role){
10848             cfg.role = this.role;
10849         }
10850         
10851         if(this.aria_valuenow){
10852             cfg['aria-valuenow'] = this.aria_valuenow;
10853         }
10854         
10855         if(this.aria_valuemin){
10856             cfg['aria-valuemin'] = this.aria_valuemin;
10857         }
10858         
10859         if(this.aria_valuemax){
10860             cfg['aria-valuemax'] = this.aria_valuemax;
10861         }
10862         
10863         if(this.label && !this.sr_only){
10864             cfg.html = this.label;
10865         }
10866         
10867         if(this.panel){
10868             cfg.cls += ' progress-bar-' + this.panel;
10869         }
10870         
10871         return cfg;
10872     },
10873     
10874     update : function(aria_valuenow)
10875     {
10876         this.aria_valuenow = aria_valuenow;
10877         
10878         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10879     }
10880    
10881 });
10882
10883  
10884
10885  /*
10886  * - LGPL
10887  *
10888  * TabPanel
10889  * 
10890  */
10891
10892 /**
10893  * @class Roo.bootstrap.TabPanel
10894  * @extends Roo.bootstrap.Component
10895  * Bootstrap TabPanel class
10896  * @cfg {Boolean} active panel active
10897  * @cfg {String} html panel content
10898  * @cfg {String} tabId tab relate id
10899  * 
10900  * 
10901  * @constructor
10902  * Create a new TabPanel
10903  * @param {Object} config The config object
10904  */
10905
10906 Roo.bootstrap.TabPanel = function(config){
10907     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10908 };
10909
10910 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10911     
10912     active: false,
10913     html: false,
10914     tabId: false,
10915     
10916     getAutoCreate : function(){
10917         var cfg = {
10918             tag: 'div',
10919             cls: 'tab-pane',
10920             html: this.html || ''
10921         };
10922         
10923         if(this.active){
10924             cfg.cls += ' active';
10925         }
10926         
10927         if(this.tabId){
10928             cfg.tabId = this.tabId;
10929         }
10930         
10931         return cfg;
10932     }
10933    
10934 });
10935
10936  
10937
10938  /*
10939  * - LGPL
10940  *
10941  * DateField
10942  * 
10943  */
10944
10945 /**
10946  * @class Roo.bootstrap.DateField
10947  * @extends Roo.bootstrap.Input
10948  * Bootstrap DateField class
10949  * @cfg {Number} weekStart default 0
10950  * @cfg {Number} weekStart default 0
10951  * @cfg {Number} viewMode default empty, (months|years)
10952  * @cfg {Number} minViewMode default empty, (months|years)
10953  * @cfg {Number} startDate default -Infinity
10954  * @cfg {Number} endDate default Infinity
10955  * @cfg {Boolean} todayHighlight default false
10956  * @cfg {Boolean} todayBtn default false
10957  * @cfg {Boolean} calendarWeeks default false
10958  * @cfg {Object} daysOfWeekDisabled default empty
10959  * 
10960  * @cfg {Boolean} keyboardNavigation default true
10961  * @cfg {String} language default en
10962  * 
10963  * @constructor
10964  * Create a new DateField
10965  * @param {Object} config The config object
10966  */
10967
10968 Roo.bootstrap.DateField = function(config){
10969     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10970      this.addEvents({
10971             /**
10972              * @event show
10973              * Fires when this field show.
10974              * @param {Roo.bootstrap.DateField} this
10975              * @param {Mixed} date The date value
10976              */
10977             show : true,
10978             /**
10979              * @event show
10980              * Fires when this field hide.
10981              * @param {Roo.bootstrap.DateField} this
10982              * @param {Mixed} date The date value
10983              */
10984             hide : true,
10985             /**
10986              * @event select
10987              * Fires when select a date.
10988              * @param {Roo.bootstrap.DateField} this
10989              * @param {Mixed} date The date value
10990              */
10991             select : true
10992         });
10993 };
10994
10995 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10996     
10997     /**
10998      * @cfg {String} format
10999      * The default date format string which can be overriden for localization support.  The format must be
11000      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11001      */
11002     format : "m/d/y",
11003     /**
11004      * @cfg {String} altFormats
11005      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11006      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11007      */
11008     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11009     
11010     weekStart : 0,
11011     
11012     viewMode : '',
11013     
11014     minViewMode : '',
11015     
11016     todayHighlight : false,
11017     
11018     todayBtn: false,
11019     
11020     language: 'en',
11021     
11022     keyboardNavigation: true,
11023     
11024     calendarWeeks: false,
11025     
11026     startDate: -Infinity,
11027     
11028     endDate: Infinity,
11029     
11030     daysOfWeekDisabled: [],
11031     
11032     _events: [],
11033     
11034     UTCDate: function()
11035     {
11036         return new Date(Date.UTC.apply(Date, arguments));
11037     },
11038     
11039     UTCToday: function()
11040     {
11041         var today = new Date();
11042         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11043     },
11044     
11045     getDate: function() {
11046             var d = this.getUTCDate();
11047             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11048     },
11049     
11050     getUTCDate: function() {
11051             return this.date;
11052     },
11053     
11054     setDate: function(d) {
11055             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11056     },
11057     
11058     setUTCDate: function(d) {
11059             this.date = d;
11060             this.setValue(this.formatDate(this.date));
11061     },
11062         
11063     onRender: function(ct, position)
11064     {
11065         
11066         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11067         
11068         this.language = this.language || 'en';
11069         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11070         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11071         
11072         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11073         this.format = this.format || 'm/d/y';
11074         this.isInline = false;
11075         this.isInput = true;
11076         this.component = this.el.select('.add-on', true).first() || false;
11077         this.component = (this.component && this.component.length === 0) ? false : this.component;
11078         this.hasInput = this.component && this.inputEL().length;
11079         
11080         if (typeof(this.minViewMode === 'string')) {
11081             switch (this.minViewMode) {
11082                 case 'months':
11083                     this.minViewMode = 1;
11084                     break;
11085                 case 'years':
11086                     this.minViewMode = 2;
11087                     break;
11088                 default:
11089                     this.minViewMode = 0;
11090                     break;
11091             }
11092         }
11093         
11094         if (typeof(this.viewMode === 'string')) {
11095             switch (this.viewMode) {
11096                 case 'months':
11097                     this.viewMode = 1;
11098                     break;
11099                 case 'years':
11100                     this.viewMode = 2;
11101                     break;
11102                 default:
11103                     this.viewMode = 0;
11104                     break;
11105             }
11106         }
11107                 
11108         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11109         
11110         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11111         
11112         this.picker().on('mousedown', this.onMousedown, this);
11113         this.picker().on('click', this.onClick, this);
11114         
11115         this.picker().addClass('datepicker-dropdown');
11116         
11117         this.startViewMode = this.viewMode;
11118         
11119         
11120         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11121             if(!this.calendarWeeks){
11122                 v.remove();
11123                 return;
11124             };
11125             
11126             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11127             v.attr('colspan', function(i, val){
11128                 return parseInt(val) + 1;
11129             });
11130         })
11131                         
11132         
11133         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11134         
11135         this.setStartDate(this.startDate);
11136         this.setEndDate(this.endDate);
11137         
11138         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11139         
11140         this.fillDow();
11141         this.fillMonths();
11142         this.update();
11143         this.showMode();
11144         
11145         if(this.isInline) {
11146             this.show();
11147         }
11148     },
11149     
11150     picker : function()
11151     {
11152         return this.el.select('.datepicker', true).first();
11153     },
11154     
11155     fillDow: function()
11156     {
11157         var dowCnt = this.weekStart;
11158         
11159         var dow = {
11160             tag: 'tr',
11161             cn: [
11162                 
11163             ]
11164         };
11165         
11166         if(this.calendarWeeks){
11167             dow.cn.push({
11168                 tag: 'th',
11169                 cls: 'cw',
11170                 html: '&nbsp;'
11171             })
11172         }
11173         
11174         while (dowCnt < this.weekStart + 7) {
11175             dow.cn.push({
11176                 tag: 'th',
11177                 cls: 'dow',
11178                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11179             });
11180         }
11181         
11182         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11183     },
11184     
11185     fillMonths: function()
11186     {    
11187         var i = 0
11188         var months = this.picker().select('>.datepicker-months td', true).first();
11189         
11190         months.dom.innerHTML = '';
11191         
11192         while (i < 12) {
11193             var month = {
11194                 tag: 'span',
11195                 cls: 'month',
11196                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11197             }
11198             
11199             months.createChild(month);
11200         }
11201         
11202     },
11203     
11204     update: function(){
11205         
11206         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11207         
11208         if (this.date < this.startDate) {
11209             this.viewDate = new Date(this.startDate);
11210         } else if (this.date > this.endDate) {
11211             this.viewDate = new Date(this.endDate);
11212         } else {
11213             this.viewDate = new Date(this.date);
11214         }
11215         
11216         this.fill();
11217     },
11218     
11219     fill: function() {
11220         var d = new Date(this.viewDate),
11221                 year = d.getUTCFullYear(),
11222                 month = d.getUTCMonth(),
11223                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11224                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11225                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11226                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11227                 currentDate = this.date && this.date.valueOf(),
11228                 today = this.UTCToday();
11229         
11230         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11231         
11232 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11233         
11234 //        this.picker.select('>tfoot th.today').
11235 //                                              .text(dates[this.language].today)
11236 //                                              .toggle(this.todayBtn !== false);
11237     
11238         this.updateNavArrows();
11239         this.fillMonths();
11240                                                 
11241         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11242         
11243         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11244          
11245         prevMonth.setUTCDate(day);
11246         
11247         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11248         
11249         var nextMonth = new Date(prevMonth);
11250         
11251         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11252         
11253         nextMonth = nextMonth.valueOf();
11254         
11255         var fillMonths = false;
11256         
11257         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11258         
11259         while(prevMonth.valueOf() < nextMonth) {
11260             var clsName = '';
11261             
11262             if (prevMonth.getUTCDay() === this.weekStart) {
11263                 if(fillMonths){
11264                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11265                 }
11266                     
11267                 fillMonths = {
11268                     tag: 'tr',
11269                     cn: []
11270                 };
11271                 
11272                 if(this.calendarWeeks){
11273                     // ISO 8601: First week contains first thursday.
11274                     // ISO also states week starts on Monday, but we can be more abstract here.
11275                     var
11276                     // Start of current week: based on weekstart/current date
11277                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11278                     // Thursday of this week
11279                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11280                     // First Thursday of year, year from thursday
11281                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11282                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11283                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11284                     
11285                     fillMonths.cn.push({
11286                         tag: 'td',
11287                         cls: 'cw',
11288                         html: calWeek
11289                     });
11290                 }
11291             }
11292             
11293             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11294                 clsName += ' old';
11295             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11296                 clsName += ' new';
11297             }
11298             if (this.todayHighlight &&
11299                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11300                 prevMonth.getUTCMonth() == today.getMonth() &&
11301                 prevMonth.getUTCDate() == today.getDate()) {
11302                 clsName += ' today';
11303             }
11304             
11305             if (currentDate && prevMonth.valueOf() === currentDate) {
11306                 clsName += ' active';
11307             }
11308             
11309             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11310                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11311                     clsName += ' disabled';
11312             }
11313             
11314             fillMonths.cn.push({
11315                 tag: 'td',
11316                 cls: 'day ' + clsName,
11317                 html: prevMonth.getDate()
11318             })
11319             
11320             prevMonth.setDate(prevMonth.getDate()+1);
11321         }
11322           
11323         var currentYear = this.date && this.date.getUTCFullYear();
11324         var currentMonth = this.date && this.date.getUTCMonth();
11325         
11326         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11327         
11328         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11329             v.removeClass('active');
11330             
11331             if(currentYear === year && k === currentMonth){
11332                 v.addClass('active');
11333             }
11334             
11335             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11336                 v.addClass('disabled');
11337             }
11338             
11339         });
11340         
11341         
11342         year = parseInt(year/10, 10) * 10;
11343         
11344         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11345         
11346         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11347         
11348         year -= 1;
11349         for (var i = -1; i < 11; i++) {
11350             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11351                 tag: 'span',
11352                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11353                 html: year
11354             })
11355             
11356             year += 1;
11357         }
11358     },
11359     
11360     showMode: function(dir) {
11361         if (dir) {
11362             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11363         }
11364         Roo.each(this.picker().select('>div',true).elements, function(v){
11365             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11366             v.hide();
11367         });
11368         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11369     },
11370     
11371     place: function()
11372     {
11373         if(this.isInline) return;
11374         
11375         this.picker().removeClass(['bottom', 'top']);
11376         
11377         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11378             /*
11379              * place to the top of element!
11380              *
11381              */
11382             
11383             this.picker().addClass('top');
11384             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11385             
11386             return;
11387         }
11388         
11389         this.picker().addClass('bottom');
11390         
11391         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11392     },
11393     
11394     parseDate : function(value){
11395         if(!value || value instanceof Date){
11396             return value;
11397         }
11398         var v = Date.parseDate(value, this.format);
11399         if (!v && this.useIso) {
11400             v = Date.parseDate(value, 'Y-m-d');
11401         }
11402         if(!v && this.altFormats){
11403             if(!this.altFormatsArray){
11404                 this.altFormatsArray = this.altFormats.split("|");
11405             }
11406             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11407                 v = Date.parseDate(value, this.altFormatsArray[i]);
11408             }
11409         }
11410         return v;
11411     },
11412     
11413     formatDate : function(date, fmt){
11414         return (!date || !(date instanceof Date)) ?
11415         date : date.dateFormat(fmt || this.format);
11416     },
11417     
11418     onFocus : function()
11419     {
11420         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11421         this.show();
11422     },
11423     
11424     onBlur : function()
11425     {
11426         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11427         this.hide();
11428     },
11429     
11430     show : function()
11431     {
11432         this.picker().show();
11433         this.update();
11434         this.place();
11435         
11436         this.fireEvent('show', this, this.date);
11437     },
11438     
11439     hide : function()
11440     {
11441         if(this.isInline) return;
11442         this.picker().hide();
11443         this.viewMode = this.startViewMode;
11444         this.showMode();
11445         
11446         this.fireEvent('hide', this, this.date);
11447         
11448     },
11449     
11450     onMousedown: function(e){
11451         e.stopPropagation();
11452         e.preventDefault();
11453     },
11454     
11455     keyup: function(e){
11456         Roo.bootstrap.DateField.superclass.keyup.call(this);
11457         this.update();
11458         
11459     },
11460
11461     setValue: function(v){
11462         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11463         
11464         this.fireEvent('select', this, this.date);
11465         
11466     },
11467     
11468     fireKey: function(e){
11469         if (!this.picker().isVisible()){
11470             if (e.keyCode == 27) // allow escape to hide and re-show picker
11471                 this.show();
11472             return;
11473         }
11474         var dateChanged = false,
11475         dir, day, month,
11476         newDate, newViewDate;
11477         switch(e.keyCode){
11478             case 27: // escape
11479                 this.hide();
11480                 e.preventDefault();
11481                 break;
11482             case 37: // left
11483             case 39: // right
11484                 if (!this.keyboardNavigation) break;
11485                 dir = e.keyCode == 37 ? -1 : 1;
11486                 
11487                 if (e.ctrlKey){
11488                     newDate = this.moveYear(this.date, dir);
11489                     newViewDate = this.moveYear(this.viewDate, dir);
11490                 } else if (e.shiftKey){
11491                     newDate = this.moveMonth(this.date, dir);
11492                     newViewDate = this.moveMonth(this.viewDate, dir);
11493                 } else {
11494                     newDate = new Date(this.date);
11495                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11496                     newViewDate = new Date(this.viewDate);
11497                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11498                 }
11499                 if (this.dateWithinRange(newDate)){
11500                     this.date = newDate;
11501                     this.viewDate = newViewDate;
11502                     this.setValue(this.formatDate(this.date));
11503                     this.update();
11504                     e.preventDefault();
11505                     dateChanged = true;
11506                 }
11507                 break;
11508             case 38: // up
11509             case 40: // down
11510                 if (!this.keyboardNavigation) break;
11511                 dir = e.keyCode == 38 ? -1 : 1;
11512                 if (e.ctrlKey){
11513                     newDate = this.moveYear(this.date, dir);
11514                     newViewDate = this.moveYear(this.viewDate, dir);
11515                 } else if (e.shiftKey){
11516                     newDate = this.moveMonth(this.date, dir);
11517                     newViewDate = this.moveMonth(this.viewDate, dir);
11518                 } else {
11519                     newDate = new Date(this.date);
11520                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11521                     newViewDate = new Date(this.viewDate);
11522                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11523                 }
11524                 if (this.dateWithinRange(newDate)){
11525                     this.date = newDate;
11526                     this.viewDate = newViewDate;
11527                     this.setValue(this.formatDate(this.date));
11528                     this.update();
11529                     e.preventDefault();
11530                     dateChanged = true;
11531                 }
11532                 break;
11533             case 13: // enter
11534                 this.setValue(this.formatDate(this.date));
11535                 this.hide();
11536                 e.preventDefault();
11537                 break;
11538             case 9: // tab
11539                 this.setValue(this.formatDate(this.date));
11540                 this.hide();
11541                 break;
11542         }
11543     },
11544     
11545     
11546     onClick: function(e) {
11547         e.stopPropagation();
11548         e.preventDefault();
11549         
11550         var target = e.getTarget();
11551         
11552         if(target.nodeName.toLowerCase() === 'i'){
11553             target = Roo.get(target).dom.parentNode;
11554         }
11555         
11556         var nodeName = target.nodeName;
11557         var className = target.className;
11558         var html = target.innerHTML;
11559         
11560         switch(nodeName.toLowerCase()) {
11561             case 'th':
11562                 switch(className) {
11563                     case 'switch':
11564                         this.showMode(1);
11565                         break;
11566                     case 'prev':
11567                     case 'next':
11568                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11569                         switch(this.viewMode){
11570                                 case 0:
11571                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11572                                         break;
11573                                 case 1:
11574                                 case 2:
11575                                         this.viewDate = this.moveYear(this.viewDate, dir);
11576                                         break;
11577                         }
11578                         this.fill();
11579                         break;
11580                     case 'today':
11581                         var date = new Date();
11582                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11583                         this.fill()
11584                         this.setValue(this.formatDate(this.date));
11585                         this.hide();
11586                         break;
11587                 }
11588                 break;
11589             case 'span':
11590                 if (className.indexOf('disabled') === -1) {
11591                     this.viewDate.setUTCDate(1);
11592                     if (className.indexOf('month') !== -1) {
11593                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11594                     } else {
11595                         var year = parseInt(html, 10) || 0;
11596                         this.viewDate.setUTCFullYear(year);
11597                         
11598                     }
11599                     this.showMode(-1);
11600                     this.fill();
11601                 }
11602                 break;
11603                 
11604             case 'td':
11605                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11606                     var day = parseInt(html, 10) || 1;
11607                     var year = this.viewDate.getUTCFullYear(),
11608                         month = this.viewDate.getUTCMonth();
11609
11610                     if (className.indexOf('old') !== -1) {
11611                         if(month === 0 ){
11612                             month = 11;
11613                             year -= 1;
11614                         }else{
11615                             month -= 1;
11616                         }
11617                     } else if (className.indexOf('new') !== -1) {
11618                         if (month == 11) {
11619                             month = 0;
11620                             year += 1;
11621                         } else {
11622                             month += 1;
11623                         }
11624                     }
11625                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11626                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11627                     this.fill();
11628                     this.setValue(this.formatDate(this.date));
11629                     this.hide();
11630                 }
11631                 break;
11632         }
11633     },
11634     
11635     setStartDate: function(startDate){
11636         this.startDate = startDate || -Infinity;
11637         if (this.startDate !== -Infinity) {
11638             this.startDate = this.parseDate(this.startDate);
11639         }
11640         this.update();
11641         this.updateNavArrows();
11642     },
11643
11644     setEndDate: function(endDate){
11645         this.endDate = endDate || Infinity;
11646         if (this.endDate !== Infinity) {
11647             this.endDate = this.parseDate(this.endDate);
11648         }
11649         this.update();
11650         this.updateNavArrows();
11651     },
11652     
11653     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11654         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11655         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11656             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11657         }
11658         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11659             return parseInt(d, 10);
11660         });
11661         this.update();
11662         this.updateNavArrows();
11663     },
11664     
11665     updateNavArrows: function() {
11666         var d = new Date(this.viewDate),
11667         year = d.getUTCFullYear(),
11668         month = d.getUTCMonth();
11669         
11670         Roo.each(this.picker().select('.prev', true).elements, function(v){
11671             v.show();
11672             switch (this.viewMode) {
11673                 case 0:
11674
11675                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11676                         v.hide();
11677                     }
11678                     break;
11679                 case 1:
11680                 case 2:
11681                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11682                         v.hide();
11683                     }
11684                     break;
11685             }
11686         });
11687         
11688         Roo.each(this.picker().select('.next', true).elements, function(v){
11689             v.show();
11690             switch (this.viewMode) {
11691                 case 0:
11692
11693                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11694                         v.hide();
11695                     }
11696                     break;
11697                 case 1:
11698                 case 2:
11699                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11700                         v.hide();
11701                     }
11702                     break;
11703             }
11704         })
11705     },
11706     
11707     moveMonth: function(date, dir){
11708         if (!dir) return date;
11709         var new_date = new Date(date.valueOf()),
11710         day = new_date.getUTCDate(),
11711         month = new_date.getUTCMonth(),
11712         mag = Math.abs(dir),
11713         new_month, test;
11714         dir = dir > 0 ? 1 : -1;
11715         if (mag == 1){
11716             test = dir == -1
11717             // If going back one month, make sure month is not current month
11718             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11719             ? function(){
11720                 return new_date.getUTCMonth() == month;
11721             }
11722             // If going forward one month, make sure month is as expected
11723             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11724             : function(){
11725                 return new_date.getUTCMonth() != new_month;
11726             };
11727             new_month = month + dir;
11728             new_date.setUTCMonth(new_month);
11729             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11730             if (new_month < 0 || new_month > 11)
11731                 new_month = (new_month + 12) % 12;
11732         } else {
11733             // For magnitudes >1, move one month at a time...
11734             for (var i=0; i<mag; i++)
11735                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11736                 new_date = this.moveMonth(new_date, dir);
11737             // ...then reset the day, keeping it in the new month
11738             new_month = new_date.getUTCMonth();
11739             new_date.setUTCDate(day);
11740             test = function(){
11741                 return new_month != new_date.getUTCMonth();
11742             };
11743         }
11744         // Common date-resetting loop -- if date is beyond end of month, make it
11745         // end of month
11746         while (test()){
11747             new_date.setUTCDate(--day);
11748             new_date.setUTCMonth(new_month);
11749         }
11750         return new_date;
11751     },
11752
11753     moveYear: function(date, dir){
11754         return this.moveMonth(date, dir*12);
11755     },
11756
11757     dateWithinRange: function(date){
11758         return date >= this.startDate && date <= this.endDate;
11759     },
11760
11761     
11762     remove: function() {
11763         this.picker().remove();
11764     }
11765    
11766 });
11767
11768 Roo.apply(Roo.bootstrap.DateField,  {
11769     
11770     head : {
11771         tag: 'thead',
11772         cn: [
11773         {
11774             tag: 'tr',
11775             cn: [
11776             {
11777                 tag: 'th',
11778                 cls: 'prev',
11779                 html: '<i class="icon-arrow-left"/>'
11780             },
11781             {
11782                 tag: 'th',
11783                 cls: 'switch',
11784                 colspan: '5'
11785             },
11786             {
11787                 tag: 'th',
11788                 cls: 'next',
11789                 html: '<i class="icon-arrow-right"/>'
11790             }
11791
11792             ]
11793         }
11794         ]
11795     },
11796     
11797     content : {
11798         tag: 'tbody',
11799         cn: [
11800         {
11801             tag: 'tr',
11802             cn: [
11803             {
11804                 tag: 'td',
11805                 colspan: '7'
11806             }
11807             ]
11808         }
11809         ]
11810     },
11811     
11812     footer : {
11813         tag: 'tfoot',
11814         cn: [
11815         {
11816             tag: 'tr',
11817             cn: [
11818             {
11819                 tag: 'th',
11820                 colspan: '7',
11821                 cls: 'today'
11822             }
11823                     
11824             ]
11825         }
11826         ]
11827     },
11828     
11829     dates:{
11830         en: {
11831             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11832             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11833             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11834             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11835             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11836             today: "Today"
11837         }
11838     },
11839     
11840     modes: [
11841     {
11842         clsName: 'days',
11843         navFnc: 'Month',
11844         navStep: 1
11845     },
11846     {
11847         clsName: 'months',
11848         navFnc: 'FullYear',
11849         navStep: 1
11850     },
11851     {
11852         clsName: 'years',
11853         navFnc: 'FullYear',
11854         navStep: 10
11855     }]
11856 });
11857
11858 Roo.apply(Roo.bootstrap.DateField,  {
11859   
11860     template : {
11861         tag: 'div',
11862         cls: 'datepicker dropdown-menu',
11863         cn: [
11864         {
11865             tag: 'div',
11866             cls: 'datepicker-days',
11867             cn: [
11868             {
11869                 tag: 'table',
11870                 cls: 'table-condensed',
11871                 cn:[
11872                 Roo.bootstrap.DateField.head,
11873                 {
11874                     tag: 'tbody'
11875                 },
11876                 Roo.bootstrap.DateField.footer
11877                 ]
11878             }
11879             ]
11880         },
11881         {
11882             tag: 'div',
11883             cls: 'datepicker-months',
11884             cn: [
11885             {
11886                 tag: 'table',
11887                 cls: 'table-condensed',
11888                 cn:[
11889                 Roo.bootstrap.DateField.head,
11890                 Roo.bootstrap.DateField.content,
11891                 Roo.bootstrap.DateField.footer
11892                 ]
11893             }
11894             ]
11895         },
11896         {
11897             tag: 'div',
11898             cls: 'datepicker-years',
11899             cn: [
11900             {
11901                 tag: 'table',
11902                 cls: 'table-condensed',
11903                 cn:[
11904                 Roo.bootstrap.DateField.head,
11905                 Roo.bootstrap.DateField.content,
11906                 Roo.bootstrap.DateField.footer
11907                 ]
11908             }
11909             ]
11910         }
11911         ]
11912     }
11913 });
11914
11915  
11916
11917  /*
11918  * - LGPL
11919  *
11920  * TimeField
11921  * 
11922  */
11923
11924 /**
11925  * @class Roo.bootstrap.TimeField
11926  * @extends Roo.bootstrap.Input
11927  * Bootstrap DateField class
11928  * 
11929  * 
11930  * @constructor
11931  * Create a new TimeField
11932  * @param {Object} config The config object
11933  */
11934
11935 Roo.bootstrap.TimeField = function(config){
11936     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11937     this.addEvents({
11938             /**
11939              * @event show
11940              * Fires when this field show.
11941              * @param {Roo.bootstrap.DateField} this
11942              * @param {Mixed} date The date value
11943              */
11944             show : true,
11945             /**
11946              * @event show
11947              * Fires when this field hide.
11948              * @param {Roo.bootstrap.DateField} this
11949              * @param {Mixed} date The date value
11950              */
11951             hide : true,
11952             /**
11953              * @event select
11954              * Fires when select a date.
11955              * @param {Roo.bootstrap.DateField} this
11956              * @param {Mixed} date The date value
11957              */
11958             select : true
11959         });
11960 };
11961
11962 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11963     
11964     /**
11965      * @cfg {String} format
11966      * The default time format string which can be overriden for localization support.  The format must be
11967      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11968      */
11969     format : "H:i",
11970        
11971     onRender: function(ct, position)
11972     {
11973         
11974         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11975                 
11976         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11977         
11978         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11979         
11980         this.pop = this.picker().select('>.datepicker-time',true).first();
11981         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11982         
11983         this.picker().on('mousedown', this.onMousedown, this);
11984         this.picker().on('click', this.onClick, this);
11985         
11986         this.picker().addClass('datepicker-dropdown');
11987     
11988         this.fillTime();
11989         this.update();
11990             
11991         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11992         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11993         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11994         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11995         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11996         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11997
11998     },
11999     
12000     fireKey: function(e){
12001         if (!this.picker().isVisible()){
12002             if (e.keyCode == 27) // allow escape to hide and re-show picker
12003                 this.show();
12004             return;
12005         }
12006
12007         e.preventDefault();
12008         
12009         switch(e.keyCode){
12010             case 27: // escape
12011                 this.hide();
12012                 break;
12013             case 37: // left
12014             case 39: // right
12015                 this.onTogglePeriod();
12016                 break;
12017             case 38: // up
12018                 this.onIncrementMinutes();
12019                 break;
12020             case 40: // down
12021                 this.onDecrementMinutes();
12022                 break;
12023             case 13: // enter
12024             case 9: // tab
12025                 this.setTime();
12026                 break;
12027         }
12028     },
12029     
12030     onClick: function(e) {
12031         e.stopPropagation();
12032         e.preventDefault();
12033     },
12034     
12035     picker : function()
12036     {
12037         return this.el.select('.datepicker', true).first();
12038     },
12039     
12040     fillTime: function()
12041     {    
12042         var time = this.pop.select('tbody', true).first();
12043         
12044         time.dom.innerHTML = '';
12045         
12046         time.createChild({
12047             tag: 'tr',
12048             cn: [
12049                 {
12050                     tag: 'td',
12051                     cn: [
12052                         {
12053                             tag: 'a',
12054                             href: '#',
12055                             cls: 'btn',
12056                             cn: [
12057                                 {
12058                                     tag: 'span',
12059                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12060                                 }
12061                             ]
12062                         } 
12063                     ]
12064                 },
12065                 {
12066                     tag: 'td',
12067                     cls: 'separator'
12068                 },
12069                 {
12070                     tag: 'td',
12071                     cn: [
12072                         {
12073                             tag: 'a',
12074                             href: '#',
12075                             cls: 'btn',
12076                             cn: [
12077                                 {
12078                                     tag: 'span',
12079                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12080                                 }
12081                             ]
12082                         }
12083                     ]
12084                 },
12085                 {
12086                     tag: 'td',
12087                     cls: 'separator'
12088                 }
12089             ]
12090         });
12091         
12092         time.createChild({
12093             tag: 'tr',
12094             cn: [
12095                 {
12096                     tag: 'td',
12097                     cn: [
12098                         {
12099                             tag: 'span',
12100                             cls: 'timepicker-hour',
12101                             html: '00'
12102                         }  
12103                     ]
12104                 },
12105                 {
12106                     tag: 'td',
12107                     cls: 'separator',
12108                     html: ':'
12109                 },
12110                 {
12111                     tag: 'td',
12112                     cn: [
12113                         {
12114                             tag: 'span',
12115                             cls: 'timepicker-minute',
12116                             html: '00'
12117                         }  
12118                     ]
12119                 },
12120                 {
12121                     tag: 'td',
12122                     cls: 'separator'
12123                 },
12124                 {
12125                     tag: 'td',
12126                     cn: [
12127                         {
12128                             tag: 'button',
12129                             type: 'button',
12130                             cls: 'btn btn-primary period',
12131                             html: 'AM'
12132                             
12133                         }
12134                     ]
12135                 }
12136             ]
12137         });
12138         
12139         time.createChild({
12140             tag: 'tr',
12141             cn: [
12142                 {
12143                     tag: 'td',
12144                     cn: [
12145                         {
12146                             tag: 'a',
12147                             href: '#',
12148                             cls: 'btn',
12149                             cn: [
12150                                 {
12151                                     tag: 'span',
12152                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12153                                 }
12154                             ]
12155                         }
12156                     ]
12157                 },
12158                 {
12159                     tag: 'td',
12160                     cls: 'separator'
12161                 },
12162                 {
12163                     tag: 'td',
12164                     cn: [
12165                         {
12166                             tag: 'a',
12167                             href: '#',
12168                             cls: 'btn',
12169                             cn: [
12170                                 {
12171                                     tag: 'span',
12172                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12173                                 }
12174                             ]
12175                         }
12176                     ]
12177                 },
12178                 {
12179                     tag: 'td',
12180                     cls: 'separator'
12181                 }
12182             ]
12183         });
12184         
12185     },
12186     
12187     update: function()
12188     {
12189         
12190         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12191         
12192         this.fill();
12193     },
12194     
12195     fill: function() 
12196     {
12197         var hours = this.time.getHours();
12198         var minutes = this.time.getMinutes();
12199         var period = 'AM';
12200         
12201         if(hours > 11){
12202             period = 'PM';
12203         }
12204         
12205         if(hours == 0){
12206             hours = 12;
12207         }
12208         
12209         
12210         if(hours > 12){
12211             hours = hours - 12;
12212         }
12213         
12214         if(hours < 10){
12215             hours = '0' + hours;
12216         }
12217         
12218         if(minutes < 10){
12219             minutes = '0' + minutes;
12220         }
12221         
12222         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12223         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12224         this.pop.select('button', true).first().dom.innerHTML = period;
12225         
12226     },
12227     
12228     place: function()
12229     {   
12230         this.picker().removeClass(['bottom', 'top']);
12231         
12232         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12233             /*
12234              * place to the top of element!
12235              *
12236              */
12237             
12238             this.picker().addClass('top');
12239             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12240             
12241             return;
12242         }
12243         
12244         this.picker().addClass('bottom');
12245         
12246         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12247     },
12248   
12249     onFocus : function()
12250     {
12251         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12252         this.show();
12253     },
12254     
12255     onBlur : function()
12256     {
12257         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12258         this.hide();
12259     },
12260     
12261     show : function()
12262     {
12263         this.picker().show();
12264         this.pop.show();
12265         this.update();
12266         this.place();
12267         
12268         this.fireEvent('show', this, this.date);
12269     },
12270     
12271     hide : function()
12272     {
12273         this.picker().hide();
12274         this.pop.hide();
12275         
12276         this.fireEvent('hide', this, this.date);
12277     },
12278     
12279     setTime : function()
12280     {
12281         this.hide();
12282         this.setValue(this.time.format(this.format));
12283         
12284         this.fireEvent('select', this, this.date);
12285         
12286         
12287     },
12288     
12289     onMousedown: function(e){
12290         e.stopPropagation();
12291         e.preventDefault();
12292     },
12293     
12294     onIncrementHours: function()
12295     {
12296         Roo.log('onIncrementHours');
12297         this.time = this.time.add(Date.HOUR, 1);
12298         this.update();
12299         
12300     },
12301     
12302     onDecrementHours: function()
12303     {
12304         Roo.log('onDecrementHours');
12305         this.time = this.time.add(Date.HOUR, -1);
12306         this.update();
12307     },
12308     
12309     onIncrementMinutes: function()
12310     {
12311         Roo.log('onIncrementMinutes');
12312         this.time = this.time.add(Date.MINUTE, 1);
12313         this.update();
12314     },
12315     
12316     onDecrementMinutes: function()
12317     {
12318         Roo.log('onDecrementMinutes');
12319         this.time = this.time.add(Date.MINUTE, -1);
12320         this.update();
12321     },
12322     
12323     onTogglePeriod: function()
12324     {
12325         Roo.log('onTogglePeriod');
12326         this.time = this.time.add(Date.HOUR, 12);
12327         this.update();
12328     }
12329     
12330    
12331 });
12332
12333 Roo.apply(Roo.bootstrap.TimeField,  {
12334     
12335     content : {
12336         tag: 'tbody',
12337         cn: [
12338             {
12339                 tag: 'tr',
12340                 cn: [
12341                 {
12342                     tag: 'td',
12343                     colspan: '7'
12344                 }
12345                 ]
12346             }
12347         ]
12348     },
12349     
12350     footer : {
12351         tag: 'tfoot',
12352         cn: [
12353             {
12354                 tag: 'tr',
12355                 cn: [
12356                 {
12357                     tag: 'th',
12358                     colspan: '7',
12359                     cls: '',
12360                     cn: [
12361                         {
12362                             tag: 'button',
12363                             cls: 'btn btn-info ok',
12364                             html: 'OK'
12365                         }
12366                     ]
12367                 }
12368
12369                 ]
12370             }
12371         ]
12372     }
12373 });
12374
12375 Roo.apply(Roo.bootstrap.TimeField,  {
12376   
12377     template : {
12378         tag: 'div',
12379         cls: 'datepicker dropdown-menu',
12380         cn: [
12381             {
12382                 tag: 'div',
12383                 cls: 'datepicker-time',
12384                 cn: [
12385                 {
12386                     tag: 'table',
12387                     cls: 'table-condensed',
12388                     cn:[
12389                     Roo.bootstrap.TimeField.content,
12390                     Roo.bootstrap.TimeField.footer
12391                     ]
12392                 }
12393                 ]
12394             }
12395         ]
12396     }
12397 });
12398
12399  
12400
12401  /*
12402  * - LGPL
12403  *
12404  * CheckBox
12405  * 
12406  */
12407
12408 /**
12409  * @class Roo.bootstrap.CheckBox
12410  * @extends Roo.bootstrap.Input
12411  * Bootstrap CheckBox class
12412  * 
12413  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12414  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12415  * @cfg {String} boxLabel The text that appears beside the checkbox
12416  * @cfg {Boolean} checked initnal the element
12417  * 
12418  * @constructor
12419  * Create a new CheckBox
12420  * @param {Object} config The config object
12421  */
12422
12423 Roo.bootstrap.CheckBox = function(config){
12424     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12425    
12426         this.addEvents({
12427             /**
12428             * @event check
12429             * Fires when the element is checked or unchecked.
12430             * @param {Roo.bootstrap.CheckBox} this This input
12431             * @param {Boolean} checked The new checked value
12432             */
12433            check : true
12434         });
12435 };
12436
12437 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12438     
12439     inputType: 'checkbox',
12440     inputValue: 1,
12441     valueOff: 0,
12442     boxLabel: false,
12443     checked: false,
12444     
12445     getAutoCreate : function()
12446     {
12447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12448         
12449         var id = Roo.id();
12450         
12451         var cfg = {};
12452         
12453         cfg.cls = 'form-group' //input-group
12454         
12455         var input =  {
12456             tag: 'input',
12457             id : id,
12458             type : this.inputType,
12459             value : (!this.checked) ? this.valueOff : this.inputValue,
12460             cls : 'form-box',
12461             placeholder : this.placeholder || ''
12462             
12463         };
12464         
12465         if (this.disabled) {
12466             input.disabled=true;
12467         }
12468         
12469         if(this.checked){
12470             input.checked = this.checked;
12471         }
12472         
12473         if (this.name) {
12474             input.name = this.name;
12475         }
12476         
12477         if (this.size) {
12478             input.cls += ' input-' + this.size;
12479         }
12480         
12481         var settings=this;
12482         ['xs','sm','md','lg'].map(function(size){
12483             if (settings[size]) {
12484                 cfg.cls += ' col-' + size + '-' + settings[size];
12485             }
12486         });
12487         
12488         var inputblock = input;
12489         
12490         if (this.before || this.after) {
12491             
12492             inputblock = {
12493                 cls : 'input-group',
12494                 cn :  [] 
12495             };
12496             if (this.before) {
12497                 inputblock.cn.push({
12498                     tag :'span',
12499                     cls : 'input-group-addon',
12500                     html : this.before
12501                 });
12502             }
12503             inputblock.cn.push(input);
12504             if (this.after) {
12505                 inputblock.cn.push({
12506                     tag :'span',
12507                     cls : 'input-group-addon',
12508                     html : this.after
12509                 });
12510             }
12511             
12512         };
12513         
12514         if (align ==='left' && this.fieldLabel.length) {
12515                 Roo.log("left and has label");
12516                 cfg.cn = [
12517                     
12518                     {
12519                         tag: 'label',
12520                         'for' :  id,
12521                         cls : 'control-label col-md-' + this.labelWidth,
12522                         html : this.fieldLabel
12523                         
12524                     },
12525                     {
12526                         cls : "col-md-" + (12 - this.labelWidth), 
12527                         cn: [
12528                             inputblock
12529                         ]
12530                     }
12531                     
12532                 ];
12533         } else if ( this.fieldLabel.length) {
12534                 Roo.log(" label");
12535                 cfg.cn = [
12536                    
12537                     {
12538                         tag: this.boxLabel ? 'span' : 'label',
12539                         'for': id,
12540                         cls: 'control-label box-input-label',
12541                         //cls : 'input-group-addon',
12542                         html : this.fieldLabel
12543                         
12544                     },
12545                     
12546                     inputblock
12547                     
12548                 ];
12549
12550         } else {
12551             
12552                    Roo.log(" no label && no align");
12553                 cfg.cn = [
12554                     
12555                         inputblock
12556                     
12557                 ];
12558                 
12559                 
12560         };
12561         
12562         if(this.boxLabel){
12563             cfg.cn.push({
12564                 tag: 'label',
12565                 'for': id,
12566                 cls: 'box-label',
12567                 html: this.boxLabel
12568             })
12569         }
12570         
12571         return cfg;
12572         
12573     },
12574     
12575     /**
12576      * return the real input element.
12577      */
12578     inputEl: function ()
12579     {
12580         return this.el.select('input.form-box',true).first();
12581     },
12582     
12583     initEvents : function()
12584     {
12585 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12586         
12587         this.inputEl().on('click', this.onClick,  this);
12588         
12589     },
12590     
12591     onClick : function()
12592     {   
12593         this.setChecked(!this.checked);
12594     },
12595     
12596     setChecked : function(state,suppressEvent)
12597     {
12598         this.checked = state;
12599         
12600         this.inputEl().dom.checked = state;
12601         
12602         if(suppressEvent !== true){
12603             this.fireEvent('check', this, state);
12604         }
12605         
12606         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12607         
12608     },
12609     
12610     setValue : function(v,suppressEvent)
12611     {
12612         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12613     }
12614     
12615 });
12616
12617  
12618 /*
12619  * - LGPL
12620  *
12621  * Radio
12622  * 
12623  */
12624
12625 /**
12626  * @class Roo.bootstrap.Radio
12627  * @extends Roo.bootstrap.CheckBox
12628  * Bootstrap Radio class
12629
12630  * @constructor
12631  * Create a new Radio
12632  * @param {Object} config The config object
12633  */
12634
12635 Roo.bootstrap.Radio = function(config){
12636     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12637    
12638 };
12639
12640 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12641     
12642     inputType: 'radio',
12643     inputValue: '',
12644     valueOff: '',
12645     
12646     getAutoCreate : function()
12647     {
12648         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12649         
12650         var id = Roo.id();
12651         
12652         var cfg = {};
12653         
12654         cfg.cls = 'form-group' //input-group
12655         
12656         var input =  {
12657             tag: 'input',
12658             id : id,
12659             type : this.inputType,
12660             value : (!this.checked) ? this.valueOff : this.inputValue,
12661             cls : 'form-box',
12662             placeholder : this.placeholder || ''
12663             
12664         };
12665         
12666         if (this.disabled) {
12667             input.disabled=true;
12668         }
12669         
12670         if(this.checked){
12671             input.checked = this.checked;
12672         }
12673         
12674         if (this.name) {
12675             input.name = this.name;
12676         }
12677         
12678         if (this.size) {
12679             input.cls += ' input-' + this.size;
12680         }
12681         
12682         var settings=this;
12683         ['xs','sm','md','lg'].map(function(size){
12684             if (settings[size]) {
12685                 cfg.cls += ' col-' + size + '-' + settings[size];
12686             }
12687         });
12688         
12689         var inputblock = input;
12690         
12691         if (this.before || this.after) {
12692             
12693             inputblock = {
12694                 cls : 'input-group',
12695                 cn :  [] 
12696             };
12697             if (this.before) {
12698                 inputblock.cn.push({
12699                     tag :'span',
12700                     cls : 'input-group-addon',
12701                     html : this.before
12702                 });
12703             }
12704             inputblock.cn.push(input);
12705             if (this.after) {
12706                 inputblock.cn.push({
12707                     tag :'span',
12708                     cls : 'input-group-addon',
12709                     html : this.after
12710                 });
12711             }
12712             
12713         };
12714         
12715         if (align ==='left' && this.fieldLabel.length) {
12716                 Roo.log("left and has label");
12717                 cfg.cn = [
12718                     
12719                     {
12720                         tag: 'label',
12721                         'for' :  id,
12722                         cls : 'control-label col-md-' + this.labelWidth,
12723                         html : this.fieldLabel
12724                         
12725                     },
12726                     {
12727                         cls : "col-md-" + (12 - this.labelWidth), 
12728                         cn: [
12729                             inputblock
12730                         ]
12731                     }
12732                     
12733                 ];
12734         } else if ( this.fieldLabel.length) {
12735                 Roo.log(" label");
12736                  cfg.cn = [
12737                    
12738                     {
12739                         tag: 'label',
12740                         'for': id,
12741                         cls: 'control-label box-input-label',
12742                         //cls : 'input-group-addon',
12743                         html : this.fieldLabel
12744                         
12745                     },
12746                     
12747                     inputblock
12748                     
12749                 ];
12750
12751         } else {
12752             
12753                    Roo.log(" no label && no align");
12754                 cfg.cn = [
12755                     
12756                         inputblock
12757                     
12758                 ];
12759                 
12760                 
12761         };
12762         
12763         if(this.boxLabel){
12764             cfg.cn.push({
12765                 tag: 'label',
12766                 'for': id,
12767                 cls: 'box-label',
12768                 html: this.boxLabel
12769             })
12770         }
12771         
12772         return cfg;
12773         
12774     },
12775    
12776     onClick : function()
12777     {   
12778         this.setChecked(true);
12779     },
12780     
12781     setChecked : function(state,suppressEvent)
12782     {
12783         if(state){
12784             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12785                 v.dom.checked = false;
12786             });
12787         }
12788         
12789         this.checked = state;
12790         this.inputEl().dom.checked = state;
12791         
12792         if(suppressEvent !== true){
12793             this.fireEvent('check', this, state);
12794         }
12795         
12796         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12797         
12798     },
12799     
12800     getGroupValue : function()
12801     {
12802         var value = ''
12803         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12804             if(v.dom.checked == true){
12805                 value = v.dom.value;
12806             }
12807         });
12808         
12809         return value;
12810     },
12811     
12812     /**
12813      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12814      * @return {Mixed} value The field value
12815      */
12816     getValue : function(){
12817         return this.getGroupValue();
12818     }
12819     
12820 });
12821
12822  
12823 //<script type="text/javascript">
12824
12825 /*
12826  * Based  Ext JS Library 1.1.1
12827  * Copyright(c) 2006-2007, Ext JS, LLC.
12828  * LGPL
12829  *
12830  */
12831  
12832 /**
12833  * @class Roo.HtmlEditorCore
12834  * @extends Roo.Component
12835  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12836  *
12837  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12838  */
12839
12840 Roo.HtmlEditorCore = function(config){
12841     
12842     
12843     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12844     this.addEvents({
12845         /**
12846          * @event initialize
12847          * Fires when the editor is fully initialized (including the iframe)
12848          * @param {Roo.HtmlEditorCore} this
12849          */
12850         initialize: true,
12851         /**
12852          * @event activate
12853          * Fires when the editor is first receives the focus. Any insertion must wait
12854          * until after this event.
12855          * @param {Roo.HtmlEditorCore} this
12856          */
12857         activate: true,
12858          /**
12859          * @event beforesync
12860          * Fires before the textarea is updated with content from the editor iframe. Return false
12861          * to cancel the sync.
12862          * @param {Roo.HtmlEditorCore} this
12863          * @param {String} html
12864          */
12865         beforesync: true,
12866          /**
12867          * @event beforepush
12868          * Fires before the iframe editor is updated with content from the textarea. Return false
12869          * to cancel the push.
12870          * @param {Roo.HtmlEditorCore} this
12871          * @param {String} html
12872          */
12873         beforepush: true,
12874          /**
12875          * @event sync
12876          * Fires when the textarea is updated with content from the editor iframe.
12877          * @param {Roo.HtmlEditorCore} this
12878          * @param {String} html
12879          */
12880         sync: true,
12881          /**
12882          * @event push
12883          * Fires when the iframe editor is updated with content from the textarea.
12884          * @param {Roo.HtmlEditorCore} this
12885          * @param {String} html
12886          */
12887         push: true,
12888         
12889         /**
12890          * @event editorevent
12891          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12892          * @param {Roo.HtmlEditorCore} this
12893          */
12894         editorevent: true
12895     });
12896      
12897 };
12898
12899
12900 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12901
12902
12903      /**
12904      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12905      */
12906     
12907     owner : false,
12908     
12909      /**
12910      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12911      *                        Roo.resizable.
12912      */
12913     resizable : false,
12914      /**
12915      * @cfg {Number} height (in pixels)
12916      */   
12917     height: 300,
12918    /**
12919      * @cfg {Number} width (in pixels)
12920      */   
12921     width: 500,
12922     
12923     /**
12924      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12925      * 
12926      */
12927     stylesheets: false,
12928     
12929     // id of frame..
12930     frameId: false,
12931     
12932     // private properties
12933     validationEvent : false,
12934     deferHeight: true,
12935     initialized : false,
12936     activated : false,
12937     sourceEditMode : false,
12938     onFocus : Roo.emptyFn,
12939     iframePad:3,
12940     hideMode:'offsets',
12941     
12942     clearUp: true,
12943     
12944      
12945     
12946
12947     /**
12948      * Protected method that will not generally be called directly. It
12949      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12950      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12951      */
12952     getDocMarkup : function(){
12953         // body styles..
12954         var st = '';
12955         Roo.log(this.stylesheets);
12956         
12957         // inherit styels from page...?? 
12958         if (this.stylesheets === false) {
12959             
12960             Roo.get(document.head).select('style').each(function(node) {
12961                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12962             });
12963             
12964             Roo.get(document.head).select('link').each(function(node) { 
12965                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12966             });
12967             
12968         } else if (!this.stylesheets.length) {
12969                 // simple..
12970                 st = '<style type="text/css">' +
12971                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12972                    '</style>';
12973         } else {
12974             Roo.each(this.stylesheets, function(s) {
12975                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12976             });
12977             
12978         }
12979         
12980         st +=  '<style type="text/css">' +
12981             'IMG { cursor: pointer } ' +
12982         '</style>';
12983
12984         
12985         return '<html><head>' + st  +
12986             //<style type="text/css">' +
12987             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12988             //'</style>' +
12989             ' </head><body class="roo-htmleditor-body"></body></html>';
12990     },
12991
12992     // private
12993     onRender : function(ct, position)
12994     {
12995         var _t = this;
12996         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12997         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12998         
12999         
13000         this.el.dom.style.border = '0 none';
13001         this.el.dom.setAttribute('tabIndex', -1);
13002         this.el.addClass('x-hidden hide');
13003         
13004         
13005         
13006         if(Roo.isIE){ // fix IE 1px bogus margin
13007             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13008         }
13009        
13010         
13011         this.frameId = Roo.id();
13012         
13013          
13014         
13015         var iframe = this.owner.wrap.createChild({
13016             tag: 'iframe',
13017             cls: 'form-control', // bootstrap..
13018             id: this.frameId,
13019             name: this.frameId,
13020             frameBorder : 'no',
13021             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13022         }, this.el
13023         );
13024         
13025         
13026         this.iframe = iframe.dom;
13027
13028          this.assignDocWin();
13029         
13030         this.doc.designMode = 'on';
13031        
13032         this.doc.open();
13033         this.doc.write(this.getDocMarkup());
13034         this.doc.close();
13035
13036         
13037         var task = { // must defer to wait for browser to be ready
13038             run : function(){
13039                 //console.log("run task?" + this.doc.readyState);
13040                 this.assignDocWin();
13041                 if(this.doc.body || this.doc.readyState == 'complete'){
13042                     try {
13043                         this.doc.designMode="on";
13044                     } catch (e) {
13045                         return;
13046                     }
13047                     Roo.TaskMgr.stop(task);
13048                     this.initEditor.defer(10, this);
13049                 }
13050             },
13051             interval : 10,
13052             duration: 10000,
13053             scope: this
13054         };
13055         Roo.TaskMgr.start(task);
13056
13057         
13058          
13059     },
13060
13061     // private
13062     onResize : function(w, h)
13063     {
13064          Roo.log('resize: ' +w + ',' + h );
13065         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13066         if(!this.iframe){
13067             return;
13068         }
13069         if(typeof w == 'number'){
13070             
13071             this.iframe.style.width = w + 'px';
13072         }
13073         if(typeof h == 'number'){
13074             
13075             this.iframe.style.height = h + 'px';
13076             if(this.doc){
13077                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13078             }
13079         }
13080         
13081     },
13082
13083     /**
13084      * Toggles the editor between standard and source edit mode.
13085      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13086      */
13087     toggleSourceEdit : function(sourceEditMode){
13088         
13089         this.sourceEditMode = sourceEditMode === true;
13090         
13091         if(this.sourceEditMode){
13092  
13093             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13094             
13095         }else{
13096             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13097             //this.iframe.className = '';
13098             this.deferFocus();
13099         }
13100         //this.setSize(this.owner.wrap.getSize());
13101         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13102     },
13103
13104     
13105   
13106
13107     /**
13108      * Protected method that will not generally be called directly. If you need/want
13109      * custom HTML cleanup, this is the method you should override.
13110      * @param {String} html The HTML to be cleaned
13111      * return {String} The cleaned HTML
13112      */
13113     cleanHtml : function(html){
13114         html = String(html);
13115         if(html.length > 5){
13116             if(Roo.isSafari){ // strip safari nonsense
13117                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13118             }
13119         }
13120         if(html == '&nbsp;'){
13121             html = '';
13122         }
13123         return html;
13124     },
13125
13126     /**
13127      * HTML Editor -> Textarea
13128      * Protected method that will not generally be called directly. Syncs the contents
13129      * of the editor iframe with the textarea.
13130      */
13131     syncValue : function(){
13132         if(this.initialized){
13133             var bd = (this.doc.body || this.doc.documentElement);
13134             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13135             var html = bd.innerHTML;
13136             if(Roo.isSafari){
13137                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13138                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13139                 if(m && m[1]){
13140                     html = '<div style="'+m[0]+'">' + html + '</div>';
13141                 }
13142             }
13143             html = this.cleanHtml(html);
13144             // fix up the special chars.. normaly like back quotes in word...
13145             // however we do not want to do this with chinese..
13146             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13147                 var cc = b.charCodeAt();
13148                 if (
13149                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13150                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13151                     (cc >= 0xf900 && cc < 0xfb00 )
13152                 ) {
13153                         return b;
13154                 }
13155                 return "&#"+cc+";" 
13156             });
13157             if(this.owner.fireEvent('beforesync', this, html) !== false){
13158                 this.el.dom.value = html;
13159                 this.owner.fireEvent('sync', this, html);
13160             }
13161         }
13162     },
13163
13164     /**
13165      * Protected method that will not generally be called directly. Pushes the value of the textarea
13166      * into the iframe editor.
13167      */
13168     pushValue : function(){
13169         if(this.initialized){
13170             var v = this.el.dom.value.trim();
13171             
13172 //            if(v.length < 1){
13173 //                v = '&#160;';
13174 //            }
13175             
13176             if(this.owner.fireEvent('beforepush', this, v) !== false){
13177                 var d = (this.doc.body || this.doc.documentElement);
13178                 d.innerHTML = v;
13179                 this.cleanUpPaste();
13180                 this.el.dom.value = d.innerHTML;
13181                 this.owner.fireEvent('push', this, v);
13182             }
13183         }
13184     },
13185
13186     // private
13187     deferFocus : function(){
13188         this.focus.defer(10, this);
13189     },
13190
13191     // doc'ed in Field
13192     focus : function(){
13193         if(this.win && !this.sourceEditMode){
13194             this.win.focus();
13195         }else{
13196             this.el.focus();
13197         }
13198     },
13199     
13200     assignDocWin: function()
13201     {
13202         var iframe = this.iframe;
13203         
13204          if(Roo.isIE){
13205             this.doc = iframe.contentWindow.document;
13206             this.win = iframe.contentWindow;
13207         } else {
13208             if (!Roo.get(this.frameId)) {
13209                 return;
13210             }
13211             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13212             this.win = Roo.get(this.frameId).dom.contentWindow;
13213         }
13214     },
13215     
13216     // private
13217     initEditor : function(){
13218         //console.log("INIT EDITOR");
13219         this.assignDocWin();
13220         
13221         
13222         
13223         this.doc.designMode="on";
13224         this.doc.open();
13225         this.doc.write(this.getDocMarkup());
13226         this.doc.close();
13227         
13228         var dbody = (this.doc.body || this.doc.documentElement);
13229         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13230         // this copies styles from the containing element into thsi one..
13231         // not sure why we need all of this..
13232         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13233         ss['background-attachment'] = 'fixed'; // w3c
13234         dbody.bgProperties = 'fixed'; // ie
13235         Roo.DomHelper.applyStyles(dbody, ss);
13236         Roo.EventManager.on(this.doc, {
13237             //'mousedown': this.onEditorEvent,
13238             'mouseup': this.onEditorEvent,
13239             'dblclick': this.onEditorEvent,
13240             'click': this.onEditorEvent,
13241             'keyup': this.onEditorEvent,
13242             buffer:100,
13243             scope: this
13244         });
13245         if(Roo.isGecko){
13246             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13247         }
13248         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13249             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13250         }
13251         this.initialized = true;
13252
13253         this.owner.fireEvent('initialize', this);
13254         this.pushValue();
13255     },
13256
13257     // private
13258     onDestroy : function(){
13259         
13260         
13261         
13262         if(this.rendered){
13263             
13264             //for (var i =0; i < this.toolbars.length;i++) {
13265             //    // fixme - ask toolbars for heights?
13266             //    this.toolbars[i].onDestroy();
13267            // }
13268             
13269             //this.wrap.dom.innerHTML = '';
13270             //this.wrap.remove();
13271         }
13272     },
13273
13274     // private
13275     onFirstFocus : function(){
13276         
13277         this.assignDocWin();
13278         
13279         
13280         this.activated = true;
13281          
13282     
13283         if(Roo.isGecko){ // prevent silly gecko errors
13284             this.win.focus();
13285             var s = this.win.getSelection();
13286             if(!s.focusNode || s.focusNode.nodeType != 3){
13287                 var r = s.getRangeAt(0);
13288                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13289                 r.collapse(true);
13290                 this.deferFocus();
13291             }
13292             try{
13293                 this.execCmd('useCSS', true);
13294                 this.execCmd('styleWithCSS', false);
13295             }catch(e){}
13296         }
13297         this.owner.fireEvent('activate', this);
13298     },
13299
13300     // private
13301     adjustFont: function(btn){
13302         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13303         //if(Roo.isSafari){ // safari
13304         //    adjust *= 2;
13305        // }
13306         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13307         if(Roo.isSafari){ // safari
13308             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13309             v =  (v < 10) ? 10 : v;
13310             v =  (v > 48) ? 48 : v;
13311             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13312             
13313         }
13314         
13315         
13316         v = Math.max(1, v+adjust);
13317         
13318         this.execCmd('FontSize', v  );
13319     },
13320
13321     onEditorEvent : function(e){
13322         this.owner.fireEvent('editorevent', this, e);
13323       //  this.updateToolbar();
13324         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13325     },
13326
13327     insertTag : function(tg)
13328     {
13329         // could be a bit smarter... -> wrap the current selected tRoo..
13330         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13331             
13332             range = this.createRange(this.getSelection());
13333             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13334             wrappingNode.appendChild(range.extractContents());
13335             range.insertNode(wrappingNode);
13336
13337             return;
13338             
13339             
13340             
13341         }
13342         this.execCmd("formatblock",   tg);
13343         
13344     },
13345     
13346     insertText : function(txt)
13347     {
13348         
13349         
13350         var range = this.createRange();
13351         range.deleteContents();
13352                //alert(Sender.getAttribute('label'));
13353                
13354         range.insertNode(this.doc.createTextNode(txt));
13355     } ,
13356     
13357      
13358
13359     /**
13360      * Executes a Midas editor command on the editor document and performs necessary focus and
13361      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13362      * @param {String} cmd The Midas command
13363      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13364      */
13365     relayCmd : function(cmd, value){
13366         this.win.focus();
13367         this.execCmd(cmd, value);
13368         this.owner.fireEvent('editorevent', this);
13369         //this.updateToolbar();
13370         this.owner.deferFocus();
13371     },
13372
13373     /**
13374      * Executes a Midas editor command directly on the editor document.
13375      * For visual commands, you should use {@link #relayCmd} instead.
13376      * <b>This should only be called after the editor is initialized.</b>
13377      * @param {String} cmd The Midas command
13378      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13379      */
13380     execCmd : function(cmd, value){
13381         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13382         this.syncValue();
13383     },
13384  
13385  
13386    
13387     /**
13388      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13389      * to insert tRoo.
13390      * @param {String} text | dom node.. 
13391      */
13392     insertAtCursor : function(text)
13393     {
13394         
13395         
13396         
13397         if(!this.activated){
13398             return;
13399         }
13400         /*
13401         if(Roo.isIE){
13402             this.win.focus();
13403             var r = this.doc.selection.createRange();
13404             if(r){
13405                 r.collapse(true);
13406                 r.pasteHTML(text);
13407                 this.syncValue();
13408                 this.deferFocus();
13409             
13410             }
13411             return;
13412         }
13413         */
13414         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13415             this.win.focus();
13416             
13417             
13418             // from jquery ui (MIT licenced)
13419             var range, node;
13420             var win = this.win;
13421             
13422             if (win.getSelection && win.getSelection().getRangeAt) {
13423                 range = win.getSelection().getRangeAt(0);
13424                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13425                 range.insertNode(node);
13426             } else if (win.document.selection && win.document.selection.createRange) {
13427                 // no firefox support
13428                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13429                 win.document.selection.createRange().pasteHTML(txt);
13430             } else {
13431                 // no firefox support
13432                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13433                 this.execCmd('InsertHTML', txt);
13434             } 
13435             
13436             this.syncValue();
13437             
13438             this.deferFocus();
13439         }
13440     },
13441  // private
13442     mozKeyPress : function(e){
13443         if(e.ctrlKey){
13444             var c = e.getCharCode(), cmd;
13445           
13446             if(c > 0){
13447                 c = String.fromCharCode(c).toLowerCase();
13448                 switch(c){
13449                     case 'b':
13450                         cmd = 'bold';
13451                         break;
13452                     case 'i':
13453                         cmd = 'italic';
13454                         break;
13455                     
13456                     case 'u':
13457                         cmd = 'underline';
13458                         break;
13459                     
13460                     case 'v':
13461                         this.cleanUpPaste.defer(100, this);
13462                         return;
13463                         
13464                 }
13465                 if(cmd){
13466                     this.win.focus();
13467                     this.execCmd(cmd);
13468                     this.deferFocus();
13469                     e.preventDefault();
13470                 }
13471                 
13472             }
13473         }
13474     },
13475
13476     // private
13477     fixKeys : function(){ // load time branching for fastest keydown performance
13478         if(Roo.isIE){
13479             return function(e){
13480                 var k = e.getKey(), r;
13481                 if(k == e.TAB){
13482                     e.stopEvent();
13483                     r = this.doc.selection.createRange();
13484                     if(r){
13485                         r.collapse(true);
13486                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13487                         this.deferFocus();
13488                     }
13489                     return;
13490                 }
13491                 
13492                 if(k == e.ENTER){
13493                     r = this.doc.selection.createRange();
13494                     if(r){
13495                         var target = r.parentElement();
13496                         if(!target || target.tagName.toLowerCase() != 'li'){
13497                             e.stopEvent();
13498                             r.pasteHTML('<br />');
13499                             r.collapse(false);
13500                             r.select();
13501                         }
13502                     }
13503                 }
13504                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13505                     this.cleanUpPaste.defer(100, this);
13506                     return;
13507                 }
13508                 
13509                 
13510             };
13511         }else if(Roo.isOpera){
13512             return function(e){
13513                 var k = e.getKey();
13514                 if(k == e.TAB){
13515                     e.stopEvent();
13516                     this.win.focus();
13517                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13518                     this.deferFocus();
13519                 }
13520                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13521                     this.cleanUpPaste.defer(100, this);
13522                     return;
13523                 }
13524                 
13525             };
13526         }else if(Roo.isSafari){
13527             return function(e){
13528                 var k = e.getKey();
13529                 
13530                 if(k == e.TAB){
13531                     e.stopEvent();
13532                     this.execCmd('InsertText','\t');
13533                     this.deferFocus();
13534                     return;
13535                 }
13536                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13537                     this.cleanUpPaste.defer(100, this);
13538                     return;
13539                 }
13540                 
13541              };
13542         }
13543     }(),
13544     
13545     getAllAncestors: function()
13546     {
13547         var p = this.getSelectedNode();
13548         var a = [];
13549         if (!p) {
13550             a.push(p); // push blank onto stack..
13551             p = this.getParentElement();
13552         }
13553         
13554         
13555         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13556             a.push(p);
13557             p = p.parentNode;
13558         }
13559         a.push(this.doc.body);
13560         return a;
13561     },
13562     lastSel : false,
13563     lastSelNode : false,
13564     
13565     
13566     getSelection : function() 
13567     {
13568         this.assignDocWin();
13569         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13570     },
13571     
13572     getSelectedNode: function() 
13573     {
13574         // this may only work on Gecko!!!
13575         
13576         // should we cache this!!!!
13577         
13578         
13579         
13580          
13581         var range = this.createRange(this.getSelection()).cloneRange();
13582         
13583         if (Roo.isIE) {
13584             var parent = range.parentElement();
13585             while (true) {
13586                 var testRange = range.duplicate();
13587                 testRange.moveToElementText(parent);
13588                 if (testRange.inRange(range)) {
13589                     break;
13590                 }
13591                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13592                     break;
13593                 }
13594                 parent = parent.parentElement;
13595             }
13596             return parent;
13597         }
13598         
13599         // is ancestor a text element.
13600         var ac =  range.commonAncestorContainer;
13601         if (ac.nodeType == 3) {
13602             ac = ac.parentNode;
13603         }
13604         
13605         var ar = ac.childNodes;
13606          
13607         var nodes = [];
13608         var other_nodes = [];
13609         var has_other_nodes = false;
13610         for (var i=0;i<ar.length;i++) {
13611             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13612                 continue;
13613             }
13614             // fullly contained node.
13615             
13616             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13617                 nodes.push(ar[i]);
13618                 continue;
13619             }
13620             
13621             // probably selected..
13622             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13623                 other_nodes.push(ar[i]);
13624                 continue;
13625             }
13626             // outer..
13627             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13628                 continue;
13629             }
13630             
13631             
13632             has_other_nodes = true;
13633         }
13634         if (!nodes.length && other_nodes.length) {
13635             nodes= other_nodes;
13636         }
13637         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13638             return false;
13639         }
13640         
13641         return nodes[0];
13642     },
13643     createRange: function(sel)
13644     {
13645         // this has strange effects when using with 
13646         // top toolbar - not sure if it's a great idea.
13647         //this.editor.contentWindow.focus();
13648         if (typeof sel != "undefined") {
13649             try {
13650                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13651             } catch(e) {
13652                 return this.doc.createRange();
13653             }
13654         } else {
13655             return this.doc.createRange();
13656         }
13657     },
13658     getParentElement: function()
13659     {
13660         
13661         this.assignDocWin();
13662         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13663         
13664         var range = this.createRange(sel);
13665          
13666         try {
13667             var p = range.commonAncestorContainer;
13668             while (p.nodeType == 3) { // text node
13669                 p = p.parentNode;
13670             }
13671             return p;
13672         } catch (e) {
13673             return null;
13674         }
13675     
13676     },
13677     /***
13678      *
13679      * Range intersection.. the hard stuff...
13680      *  '-1' = before
13681      *  '0' = hits..
13682      *  '1' = after.
13683      *         [ -- selected range --- ]
13684      *   [fail]                        [fail]
13685      *
13686      *    basically..
13687      *      if end is before start or  hits it. fail.
13688      *      if start is after end or hits it fail.
13689      *
13690      *   if either hits (but other is outside. - then it's not 
13691      *   
13692      *    
13693      **/
13694     
13695     
13696     // @see http://www.thismuchiknow.co.uk/?p=64.
13697     rangeIntersectsNode : function(range, node)
13698     {
13699         var nodeRange = node.ownerDocument.createRange();
13700         try {
13701             nodeRange.selectNode(node);
13702         } catch (e) {
13703             nodeRange.selectNodeContents(node);
13704         }
13705     
13706         var rangeStartRange = range.cloneRange();
13707         rangeStartRange.collapse(true);
13708     
13709         var rangeEndRange = range.cloneRange();
13710         rangeEndRange.collapse(false);
13711     
13712         var nodeStartRange = nodeRange.cloneRange();
13713         nodeStartRange.collapse(true);
13714     
13715         var nodeEndRange = nodeRange.cloneRange();
13716         nodeEndRange.collapse(false);
13717     
13718         return rangeStartRange.compareBoundaryPoints(
13719                  Range.START_TO_START, nodeEndRange) == -1 &&
13720                rangeEndRange.compareBoundaryPoints(
13721                  Range.START_TO_START, nodeStartRange) == 1;
13722         
13723          
13724     },
13725     rangeCompareNode : function(range, node)
13726     {
13727         var nodeRange = node.ownerDocument.createRange();
13728         try {
13729             nodeRange.selectNode(node);
13730         } catch (e) {
13731             nodeRange.selectNodeContents(node);
13732         }
13733         
13734         
13735         range.collapse(true);
13736     
13737         nodeRange.collapse(true);
13738      
13739         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13740         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13741          
13742         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13743         
13744         var nodeIsBefore   =  ss == 1;
13745         var nodeIsAfter    = ee == -1;
13746         
13747         if (nodeIsBefore && nodeIsAfter)
13748             return 0; // outer
13749         if (!nodeIsBefore && nodeIsAfter)
13750             return 1; //right trailed.
13751         
13752         if (nodeIsBefore && !nodeIsAfter)
13753             return 2;  // left trailed.
13754         // fully contined.
13755         return 3;
13756     },
13757
13758     // private? - in a new class?
13759     cleanUpPaste :  function()
13760     {
13761         // cleans up the whole document..
13762         Roo.log('cleanuppaste');
13763         
13764         this.cleanUpChildren(this.doc.body);
13765         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13766         if (clean != this.doc.body.innerHTML) {
13767             this.doc.body.innerHTML = clean;
13768         }
13769         
13770     },
13771     
13772     cleanWordChars : function(input) {// change the chars to hex code
13773         var he = Roo.HtmlEditorCore;
13774         
13775         var output = input;
13776         Roo.each(he.swapCodes, function(sw) { 
13777             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13778             
13779             output = output.replace(swapper, sw[1]);
13780         });
13781         
13782         return output;
13783     },
13784     
13785     
13786     cleanUpChildren : function (n)
13787     {
13788         if (!n.childNodes.length) {
13789             return;
13790         }
13791         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13792            this.cleanUpChild(n.childNodes[i]);
13793         }
13794     },
13795     
13796     
13797         
13798     
13799     cleanUpChild : function (node)
13800     {
13801         var ed = this;
13802         //console.log(node);
13803         if (node.nodeName == "#text") {
13804             // clean up silly Windows -- stuff?
13805             return; 
13806         }
13807         if (node.nodeName == "#comment") {
13808             node.parentNode.removeChild(node);
13809             // clean up silly Windows -- stuff?
13810             return; 
13811         }
13812         
13813         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13814             // remove node.
13815             node.parentNode.removeChild(node);
13816             return;
13817             
13818         }
13819         
13820         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13821         
13822         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13823         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13824         
13825         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13826         //    remove_keep_children = true;
13827         //}
13828         
13829         if (remove_keep_children) {
13830             this.cleanUpChildren(node);
13831             // inserts everything just before this node...
13832             while (node.childNodes.length) {
13833                 var cn = node.childNodes[0];
13834                 node.removeChild(cn);
13835                 node.parentNode.insertBefore(cn, node);
13836             }
13837             node.parentNode.removeChild(node);
13838             return;
13839         }
13840         
13841         if (!node.attributes || !node.attributes.length) {
13842             this.cleanUpChildren(node);
13843             return;
13844         }
13845         
13846         function cleanAttr(n,v)
13847         {
13848             
13849             if (v.match(/^\./) || v.match(/^\//)) {
13850                 return;
13851             }
13852             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13853                 return;
13854             }
13855             if (v.match(/^#/)) {
13856                 return;
13857             }
13858 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13859             node.removeAttribute(n);
13860             
13861         }
13862         
13863         function cleanStyle(n,v)
13864         {
13865             if (v.match(/expression/)) { //XSS?? should we even bother..
13866                 node.removeAttribute(n);
13867                 return;
13868             }
13869             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13870             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13871             
13872             
13873             var parts = v.split(/;/);
13874             var clean = [];
13875             
13876             Roo.each(parts, function(p) {
13877                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13878                 if (!p.length) {
13879                     return true;
13880                 }
13881                 var l = p.split(':').shift().replace(/\s+/g,'');
13882                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13883                 
13884                 if ( cblack.indexOf(l) > -1) {
13885 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13886                     //node.removeAttribute(n);
13887                     return true;
13888                 }
13889                 //Roo.log()
13890                 // only allow 'c whitelisted system attributes'
13891                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13892 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13893                     //node.removeAttribute(n);
13894                     return true;
13895                 }
13896                 
13897                 
13898                  
13899                 
13900                 clean.push(p);
13901                 return true;
13902             });
13903             if (clean.length) { 
13904                 node.setAttribute(n, clean.join(';'));
13905             } else {
13906                 node.removeAttribute(n);
13907             }
13908             
13909         }
13910         
13911         
13912         for (var i = node.attributes.length-1; i > -1 ; i--) {
13913             var a = node.attributes[i];
13914             //console.log(a);
13915             
13916             if (a.name.toLowerCase().substr(0,2)=='on')  {
13917                 node.removeAttribute(a.name);
13918                 continue;
13919             }
13920             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13921                 node.removeAttribute(a.name);
13922                 continue;
13923             }
13924             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13925                 cleanAttr(a.name,a.value); // fixme..
13926                 continue;
13927             }
13928             if (a.name == 'style') {
13929                 cleanStyle(a.name,a.value);
13930                 continue;
13931             }
13932             /// clean up MS crap..
13933             // tecnically this should be a list of valid class'es..
13934             
13935             
13936             if (a.name == 'class') {
13937                 if (a.value.match(/^Mso/)) {
13938                     node.className = '';
13939                 }
13940                 
13941                 if (a.value.match(/body/)) {
13942                     node.className = '';
13943                 }
13944                 continue;
13945             }
13946             
13947             // style cleanup!?
13948             // class cleanup?
13949             
13950         }
13951         
13952         
13953         this.cleanUpChildren(node);
13954         
13955         
13956     }
13957     
13958     
13959     // hide stuff that is not compatible
13960     /**
13961      * @event blur
13962      * @hide
13963      */
13964     /**
13965      * @event change
13966      * @hide
13967      */
13968     /**
13969      * @event focus
13970      * @hide
13971      */
13972     /**
13973      * @event specialkey
13974      * @hide
13975      */
13976     /**
13977      * @cfg {String} fieldClass @hide
13978      */
13979     /**
13980      * @cfg {String} focusClass @hide
13981      */
13982     /**
13983      * @cfg {String} autoCreate @hide
13984      */
13985     /**
13986      * @cfg {String} inputType @hide
13987      */
13988     /**
13989      * @cfg {String} invalidClass @hide
13990      */
13991     /**
13992      * @cfg {String} invalidText @hide
13993      */
13994     /**
13995      * @cfg {String} msgFx @hide
13996      */
13997     /**
13998      * @cfg {String} validateOnBlur @hide
13999      */
14000 });
14001
14002 Roo.HtmlEditorCore.white = [
14003         'area', 'br', 'img', 'input', 'hr', 'wbr',
14004         
14005        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14006        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14007        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14008        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14009        'table',   'ul',         'xmp', 
14010        
14011        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14012       'thead',   'tr', 
14013      
14014       'dir', 'menu', 'ol', 'ul', 'dl',
14015        
14016       'embed',  'object'
14017 ];
14018
14019
14020 Roo.HtmlEditorCore.black = [
14021     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14022         'applet', // 
14023         'base',   'basefont', 'bgsound', 'blink',  'body', 
14024         'frame',  'frameset', 'head',    'html',   'ilayer', 
14025         'iframe', 'layer',  'link',     'meta',    'object',   
14026         'script', 'style' ,'title',  'xml' // clean later..
14027 ];
14028 Roo.HtmlEditorCore.clean = [
14029     'script', 'style', 'title', 'xml'
14030 ];
14031 Roo.HtmlEditorCore.remove = [
14032     'font'
14033 ];
14034 // attributes..
14035
14036 Roo.HtmlEditorCore.ablack = [
14037     'on'
14038 ];
14039     
14040 Roo.HtmlEditorCore.aclean = [ 
14041     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14042 ];
14043
14044 // protocols..
14045 Roo.HtmlEditorCore.pwhite= [
14046         'http',  'https',  'mailto'
14047 ];
14048
14049 // white listed style attributes.
14050 Roo.HtmlEditorCore.cwhite= [
14051       //  'text-align', /// default is to allow most things..
14052       
14053          
14054 //        'font-size'//??
14055 ];
14056
14057 // black listed style attributes.
14058 Roo.HtmlEditorCore.cblack= [
14059       //  'font-size' -- this can be set by the project 
14060 ];
14061
14062
14063 Roo.HtmlEditorCore.swapCodes   =[ 
14064     [    8211, "--" ], 
14065     [    8212, "--" ], 
14066     [    8216,  "'" ],  
14067     [    8217, "'" ],  
14068     [    8220, '"' ],  
14069     [    8221, '"' ],  
14070     [    8226, "*" ],  
14071     [    8230, "..." ]
14072 ]; 
14073
14074     /*
14075  * - LGPL
14076  *
14077  * HtmlEditor
14078  * 
14079  */
14080
14081 /**
14082  * @class Roo.bootstrap.HtmlEditor
14083  * @extends Roo.bootstrap.TextArea
14084  * Bootstrap HtmlEditor class
14085
14086  * @constructor
14087  * Create a new HtmlEditor
14088  * @param {Object} config The config object
14089  */
14090
14091 Roo.bootstrap.HtmlEditor = function(config){
14092     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14093     if (!this.toolbars) {
14094         this.toolbars = [];
14095     }
14096     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14097     this.addEvents({
14098             /**
14099              * @event initialize
14100              * Fires when the editor is fully initialized (including the iframe)
14101              * @param {HtmlEditor} this
14102              */
14103             initialize: true,
14104             /**
14105              * @event activate
14106              * Fires when the editor is first receives the focus. Any insertion must wait
14107              * until after this event.
14108              * @param {HtmlEditor} this
14109              */
14110             activate: true,
14111              /**
14112              * @event beforesync
14113              * Fires before the textarea is updated with content from the editor iframe. Return false
14114              * to cancel the sync.
14115              * @param {HtmlEditor} this
14116              * @param {String} html
14117              */
14118             beforesync: true,
14119              /**
14120              * @event beforepush
14121              * Fires before the iframe editor is updated with content from the textarea. Return false
14122              * to cancel the push.
14123              * @param {HtmlEditor} this
14124              * @param {String} html
14125              */
14126             beforepush: true,
14127              /**
14128              * @event sync
14129              * Fires when the textarea is updated with content from the editor iframe.
14130              * @param {HtmlEditor} this
14131              * @param {String} html
14132              */
14133             sync: true,
14134              /**
14135              * @event push
14136              * Fires when the iframe editor is updated with content from the textarea.
14137              * @param {HtmlEditor} this
14138              * @param {String} html
14139              */
14140             push: true,
14141              /**
14142              * @event editmodechange
14143              * Fires when the editor switches edit modes
14144              * @param {HtmlEditor} this
14145              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14146              */
14147             editmodechange: true,
14148             /**
14149              * @event editorevent
14150              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14151              * @param {HtmlEditor} this
14152              */
14153             editorevent: true,
14154             /**
14155              * @event firstfocus
14156              * Fires when on first focus - needed by toolbars..
14157              * @param {HtmlEditor} this
14158              */
14159             firstfocus: true,
14160             /**
14161              * @event autosave
14162              * Auto save the htmlEditor value as a file into Events
14163              * @param {HtmlEditor} this
14164              */
14165             autosave: true,
14166             /**
14167              * @event savedpreview
14168              * preview the saved version of htmlEditor
14169              * @param {HtmlEditor} this
14170              */
14171             savedpreview: true
14172         });
14173 };
14174
14175
14176 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14177     
14178     
14179       /**
14180      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14181      */
14182     toolbars : false,
14183    
14184      /**
14185      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14186      *                        Roo.resizable.
14187      */
14188     resizable : false,
14189      /**
14190      * @cfg {Number} height (in pixels)
14191      */   
14192     height: 300,
14193    /**
14194      * @cfg {Number} width (in pixels)
14195      */   
14196     width: false,
14197     
14198     /**
14199      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14200      * 
14201      */
14202     stylesheets: false,
14203     
14204     // id of frame..
14205     frameId: false,
14206     
14207     // private properties
14208     validationEvent : false,
14209     deferHeight: true,
14210     initialized : false,
14211     activated : false,
14212     
14213     onFocus : Roo.emptyFn,
14214     iframePad:3,
14215     hideMode:'offsets',
14216     
14217     
14218     tbContainer : false,
14219     
14220     toolbarContainer :function() {
14221         return this.wrap.select('.x-html-editor-tb',true).first();
14222     },
14223
14224     /**
14225      * Protected method that will not generally be called directly. It
14226      * is called when the editor creates its toolbar. Override this method if you need to
14227      * add custom toolbar buttons.
14228      * @param {HtmlEditor} editor
14229      */
14230     createToolbar : function(){
14231         
14232         Roo.log("create toolbars");
14233         
14234         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14235         this.toolbars[0].render(this.toolbarContainer());
14236         
14237         return;
14238         
14239 //        if (!editor.toolbars || !editor.toolbars.length) {
14240 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14241 //        }
14242 //        
14243 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14244 //            editor.toolbars[i] = Roo.factory(
14245 //                    typeof(editor.toolbars[i]) == 'string' ?
14246 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14247 //                Roo.bootstrap.HtmlEditor);
14248 //            editor.toolbars[i].init(editor);
14249 //        }
14250     },
14251
14252      
14253     // private
14254     onRender : function(ct, position)
14255     {
14256        // Roo.log("Call onRender: " + this.xtype);
14257         var _t = this;
14258         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14259       
14260         this.wrap = this.inputEl().wrap({
14261             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14262         });
14263         
14264         this.editorcore.onRender(ct, position);
14265          
14266         if (this.resizable) {
14267             this.resizeEl = new Roo.Resizable(this.wrap, {
14268                 pinned : true,
14269                 wrap: true,
14270                 dynamic : true,
14271                 minHeight : this.height,
14272                 height: this.height,
14273                 handles : this.resizable,
14274                 width: this.width,
14275                 listeners : {
14276                     resize : function(r, w, h) {
14277                         _t.onResize(w,h); // -something
14278                     }
14279                 }
14280             });
14281             
14282         }
14283         this.createToolbar(this);
14284        
14285         
14286         if(!this.width && this.resizable){
14287             this.setSize(this.wrap.getSize());
14288         }
14289         if (this.resizeEl) {
14290             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14291             // should trigger onReize..
14292         }
14293         
14294     },
14295
14296     // private
14297     onResize : function(w, h)
14298     {
14299         Roo.log('resize: ' +w + ',' + h );
14300         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14301         var ew = false;
14302         var eh = false;
14303         
14304         if(this.inputEl() ){
14305             if(typeof w == 'number'){
14306                 var aw = w - this.wrap.getFrameWidth('lr');
14307                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14308                 ew = aw;
14309             }
14310             if(typeof h == 'number'){
14311                  var tbh = -11;  // fixme it needs to tool bar size!
14312                 for (var i =0; i < this.toolbars.length;i++) {
14313                     // fixme - ask toolbars for heights?
14314                     tbh += this.toolbars[i].el.getHeight();
14315                     //if (this.toolbars[i].footer) {
14316                     //    tbh += this.toolbars[i].footer.el.getHeight();
14317                     //}
14318                 }
14319               
14320                 
14321                 
14322                 
14323                 
14324                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14325                 ah -= 5; // knock a few pixes off for look..
14326                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14327                 var eh = ah;
14328             }
14329         }
14330         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14331         this.editorcore.onResize(ew,eh);
14332         
14333     },
14334
14335     /**
14336      * Toggles the editor between standard and source edit mode.
14337      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14338      */
14339     toggleSourceEdit : function(sourceEditMode)
14340     {
14341         this.editorcore.toggleSourceEdit(sourceEditMode);
14342         
14343         if(this.editorcore.sourceEditMode){
14344             Roo.log('editor - showing textarea');
14345             
14346 //            Roo.log('in');
14347 //            Roo.log(this.syncValue());
14348             this.syncValue();
14349             this.inputEl().removeClass('hide');
14350             this.inputEl().dom.removeAttribute('tabIndex');
14351             this.inputEl().focus();
14352         }else{
14353             Roo.log('editor - hiding textarea');
14354 //            Roo.log('out')
14355 //            Roo.log(this.pushValue()); 
14356             this.pushValue();
14357             
14358             this.inputEl().addClass('hide');
14359             this.inputEl().dom.setAttribute('tabIndex', -1);
14360             //this.deferFocus();
14361         }
14362          
14363         if(this.resizable){
14364             this.setSize(this.wrap.getSize());
14365         }
14366         
14367         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14368     },
14369  
14370     // private (for BoxComponent)
14371     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14372
14373     // private (for BoxComponent)
14374     getResizeEl : function(){
14375         return this.wrap;
14376     },
14377
14378     // private (for BoxComponent)
14379     getPositionEl : function(){
14380         return this.wrap;
14381     },
14382
14383     // private
14384     initEvents : function(){
14385         this.originalValue = this.getValue();
14386     },
14387
14388 //    /**
14389 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14390 //     * @method
14391 //     */
14392 //    markInvalid : Roo.emptyFn,
14393 //    /**
14394 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14395 //     * @method
14396 //     */
14397 //    clearInvalid : Roo.emptyFn,
14398
14399     setValue : function(v){
14400         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14401         this.editorcore.pushValue();
14402     },
14403
14404      
14405     // private
14406     deferFocus : function(){
14407         this.focus.defer(10, this);
14408     },
14409
14410     // doc'ed in Field
14411     focus : function(){
14412         this.editorcore.focus();
14413         
14414     },
14415       
14416
14417     // private
14418     onDestroy : function(){
14419         
14420         
14421         
14422         if(this.rendered){
14423             
14424             for (var i =0; i < this.toolbars.length;i++) {
14425                 // fixme - ask toolbars for heights?
14426                 this.toolbars[i].onDestroy();
14427             }
14428             
14429             this.wrap.dom.innerHTML = '';
14430             this.wrap.remove();
14431         }
14432     },
14433
14434     // private
14435     onFirstFocus : function(){
14436         //Roo.log("onFirstFocus");
14437         this.editorcore.onFirstFocus();
14438          for (var i =0; i < this.toolbars.length;i++) {
14439             this.toolbars[i].onFirstFocus();
14440         }
14441         
14442     },
14443     
14444     // private
14445     syncValue : function()
14446     {   
14447         this.editorcore.syncValue();
14448     },
14449     
14450     pushValue : function()
14451     {   
14452         this.editorcore.pushValue();
14453     }
14454      
14455     
14456     // hide stuff that is not compatible
14457     /**
14458      * @event blur
14459      * @hide
14460      */
14461     /**
14462      * @event change
14463      * @hide
14464      */
14465     /**
14466      * @event focus
14467      * @hide
14468      */
14469     /**
14470      * @event specialkey
14471      * @hide
14472      */
14473     /**
14474      * @cfg {String} fieldClass @hide
14475      */
14476     /**
14477      * @cfg {String} focusClass @hide
14478      */
14479     /**
14480      * @cfg {String} autoCreate @hide
14481      */
14482     /**
14483      * @cfg {String} inputType @hide
14484      */
14485     /**
14486      * @cfg {String} invalidClass @hide
14487      */
14488     /**
14489      * @cfg {String} invalidText @hide
14490      */
14491     /**
14492      * @cfg {String} msgFx @hide
14493      */
14494     /**
14495      * @cfg {String} validateOnBlur @hide
14496      */
14497 });
14498  
14499     
14500    
14501    
14502    
14503       
14504
14505 /**
14506  * @class Roo.bootstrap.HtmlEditorToolbar1
14507  * Basic Toolbar
14508  * 
14509  * Usage:
14510  *
14511  new Roo.bootstrap.HtmlEditor({
14512     ....
14513     toolbars : [
14514         new Roo.bootstrap.HtmlEditorToolbar1({
14515             disable : { fonts: 1 , format: 1, ..., ... , ...],
14516             btns : [ .... ]
14517         })
14518     }
14519      
14520  * 
14521  * @cfg {Object} disable List of elements to disable..
14522  * @cfg {Array} btns List of additional buttons.
14523  * 
14524  * 
14525  * NEEDS Extra CSS? 
14526  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14527  */
14528  
14529 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14530 {
14531     
14532     Roo.apply(this, config);
14533     
14534     // default disabled, based on 'good practice'..
14535     this.disable = this.disable || {};
14536     Roo.applyIf(this.disable, {
14537         fontSize : true,
14538         colors : true,
14539         specialElements : true
14540     });
14541     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14542     
14543     this.editor = config.editor;
14544     this.editorcore = config.editor.editorcore;
14545     
14546     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14547     
14548     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14549     // dont call parent... till later.
14550 }
14551 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14552     
14553     
14554     bar : true,
14555     
14556     editor : false,
14557     editorcore : false,
14558     
14559     
14560     formats : [
14561         "p" ,  
14562         "h1","h2","h3","h4","h5","h6", 
14563         "pre", "code", 
14564         "abbr", "acronym", "address", "cite", "samp", "var",
14565         'div','span'
14566     ],
14567     
14568     onRender : function(ct, position)
14569     {
14570        // Roo.log("Call onRender: " + this.xtype);
14571         
14572        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14573        Roo.log(this.el);
14574        this.el.dom.style.marginBottom = '0';
14575        var _this = this;
14576        var editorcore = this.editorcore;
14577        var editor= this.editor;
14578        
14579        var children = [];
14580        var btn = function(id,cmd , toggle, handler){
14581        
14582             var  event = toggle ? 'toggle' : 'click';
14583        
14584             var a = {
14585                 size : 'sm',
14586                 xtype: 'Button',
14587                 xns: Roo.bootstrap,
14588                 glyphicon : id,
14589                 cmd : id || cmd,
14590                 enableToggle:toggle !== false,
14591                 //html : 'submit'
14592                 pressed : toggle ? false : null,
14593                 listeners : {}
14594             }
14595             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14596                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14597             }
14598             children.push(a);
14599             return a;
14600        }
14601         
14602         var style = {
14603                 xtype: 'Button',
14604                 size : 'sm',
14605                 xns: Roo.bootstrap,
14606                 glyphicon : 'font',
14607                 //html : 'submit'
14608                 menu : {
14609                     xtype: 'Menu',
14610                     xns: Roo.bootstrap,
14611                     items:  []
14612                 }
14613         };
14614         Roo.each(this.formats, function(f) {
14615             style.menu.items.push({
14616                 xtype :'MenuItem',
14617                 xns: Roo.bootstrap,
14618                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14619                 tagname : f,
14620                 listeners : {
14621                     click : function()
14622                     {
14623                         editorcore.insertTag(this.tagname);
14624                         editor.focus();
14625                     }
14626                 }
14627                 
14628             });
14629         });
14630          children.push(style);   
14631             
14632             
14633         btn('bold',false,true);
14634         btn('italic',false,true);
14635         btn('align-left', 'justifyleft',true);
14636         btn('align-center', 'justifycenter',true);
14637         btn('align-right' , 'justifyright',true);
14638         btn('link', false, false, function(btn) {
14639             //Roo.log("create link?");
14640             var url = prompt(this.createLinkText, this.defaultLinkValue);
14641             if(url && url != 'http:/'+'/'){
14642                 this.editorcore.relayCmd('createlink', url);
14643             }
14644         }),
14645         btn('list','insertunorderedlist',true);
14646         btn('pencil', false,true, function(btn){
14647                 Roo.log(this);
14648                 
14649                 this.toggleSourceEdit(btn.pressed);
14650         });
14651         /*
14652         var cog = {
14653                 xtype: 'Button',
14654                 size : 'sm',
14655                 xns: Roo.bootstrap,
14656                 glyphicon : 'cog',
14657                 //html : 'submit'
14658                 menu : {
14659                     xtype: 'Menu',
14660                     xns: Roo.bootstrap,
14661                     items:  []
14662                 }
14663         };
14664         
14665         cog.menu.items.push({
14666             xtype :'MenuItem',
14667             xns: Roo.bootstrap,
14668             html : Clean styles,
14669             tagname : f,
14670             listeners : {
14671                 click : function()
14672                 {
14673                     editorcore.insertTag(this.tagname);
14674                     editor.focus();
14675                 }
14676             }
14677             
14678         });
14679        */
14680         
14681          
14682        this.xtype = 'Navbar';
14683         
14684         for(var i=0;i< children.length;i++) {
14685             
14686             this.buttons.add(this.addxtypeChild(children[i]));
14687             
14688         }
14689         
14690         editor.on('editorevent', this.updateToolbar, this);
14691     },
14692     onBtnClick : function(id)
14693     {
14694        this.editorcore.relayCmd(id);
14695        this.editorcore.focus();
14696     },
14697     
14698     /**
14699      * Protected method that will not generally be called directly. It triggers
14700      * a toolbar update by reading the markup state of the current selection in the editor.
14701      */
14702     updateToolbar: function(){
14703
14704         if(!this.editorcore.activated){
14705             this.editor.onFirstFocus(); // is this neeed?
14706             return;
14707         }
14708
14709         var btns = this.buttons; 
14710         var doc = this.editorcore.doc;
14711         btns.get('bold').setActive(doc.queryCommandState('bold'));
14712         btns.get('italic').setActive(doc.queryCommandState('italic'));
14713         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14714         
14715         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14716         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14717         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14718         
14719         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14720         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14721          /*
14722         
14723         var ans = this.editorcore.getAllAncestors();
14724         if (this.formatCombo) {
14725             
14726             
14727             var store = this.formatCombo.store;
14728             this.formatCombo.setValue("");
14729             for (var i =0; i < ans.length;i++) {
14730                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14731                     // select it..
14732                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14733                     break;
14734                 }
14735             }
14736         }
14737         
14738         
14739         
14740         // hides menus... - so this cant be on a menu...
14741         Roo.bootstrap.MenuMgr.hideAll();
14742         */
14743         Roo.bootstrap.MenuMgr.hideAll();
14744         //this.editorsyncValue();
14745     },
14746     onFirstFocus: function() {
14747         this.buttons.each(function(item){
14748            item.enable();
14749         });
14750     },
14751     toggleSourceEdit : function(sourceEditMode){
14752         
14753           
14754         if(sourceEditMode){
14755             Roo.log("disabling buttons");
14756            this.buttons.each( function(item){
14757                 if(item.cmd != 'pencil'){
14758                     item.disable();
14759                 }
14760             });
14761           
14762         }else{
14763             Roo.log("enabling buttons");
14764             if(this.editorcore.initialized){
14765                 this.buttons.each( function(item){
14766                     item.enable();
14767                 });
14768             }
14769             
14770         }
14771         Roo.log("calling toggole on editor");
14772         // tell the editor that it's been pressed..
14773         this.editor.toggleSourceEdit(sourceEditMode);
14774        
14775     }
14776 });
14777
14778
14779
14780
14781
14782 /**
14783  * @class Roo.bootstrap.Table.AbstractSelectionModel
14784  * @extends Roo.util.Observable
14785  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14786  * implemented by descendant classes.  This class should not be directly instantiated.
14787  * @constructor
14788  */
14789 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14790     this.locked = false;
14791     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14792 };
14793
14794
14795 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14796     /** @ignore Called by the grid automatically. Do not call directly. */
14797     init : function(grid){
14798         this.grid = grid;
14799         this.initEvents();
14800     },
14801
14802     /**
14803      * Locks the selections.
14804      */
14805     lock : function(){
14806         this.locked = true;
14807     },
14808
14809     /**
14810      * Unlocks the selections.
14811      */
14812     unlock : function(){
14813         this.locked = false;
14814     },
14815
14816     /**
14817      * Returns true if the selections are locked.
14818      * @return {Boolean}
14819      */
14820     isLocked : function(){
14821         return this.locked;
14822     }
14823 });
14824 /**
14825  * @class Roo.bootstrap.Table.ColumnModel
14826  * @extends Roo.util.Observable
14827  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14828  * the columns in the table.
14829  
14830  * @constructor
14831  * @param {Object} config An Array of column config objects. See this class's
14832  * config objects for details.
14833 */
14834 Roo.bootstrap.Table.ColumnModel = function(config){
14835         /**
14836      * The config passed into the constructor
14837      */
14838     this.config = config;
14839     this.lookup = {};
14840
14841     // if no id, create one
14842     // if the column does not have a dataIndex mapping,
14843     // map it to the order it is in the config
14844     for(var i = 0, len = config.length; i < len; i++){
14845         var c = config[i];
14846         if(typeof c.dataIndex == "undefined"){
14847             c.dataIndex = i;
14848         }
14849         if(typeof c.renderer == "string"){
14850             c.renderer = Roo.util.Format[c.renderer];
14851         }
14852         if(typeof c.id == "undefined"){
14853             c.id = Roo.id();
14854         }
14855 //        if(c.editor && c.editor.xtype){
14856 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14857 //        }
14858 //        if(c.editor && c.editor.isFormField){
14859 //            c.editor = new Roo.grid.GridEditor(c.editor);
14860 //        }
14861
14862         this.lookup[c.id] = c;
14863     }
14864
14865     /**
14866      * The width of columns which have no width specified (defaults to 100)
14867      * @type Number
14868      */
14869     this.defaultWidth = 100;
14870
14871     /**
14872      * Default sortable of columns which have no sortable specified (defaults to false)
14873      * @type Boolean
14874      */
14875     this.defaultSortable = false;
14876
14877     this.addEvents({
14878         /**
14879              * @event widthchange
14880              * Fires when the width of a column changes.
14881              * @param {ColumnModel} this
14882              * @param {Number} columnIndex The column index
14883              * @param {Number} newWidth The new width
14884              */
14885             "widthchange": true,
14886         /**
14887              * @event headerchange
14888              * Fires when the text of a header changes.
14889              * @param {ColumnModel} this
14890              * @param {Number} columnIndex The column index
14891              * @param {Number} newText The new header text
14892              */
14893             "headerchange": true,
14894         /**
14895              * @event hiddenchange
14896              * Fires when a column is hidden or "unhidden".
14897              * @param {ColumnModel} this
14898              * @param {Number} columnIndex The column index
14899              * @param {Boolean} hidden true if hidden, false otherwise
14900              */
14901             "hiddenchange": true,
14902             /**
14903          * @event columnmoved
14904          * Fires when a column is moved.
14905          * @param {ColumnModel} this
14906          * @param {Number} oldIndex
14907          * @param {Number} newIndex
14908          */
14909         "columnmoved" : true,
14910         /**
14911          * @event columlockchange
14912          * Fires when a column's locked state is changed
14913          * @param {ColumnModel} this
14914          * @param {Number} colIndex
14915          * @param {Boolean} locked true if locked
14916          */
14917         "columnlockchange" : true
14918     });
14919     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14920 };
14921 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14922     /**
14923      * @cfg {String} header The header text to display in the Grid view.
14924      */
14925     /**
14926      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14927      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14928      * specified, the column's index is used as an index into the Record's data Array.
14929      */
14930     /**
14931      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14932      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14933      */
14934     /**
14935      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14936      * Defaults to the value of the {@link #defaultSortable} property.
14937      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14938      */
14939     /**
14940      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14941      */
14942     /**
14943      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14944      */
14945     /**
14946      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14947      */
14948     /**
14949      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14950      */
14951     /**
14952      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14953      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14954      * default renderer uses the raw data value.
14955      */
14956     /**
14957      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14958      */
14959
14960     /**
14961      * Returns the id of the column at the specified index.
14962      * @param {Number} index The column index
14963      * @return {String} the id
14964      */
14965     getColumnId : function(index){
14966         return this.config[index].id;
14967     },
14968
14969     /**
14970      * Returns the column for a specified id.
14971      * @param {String} id The column id
14972      * @return {Object} the column
14973      */
14974     getColumnById : function(id){
14975         return this.lookup[id];
14976     },
14977
14978     
14979     /**
14980      * Returns the column for a specified dataIndex.
14981      * @param {String} dataIndex The column dataIndex
14982      * @return {Object|Boolean} the column or false if not found
14983      */
14984     getColumnByDataIndex: function(dataIndex){
14985         var index = this.findColumnIndex(dataIndex);
14986         return index > -1 ? this.config[index] : false;
14987     },
14988     
14989     /**
14990      * Returns the index for a specified column id.
14991      * @param {String} id The column id
14992      * @return {Number} the index, or -1 if not found
14993      */
14994     getIndexById : function(id){
14995         for(var i = 0, len = this.config.length; i < len; i++){
14996             if(this.config[i].id == id){
14997                 return i;
14998             }
14999         }
15000         return -1;
15001     },
15002     
15003     /**
15004      * Returns the index for a specified column dataIndex.
15005      * @param {String} dataIndex The column dataIndex
15006      * @return {Number} the index, or -1 if not found
15007      */
15008     
15009     findColumnIndex : function(dataIndex){
15010         for(var i = 0, len = this.config.length; i < len; i++){
15011             if(this.config[i].dataIndex == dataIndex){
15012                 return i;
15013             }
15014         }
15015         return -1;
15016     },
15017     
15018     
15019     moveColumn : function(oldIndex, newIndex){
15020         var c = this.config[oldIndex];
15021         this.config.splice(oldIndex, 1);
15022         this.config.splice(newIndex, 0, c);
15023         this.dataMap = null;
15024         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15025     },
15026
15027     isLocked : function(colIndex){
15028         return this.config[colIndex].locked === true;
15029     },
15030
15031     setLocked : function(colIndex, value, suppressEvent){
15032         if(this.isLocked(colIndex) == value){
15033             return;
15034         }
15035         this.config[colIndex].locked = value;
15036         if(!suppressEvent){
15037             this.fireEvent("columnlockchange", this, colIndex, value);
15038         }
15039     },
15040
15041     getTotalLockedWidth : function(){
15042         var totalWidth = 0;
15043         for(var i = 0; i < this.config.length; i++){
15044             if(this.isLocked(i) && !this.isHidden(i)){
15045                 this.totalWidth += this.getColumnWidth(i);
15046             }
15047         }
15048         return totalWidth;
15049     },
15050
15051     getLockedCount : function(){
15052         for(var i = 0, len = this.config.length; i < len; i++){
15053             if(!this.isLocked(i)){
15054                 return i;
15055             }
15056         }
15057     },
15058
15059     /**
15060      * Returns the number of columns.
15061      * @return {Number}
15062      */
15063     getColumnCount : function(visibleOnly){
15064         if(visibleOnly === true){
15065             var c = 0;
15066             for(var i = 0, len = this.config.length; i < len; i++){
15067                 if(!this.isHidden(i)){
15068                     c++;
15069                 }
15070             }
15071             return c;
15072         }
15073         return this.config.length;
15074     },
15075
15076     /**
15077      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15078      * @param {Function} fn
15079      * @param {Object} scope (optional)
15080      * @return {Array} result
15081      */
15082     getColumnsBy : function(fn, scope){
15083         var r = [];
15084         for(var i = 0, len = this.config.length; i < len; i++){
15085             var c = this.config[i];
15086             if(fn.call(scope||this, c, i) === true){
15087                 r[r.length] = c;
15088             }
15089         }
15090         return r;
15091     },
15092
15093     /**
15094      * Returns true if the specified column is sortable.
15095      * @param {Number} col The column index
15096      * @return {Boolean}
15097      */
15098     isSortable : function(col){
15099         if(typeof this.config[col].sortable == "undefined"){
15100             return this.defaultSortable;
15101         }
15102         return this.config[col].sortable;
15103     },
15104
15105     /**
15106      * Returns the rendering (formatting) function defined for the column.
15107      * @param {Number} col The column index.
15108      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15109      */
15110     getRenderer : function(col){
15111         if(!this.config[col].renderer){
15112             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15113         }
15114         return this.config[col].renderer;
15115     },
15116
15117     /**
15118      * Sets the rendering (formatting) function for a column.
15119      * @param {Number} col The column index
15120      * @param {Function} fn The function to use to process the cell's raw data
15121      * to return HTML markup for the grid view. The render function is called with
15122      * the following parameters:<ul>
15123      * <li>Data value.</li>
15124      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15125      * <li>css A CSS style string to apply to the table cell.</li>
15126      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15127      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15128      * <li>Row index</li>
15129      * <li>Column index</li>
15130      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15131      */
15132     setRenderer : function(col, fn){
15133         this.config[col].renderer = fn;
15134     },
15135
15136     /**
15137      * Returns the width for the specified column.
15138      * @param {Number} col The column index
15139      * @return {Number}
15140      */
15141     getColumnWidth : function(col){
15142         return this.config[col].width * 1 || this.defaultWidth;
15143     },
15144
15145     /**
15146      * Sets the width for a column.
15147      * @param {Number} col The column index
15148      * @param {Number} width The new width
15149      */
15150     setColumnWidth : function(col, width, suppressEvent){
15151         this.config[col].width = width;
15152         this.totalWidth = null;
15153         if(!suppressEvent){
15154              this.fireEvent("widthchange", this, col, width);
15155         }
15156     },
15157
15158     /**
15159      * Returns the total width of all columns.
15160      * @param {Boolean} includeHidden True to include hidden column widths
15161      * @return {Number}
15162      */
15163     getTotalWidth : function(includeHidden){
15164         if(!this.totalWidth){
15165             this.totalWidth = 0;
15166             for(var i = 0, len = this.config.length; i < len; i++){
15167                 if(includeHidden || !this.isHidden(i)){
15168                     this.totalWidth += this.getColumnWidth(i);
15169                 }
15170             }
15171         }
15172         return this.totalWidth;
15173     },
15174
15175     /**
15176      * Returns the header for the specified column.
15177      * @param {Number} col The column index
15178      * @return {String}
15179      */
15180     getColumnHeader : function(col){
15181         return this.config[col].header;
15182     },
15183
15184     /**
15185      * Sets the header for a column.
15186      * @param {Number} col The column index
15187      * @param {String} header The new header
15188      */
15189     setColumnHeader : function(col, header){
15190         this.config[col].header = header;
15191         this.fireEvent("headerchange", this, col, header);
15192     },
15193
15194     /**
15195      * Returns the tooltip for the specified column.
15196      * @param {Number} col The column index
15197      * @return {String}
15198      */
15199     getColumnTooltip : function(col){
15200             return this.config[col].tooltip;
15201     },
15202     /**
15203      * Sets the tooltip for a column.
15204      * @param {Number} col The column index
15205      * @param {String} tooltip The new tooltip
15206      */
15207     setColumnTooltip : function(col, tooltip){
15208             this.config[col].tooltip = tooltip;
15209     },
15210
15211     /**
15212      * Returns the dataIndex for the specified column.
15213      * @param {Number} col The column index
15214      * @return {Number}
15215      */
15216     getDataIndex : function(col){
15217         return this.config[col].dataIndex;
15218     },
15219
15220     /**
15221      * Sets the dataIndex for a column.
15222      * @param {Number} col The column index
15223      * @param {Number} dataIndex The new dataIndex
15224      */
15225     setDataIndex : function(col, dataIndex){
15226         this.config[col].dataIndex = dataIndex;
15227     },
15228
15229     
15230     
15231     /**
15232      * Returns true if the cell is editable.
15233      * @param {Number} colIndex The column index
15234      * @param {Number} rowIndex The row index
15235      * @return {Boolean}
15236      */
15237     isCellEditable : function(colIndex, rowIndex){
15238         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15239     },
15240
15241     /**
15242      * Returns the editor defined for the cell/column.
15243      * return false or null to disable editing.
15244      * @param {Number} colIndex The column index
15245      * @param {Number} rowIndex The row index
15246      * @return {Object}
15247      */
15248     getCellEditor : function(colIndex, rowIndex){
15249         return this.config[colIndex].editor;
15250     },
15251
15252     /**
15253      * Sets if a column is editable.
15254      * @param {Number} col The column index
15255      * @param {Boolean} editable True if the column is editable
15256      */
15257     setEditable : function(col, editable){
15258         this.config[col].editable = editable;
15259     },
15260
15261
15262     /**
15263      * Returns true if the column is hidden.
15264      * @param {Number} colIndex The column index
15265      * @return {Boolean}
15266      */
15267     isHidden : function(colIndex){
15268         return this.config[colIndex].hidden;
15269     },
15270
15271
15272     /**
15273      * Returns true if the column width cannot be changed
15274      */
15275     isFixed : function(colIndex){
15276         return this.config[colIndex].fixed;
15277     },
15278
15279     /**
15280      * Returns true if the column can be resized
15281      * @return {Boolean}
15282      */
15283     isResizable : function(colIndex){
15284         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15285     },
15286     /**
15287      * Sets if a column is hidden.
15288      * @param {Number} colIndex The column index
15289      * @param {Boolean} hidden True if the column is hidden
15290      */
15291     setHidden : function(colIndex, hidden){
15292         this.config[colIndex].hidden = hidden;
15293         this.totalWidth = null;
15294         this.fireEvent("hiddenchange", this, colIndex, hidden);
15295     },
15296
15297     /**
15298      * Sets the editor for a column.
15299      * @param {Number} col The column index
15300      * @param {Object} editor The editor object
15301      */
15302     setEditor : function(col, editor){
15303         this.config[col].editor = editor;
15304     }
15305 });
15306
15307 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15308         if(typeof value == "string" && value.length < 1){
15309             return "&#160;";
15310         }
15311         return value;
15312 };
15313
15314 // Alias for backwards compatibility
15315 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15316
15317 /**
15318  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15319  * @class Roo.bootstrap.Table.RowSelectionModel
15320  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15321  * It supports multiple selections and keyboard selection/navigation. 
15322  * @constructor
15323  * @param {Object} config
15324  */
15325
15326 Roo.bootstrap.Table.RowSelectionModel = function(config){
15327     Roo.apply(this, config);
15328     this.selections = new Roo.util.MixedCollection(false, function(o){
15329         return o.id;
15330     });
15331
15332     this.last = false;
15333     this.lastActive = false;
15334
15335     this.addEvents({
15336         /**
15337              * @event selectionchange
15338              * Fires when the selection changes
15339              * @param {SelectionModel} this
15340              */
15341             "selectionchange" : true,
15342         /**
15343              * @event afterselectionchange
15344              * Fires after the selection changes (eg. by key press or clicking)
15345              * @param {SelectionModel} this
15346              */
15347             "afterselectionchange" : true,
15348         /**
15349              * @event beforerowselect
15350              * Fires when a row is selected being selected, return false to cancel.
15351              * @param {SelectionModel} this
15352              * @param {Number} rowIndex The selected index
15353              * @param {Boolean} keepExisting False if other selections will be cleared
15354              */
15355             "beforerowselect" : true,
15356         /**
15357              * @event rowselect
15358              * Fires when a row is selected.
15359              * @param {SelectionModel} this
15360              * @param {Number} rowIndex The selected index
15361              * @param {Roo.data.Record} r The record
15362              */
15363             "rowselect" : true,
15364         /**
15365              * @event rowdeselect
15366              * Fires when a row is deselected.
15367              * @param {SelectionModel} this
15368              * @param {Number} rowIndex The selected index
15369              */
15370         "rowdeselect" : true
15371     });
15372     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15373     this.locked = false;
15374 };
15375
15376 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15377     /**
15378      * @cfg {Boolean} singleSelect
15379      * True to allow selection of only one row at a time (defaults to false)
15380      */
15381     singleSelect : false,
15382
15383     // private
15384     initEvents : function(){
15385
15386         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15387             this.grid.on("mousedown", this.handleMouseDown, this);
15388         }else{ // allow click to work like normal
15389             this.grid.on("rowclick", this.handleDragableRowClick, this);
15390         }
15391
15392         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15393             "up" : function(e){
15394                 if(!e.shiftKey){
15395                     this.selectPrevious(e.shiftKey);
15396                 }else if(this.last !== false && this.lastActive !== false){
15397                     var last = this.last;
15398                     this.selectRange(this.last,  this.lastActive-1);
15399                     this.grid.getView().focusRow(this.lastActive);
15400                     if(last !== false){
15401                         this.last = last;
15402                     }
15403                 }else{
15404                     this.selectFirstRow();
15405                 }
15406                 this.fireEvent("afterselectionchange", this);
15407             },
15408             "down" : function(e){
15409                 if(!e.shiftKey){
15410                     this.selectNext(e.shiftKey);
15411                 }else if(this.last !== false && this.lastActive !== false){
15412                     var last = this.last;
15413                     this.selectRange(this.last,  this.lastActive+1);
15414                     this.grid.getView().focusRow(this.lastActive);
15415                     if(last !== false){
15416                         this.last = last;
15417                     }
15418                 }else{
15419                     this.selectFirstRow();
15420                 }
15421                 this.fireEvent("afterselectionchange", this);
15422             },
15423             scope: this
15424         });
15425
15426         var view = this.grid.view;
15427         view.on("refresh", this.onRefresh, this);
15428         view.on("rowupdated", this.onRowUpdated, this);
15429         view.on("rowremoved", this.onRemove, this);
15430     },
15431
15432     // private
15433     onRefresh : function(){
15434         var ds = this.grid.dataSource, i, v = this.grid.view;
15435         var s = this.selections;
15436         s.each(function(r){
15437             if((i = ds.indexOfId(r.id)) != -1){
15438                 v.onRowSelect(i);
15439             }else{
15440                 s.remove(r);
15441             }
15442         });
15443     },
15444
15445     // private
15446     onRemove : function(v, index, r){
15447         this.selections.remove(r);
15448     },
15449
15450     // private
15451     onRowUpdated : function(v, index, r){
15452         if(this.isSelected(r)){
15453             v.onRowSelect(index);
15454         }
15455     },
15456
15457     /**
15458      * Select records.
15459      * @param {Array} records The records to select
15460      * @param {Boolean} keepExisting (optional) True to keep existing selections
15461      */
15462     selectRecords : function(records, keepExisting){
15463         if(!keepExisting){
15464             this.clearSelections();
15465         }
15466         var ds = this.grid.dataSource;
15467         for(var i = 0, len = records.length; i < len; i++){
15468             this.selectRow(ds.indexOf(records[i]), true);
15469         }
15470     },
15471
15472     /**
15473      * Gets the number of selected rows.
15474      * @return {Number}
15475      */
15476     getCount : function(){
15477         return this.selections.length;
15478     },
15479
15480     /**
15481      * Selects the first row in the grid.
15482      */
15483     selectFirstRow : function(){
15484         this.selectRow(0);
15485     },
15486
15487     /**
15488      * Select the last row.
15489      * @param {Boolean} keepExisting (optional) True to keep existing selections
15490      */
15491     selectLastRow : function(keepExisting){
15492         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15493     },
15494
15495     /**
15496      * Selects the row immediately following the last selected row.
15497      * @param {Boolean} keepExisting (optional) True to keep existing selections
15498      */
15499     selectNext : function(keepExisting){
15500         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15501             this.selectRow(this.last+1, keepExisting);
15502             this.grid.getView().focusRow(this.last);
15503         }
15504     },
15505
15506     /**
15507      * Selects the row that precedes the last selected row.
15508      * @param {Boolean} keepExisting (optional) True to keep existing selections
15509      */
15510     selectPrevious : function(keepExisting){
15511         if(this.last){
15512             this.selectRow(this.last-1, keepExisting);
15513             this.grid.getView().focusRow(this.last);
15514         }
15515     },
15516
15517     /**
15518      * Returns the selected records
15519      * @return {Array} Array of selected records
15520      */
15521     getSelections : function(){
15522         return [].concat(this.selections.items);
15523     },
15524
15525     /**
15526      * Returns the first selected record.
15527      * @return {Record}
15528      */
15529     getSelected : function(){
15530         return this.selections.itemAt(0);
15531     },
15532
15533
15534     /**
15535      * Clears all selections.
15536      */
15537     clearSelections : function(fast){
15538         if(this.locked) return;
15539         if(fast !== true){
15540             var ds = this.grid.dataSource;
15541             var s = this.selections;
15542             s.each(function(r){
15543                 this.deselectRow(ds.indexOfId(r.id));
15544             }, this);
15545             s.clear();
15546         }else{
15547             this.selections.clear();
15548         }
15549         this.last = false;
15550     },
15551
15552
15553     /**
15554      * Selects all rows.
15555      */
15556     selectAll : function(){
15557         if(this.locked) return;
15558         this.selections.clear();
15559         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15560             this.selectRow(i, true);
15561         }
15562     },
15563
15564     /**
15565      * Returns True if there is a selection.
15566      * @return {Boolean}
15567      */
15568     hasSelection : function(){
15569         return this.selections.length > 0;
15570     },
15571
15572     /**
15573      * Returns True if the specified row is selected.
15574      * @param {Number/Record} record The record or index of the record to check
15575      * @return {Boolean}
15576      */
15577     isSelected : function(index){
15578         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15579         return (r && this.selections.key(r.id) ? true : false);
15580     },
15581
15582     /**
15583      * Returns True if the specified record id is selected.
15584      * @param {String} id The id of record to check
15585      * @return {Boolean}
15586      */
15587     isIdSelected : function(id){
15588         return (this.selections.key(id) ? true : false);
15589     },
15590
15591     // private
15592     handleMouseDown : function(e, t){
15593         var view = this.grid.getView(), rowIndex;
15594         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15595             return;
15596         };
15597         if(e.shiftKey && this.last !== false){
15598             var last = this.last;
15599             this.selectRange(last, rowIndex, e.ctrlKey);
15600             this.last = last; // reset the last
15601             view.focusRow(rowIndex);
15602         }else{
15603             var isSelected = this.isSelected(rowIndex);
15604             if(e.button !== 0 && isSelected){
15605                 view.focusRow(rowIndex);
15606             }else if(e.ctrlKey && isSelected){
15607                 this.deselectRow(rowIndex);
15608             }else if(!isSelected){
15609                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15610                 view.focusRow(rowIndex);
15611             }
15612         }
15613         this.fireEvent("afterselectionchange", this);
15614     },
15615     // private
15616     handleDragableRowClick :  function(grid, rowIndex, e) 
15617     {
15618         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15619             this.selectRow(rowIndex, false);
15620             grid.view.focusRow(rowIndex);
15621              this.fireEvent("afterselectionchange", this);
15622         }
15623     },
15624     
15625     /**
15626      * Selects multiple rows.
15627      * @param {Array} rows Array of the indexes of the row to select
15628      * @param {Boolean} keepExisting (optional) True to keep existing selections
15629      */
15630     selectRows : function(rows, keepExisting){
15631         if(!keepExisting){
15632             this.clearSelections();
15633         }
15634         for(var i = 0, len = rows.length; i < len; i++){
15635             this.selectRow(rows[i], true);
15636         }
15637     },
15638
15639     /**
15640      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15641      * @param {Number} startRow The index of the first row in the range
15642      * @param {Number} endRow The index of the last row in the range
15643      * @param {Boolean} keepExisting (optional) True to retain existing selections
15644      */
15645     selectRange : function(startRow, endRow, keepExisting){
15646         if(this.locked) return;
15647         if(!keepExisting){
15648             this.clearSelections();
15649         }
15650         if(startRow <= endRow){
15651             for(var i = startRow; i <= endRow; i++){
15652                 this.selectRow(i, true);
15653             }
15654         }else{
15655             for(var i = startRow; i >= endRow; i--){
15656                 this.selectRow(i, true);
15657             }
15658         }
15659     },
15660
15661     /**
15662      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15663      * @param {Number} startRow The index of the first row in the range
15664      * @param {Number} endRow The index of the last row in the range
15665      */
15666     deselectRange : function(startRow, endRow, preventViewNotify){
15667         if(this.locked) return;
15668         for(var i = startRow; i <= endRow; i++){
15669             this.deselectRow(i, preventViewNotify);
15670         }
15671     },
15672
15673     /**
15674      * Selects a row.
15675      * @param {Number} row The index of the row to select
15676      * @param {Boolean} keepExisting (optional) True to keep existing selections
15677      */
15678     selectRow : function(index, keepExisting, preventViewNotify){
15679         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15680         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15681             if(!keepExisting || this.singleSelect){
15682                 this.clearSelections();
15683             }
15684             var r = this.grid.dataSource.getAt(index);
15685             this.selections.add(r);
15686             this.last = this.lastActive = index;
15687             if(!preventViewNotify){
15688                 this.grid.getView().onRowSelect(index);
15689             }
15690             this.fireEvent("rowselect", this, index, r);
15691             this.fireEvent("selectionchange", this);
15692         }
15693     },
15694
15695     /**
15696      * Deselects a row.
15697      * @param {Number} row The index of the row to deselect
15698      */
15699     deselectRow : function(index, preventViewNotify){
15700         if(this.locked) return;
15701         if(this.last == index){
15702             this.last = false;
15703         }
15704         if(this.lastActive == index){
15705             this.lastActive = false;
15706         }
15707         var r = this.grid.dataSource.getAt(index);
15708         this.selections.remove(r);
15709         if(!preventViewNotify){
15710             this.grid.getView().onRowDeselect(index);
15711         }
15712         this.fireEvent("rowdeselect", this, index);
15713         this.fireEvent("selectionchange", this);
15714     },
15715
15716     // private
15717     restoreLast : function(){
15718         if(this._last){
15719             this.last = this._last;
15720         }
15721     },
15722
15723     // private
15724     acceptsNav : function(row, col, cm){
15725         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15726     },
15727
15728     // private
15729     onEditorKey : function(field, e){
15730         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15731         if(k == e.TAB){
15732             e.stopEvent();
15733             ed.completeEdit();
15734             if(e.shiftKey){
15735                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15736             }else{
15737                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15738             }
15739         }else if(k == e.ENTER && !e.ctrlKey){
15740             e.stopEvent();
15741             ed.completeEdit();
15742             if(e.shiftKey){
15743                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15744             }else{
15745                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15746             }
15747         }else if(k == e.ESC){
15748             ed.cancelEdit();
15749         }
15750         if(newCell){
15751             g.startEditing(newCell[0], newCell[1]);
15752         }
15753     }
15754 });/*
15755  * - LGPL
15756  *
15757  * element
15758  * 
15759  */
15760
15761 /**
15762  * @class Roo.bootstrap.MessageBar
15763  * @extends Roo.bootstrap.Component
15764  * Bootstrap MessageBar class
15765  * @cfg {String} html contents of the MessageBar
15766  * @cfg {String} weight (info | success | warning | danger) default info
15767  * @cfg {String} beforeClass insert the bar before the given class
15768  * @cfg {Boolean} closable (true | false) default false
15769  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15770  * 
15771  * @constructor
15772  * Create a new Element
15773  * @param {Object} config The config object
15774  */
15775
15776 Roo.bootstrap.MessageBar = function(config){
15777     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15778 };
15779
15780 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15781     
15782     html: '',
15783     weight: 'info',
15784     closable: false,
15785     fixed: false,
15786     beforeClass: 'bootstrap-sticky-wrap',
15787     
15788     getAutoCreate : function(){
15789         
15790         var cfg = {
15791             tag: 'div',
15792             cls: 'alert alert-dismissable alert-' + this.weight,
15793             cn: [
15794                 {
15795                     tag: 'span',
15796                     cls: 'message',
15797                     html: this.html || ''
15798                 }
15799             ]
15800         }
15801         
15802         if(this.fixed){
15803             cfg.cls += ' alert-messages-fixed';
15804         }
15805         
15806         if(this.closable){
15807             cfg.cn.push({
15808                 tag: 'button',
15809                 cls: 'close',
15810                 html: 'x'
15811             });
15812         }
15813         
15814         return cfg;
15815     },
15816     
15817     onRender : function(ct, position)
15818     {
15819         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15820         
15821         if(!this.el){
15822             var cfg = Roo.apply({},  this.getAutoCreate());
15823             cfg.id = Roo.id();
15824             
15825             if (this.cls) {
15826                 cfg.cls += ' ' + this.cls;
15827             }
15828             if (this.style) {
15829                 cfg.style = this.style;
15830             }
15831             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15832             
15833             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15834         }
15835         
15836         this.el.select('>button.close').on('click', this.hide, this);
15837         
15838     },
15839     
15840     show : function()
15841     {
15842         if (!this.rendered) {
15843             this.render();
15844         }
15845         
15846         this.el.show();
15847         
15848         this.fireEvent('show', this);
15849         
15850     },
15851     
15852     hide : function()
15853     {
15854         if (!this.rendered) {
15855             this.render();
15856         }
15857         
15858         this.el.hide();
15859         
15860         this.fireEvent('hide', this);
15861     },
15862     
15863     update : function()
15864     {
15865 //        var e = this.el.dom.firstChild;
15866 //        
15867 //        if(this.closable){
15868 //            e = e.nextSibling;
15869 //        }
15870 //        
15871 //        e.data = this.html || '';
15872
15873         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15874     }
15875    
15876 });
15877
15878  
15879
15880