667023e42e40e2dcbf4431da6e02d82e1aa83c55
[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         var _this = this;
2952         Roo.log('initEvents with ds!!!!');
2953         
2954         Roo.each(this.el.select('thead th.sortable').elements, function(e){
2955             e.on('click', _this.store.load());
2956         });
2957 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2958 //        this.maskEl.enableDisplayMode("block");
2959 //        this.maskEl.show();
2960         
2961         this.store.on('load', this.onLoad, this);
2962         this.store.on('beforeload', this.onBeforeLoad, this);
2963         
2964         this.store.load();
2965         
2966         
2967         
2968     },
2969     
2970     renderHeader : function()
2971     {
2972         var header = {
2973             tag: 'thead',
2974             cn : []
2975         };
2976         
2977         var cm = this.cm;
2978         
2979         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2980             
2981             var config = cm.config[i];
2982             
2983             var c = {
2984                 tag: 'th',
2985                 html: cm.getColumnHeader(i)
2986             };
2987             
2988             if(typeof(config.dataIndex) != 'undefined'){
2989                 c.sort = config.dataIndex;
2990             }
2991             
2992             if(typeof(config.sortable) != 'undefined' && config.sortable){
2993                 c.cls = 'sortable';
2994             }
2995             
2996             header.cn.push(c)
2997         }
2998         
2999         return header;
3000     },
3001     
3002     renderBody : function()
3003     {
3004         var body = {
3005             tag: 'tbody',
3006             cn : []
3007         };
3008         
3009         return body;
3010     },
3011     
3012     renderFooter : function()
3013     {
3014         var footer = {
3015             tag: 'tfoot',
3016             cn : []
3017         };
3018         
3019         return footer;
3020     },
3021     
3022     onLoad : function()
3023     {
3024         Roo.log('ds onload');
3025         
3026         var cm = this.cm;
3027         
3028         var tbody = this.el.select('tbody', true).first();
3029         
3030         var renders = [];
3031         
3032         if(this.store.getCount() > 0){
3033             this.store.data.each(function(d){
3034                 var row = {
3035                     tag : 'tr',
3036                     cn : []
3037                 };
3038                 
3039                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3040                     var renderer = cm.getRenderer(i);
3041                     var config = cm.config[i];
3042                     var value = '';
3043                     var id = Roo.id();
3044                     
3045                     if(typeof(renderer) !== 'undefined'){
3046                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3047                     }
3048                     
3049                     if(typeof(value) === 'object'){
3050                         renders.push({
3051                             id : id,
3052                             cfg : value 
3053                         })
3054                     }
3055                     
3056                     var td = {
3057                         tag: 'td',
3058                         id: id,
3059                         html: (typeof(value) === 'object') ? '' : value
3060                     };
3061                     
3062                     if(typeof(config.width) != 'undefined'){
3063                         td.width = config.width;
3064                     }
3065                     
3066                     row.cn.push(td);
3067                    
3068                 }
3069                 
3070                 tbody.createChild(row);
3071                 
3072             });
3073         }
3074         
3075         
3076         if(renders.length){
3077             var _this = this;
3078             Roo.each(renders, function(r){
3079                 _this.renderColumn(r);
3080             })
3081         }
3082 //        
3083 //        if(this.loadMask){
3084 //            this.maskEl.hide();
3085 //        }
3086     },
3087     
3088     onBeforeLoad : function()
3089     {
3090         Roo.log('ds onBeforeLoad');
3091         
3092         this.clear();
3093         
3094 //        if(this.loadMask){
3095 //            this.maskEl.show();
3096 //        }
3097     },
3098     
3099     clear : function()
3100     {
3101         this.el.select('tbody', true).first().dom.innerHTML = '';
3102     },
3103     
3104     getSelectionModel : function(){
3105         if(!this.selModel){
3106             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3107         }
3108         return this.selModel;
3109     },
3110     
3111     renderColumn : function(r)
3112     {
3113         var _this = this;
3114         r.cfg.render(Roo.get(r.id));
3115         
3116         if(r.cfg.cn){
3117             Roo.each(r.cfg.cn, function(c){
3118                 var child = {
3119                     id: r.id,
3120                     cfg: c
3121                 }
3122                 _this.renderColumn(child);
3123             })
3124         }
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * - LGPL
3133  *
3134  * table cell
3135  * 
3136  */
3137
3138 /**
3139  * @class Roo.bootstrap.TableCell
3140  * @extends Roo.bootstrap.Component
3141  * Bootstrap TableCell class
3142  * @cfg {String} html cell contain text
3143  * @cfg {String} cls cell class
3144  * @cfg {String} tag cell tag (td|th) default td
3145  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3146  * @cfg {String} align Aligns the content in a cell
3147  * @cfg {String} axis Categorizes cells
3148  * @cfg {String} bgcolor Specifies the background color of a cell
3149  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3150  * @cfg {Number} colspan Specifies the number of columns a cell should span
3151  * @cfg {String} headers Specifies one or more header cells a cell is related to
3152  * @cfg {Number} height Sets the height of a cell
3153  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3154  * @cfg {Number} rowspan Sets the number of rows a cell should span
3155  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3156  * @cfg {String} valign Vertical aligns the content in a cell
3157  * @cfg {Number} width Specifies the width of a cell
3158  * 
3159  * @constructor
3160  * Create a new TableCell
3161  * @param {Object} config The config object
3162  */
3163
3164 Roo.bootstrap.TableCell = function(config){
3165     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3166 };
3167
3168 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3169     
3170     html: false,
3171     cls: false,
3172     tag: false,
3173     abbr: false,
3174     align: false,
3175     axis: false,
3176     bgcolor: false,
3177     charoff: false,
3178     colspan: false,
3179     headers: false,
3180     height: false,
3181     nowrap: false,
3182     rowspan: false,
3183     scope: false,
3184     valign: false,
3185     width: false,
3186     
3187     
3188     getAutoCreate : function(){
3189         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3190         
3191         cfg = {
3192             tag: 'td'
3193         }
3194         
3195         if(this.tag){
3196             cfg.tag = this.tag;
3197         }
3198         
3199         if (this.html) {
3200             cfg.html=this.html
3201         }
3202         if (this.cls) {
3203             cfg.cls=this.cls
3204         }
3205         if (this.abbr) {
3206             cfg.abbr=this.abbr
3207         }
3208         if (this.align) {
3209             cfg.align=this.align
3210         }
3211         if (this.axis) {
3212             cfg.axis=this.axis
3213         }
3214         if (this.bgcolor) {
3215             cfg.bgcolor=this.bgcolor
3216         }
3217         if (this.charoff) {
3218             cfg.charoff=this.charoff
3219         }
3220         if (this.colspan) {
3221             cfg.colspan=this.colspan
3222         }
3223         if (this.headers) {
3224             cfg.headers=this.headers
3225         }
3226         if (this.height) {
3227             cfg.height=this.height
3228         }
3229         if (this.nowrap) {
3230             cfg.nowrap=this.nowrap
3231         }
3232         if (this.rowspan) {
3233             cfg.rowspan=this.rowspan
3234         }
3235         if (this.scope) {
3236             cfg.scope=this.scope
3237         }
3238         if (this.valign) {
3239             cfg.valign=this.valign
3240         }
3241         if (this.width) {
3242             cfg.width=this.width
3243         }
3244         
3245         
3246         return cfg;
3247     }
3248    
3249 });
3250
3251  
3252
3253  /*
3254  * - LGPL
3255  *
3256  * table row
3257  * 
3258  */
3259
3260 /**
3261  * @class Roo.bootstrap.TableRow
3262  * @extends Roo.bootstrap.Component
3263  * Bootstrap TableRow class
3264  * @cfg {String} cls row class
3265  * @cfg {String} align Aligns the content in a table row
3266  * @cfg {String} bgcolor Specifies a background color for a table row
3267  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3268  * @cfg {String} valign Vertical aligns the content in a table row
3269  * 
3270  * @constructor
3271  * Create a new TableRow
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.TableRow = function(config){
3276     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3277 };
3278
3279 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3280     
3281     cls: false,
3282     align: false,
3283     bgcolor: false,
3284     charoff: false,
3285     valign: false,
3286     
3287     getAutoCreate : function(){
3288         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3289         
3290         cfg = {
3291             tag: 'tr'
3292         }
3293             
3294         if(this.cls){
3295             cfg.cls = this.cls;
3296         }
3297         if(this.align){
3298             cfg.align = this.align;
3299         }
3300         if(this.bgcolor){
3301             cfg.bgcolor = this.bgcolor;
3302         }
3303         if(this.charoff){
3304             cfg.charoff = this.charoff;
3305         }
3306         if(this.valign){
3307             cfg.valign = this.valign;
3308         }
3309         
3310         return cfg;
3311     }
3312    
3313 });
3314
3315  
3316
3317  /*
3318  * - LGPL
3319  *
3320  * table body
3321  * 
3322  */
3323
3324 /**
3325  * @class Roo.bootstrap.TableBody
3326  * @extends Roo.bootstrap.Component
3327  * Bootstrap TableBody class
3328  * @cfg {String} cls element class
3329  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3330  * @cfg {String} align Aligns the content inside the element
3331  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3332  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3333  * 
3334  * @constructor
3335  * Create a new TableBody
3336  * @param {Object} config The config object
3337  */
3338
3339 Roo.bootstrap.TableBody = function(config){
3340     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3341 };
3342
3343 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3344     
3345     cls: false,
3346     tag: false,
3347     align: false,
3348     charoff: false,
3349     valign: false,
3350     
3351     getAutoCreate : function(){
3352         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3353         
3354         cfg = {
3355             tag: 'tbody'
3356         }
3357             
3358         if (this.cls) {
3359             cfg.cls=this.cls
3360         }
3361         if(this.tag){
3362             cfg.tag = this.tag;
3363         }
3364         
3365         if(this.align){
3366             cfg.align = this.align;
3367         }
3368         if(this.charoff){
3369             cfg.charoff = this.charoff;
3370         }
3371         if(this.valign){
3372             cfg.valign = this.valign;
3373         }
3374         
3375         return cfg;
3376     }
3377     
3378     
3379 //    initEvents : function()
3380 //    {
3381 //        
3382 //        if(!this.store){
3383 //            return;
3384 //        }
3385 //        
3386 //        this.store = Roo.factory(this.store, Roo.data);
3387 //        this.store.on('load', this.onLoad, this);
3388 //        
3389 //        this.store.load();
3390 //        
3391 //    },
3392 //    
3393 //    onLoad: function () 
3394 //    {   
3395 //        this.fireEvent('load', this);
3396 //    }
3397 //    
3398 //   
3399 });
3400
3401  
3402
3403  /*
3404  * Based on:
3405  * Ext JS Library 1.1.1
3406  * Copyright(c) 2006-2007, Ext JS, LLC.
3407  *
3408  * Originally Released Under LGPL - original licence link has changed is not relivant.
3409  *
3410  * Fork - LGPL
3411  * <script type="text/javascript">
3412  */
3413
3414 // as we use this in bootstrap.
3415 Roo.namespace('Roo.form');
3416  /**
3417  * @class Roo.form.Action
3418  * Internal Class used to handle form actions
3419  * @constructor
3420  * @param {Roo.form.BasicForm} el The form element or its id
3421  * @param {Object} config Configuration options
3422  */
3423
3424  
3425  
3426 // define the action interface
3427 Roo.form.Action = function(form, options){
3428     this.form = form;
3429     this.options = options || {};
3430 };
3431 /**
3432  * Client Validation Failed
3433  * @const 
3434  */
3435 Roo.form.Action.CLIENT_INVALID = 'client';
3436 /**
3437  * Server Validation Failed
3438  * @const 
3439  */
3440 Roo.form.Action.SERVER_INVALID = 'server';
3441  /**
3442  * Connect to Server Failed
3443  * @const 
3444  */
3445 Roo.form.Action.CONNECT_FAILURE = 'connect';
3446 /**
3447  * Reading Data from Server Failed
3448  * @const 
3449  */
3450 Roo.form.Action.LOAD_FAILURE = 'load';
3451
3452 Roo.form.Action.prototype = {
3453     type : 'default',
3454     failureType : undefined,
3455     response : undefined,
3456     result : undefined,
3457
3458     // interface method
3459     run : function(options){
3460
3461     },
3462
3463     // interface method
3464     success : function(response){
3465
3466     },
3467
3468     // interface method
3469     handleResponse : function(response){
3470
3471     },
3472
3473     // default connection failure
3474     failure : function(response){
3475         
3476         this.response = response;
3477         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3478         this.form.afterAction(this, false);
3479     },
3480
3481     processResponse : function(response){
3482         this.response = response;
3483         if(!response.responseText){
3484             return true;
3485         }
3486         this.result = this.handleResponse(response);
3487         return this.result;
3488     },
3489
3490     // utility functions used internally
3491     getUrl : function(appendParams){
3492         var url = this.options.url || this.form.url || this.form.el.dom.action;
3493         if(appendParams){
3494             var p = this.getParams();
3495             if(p){
3496                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3497             }
3498         }
3499         return url;
3500     },
3501
3502     getMethod : function(){
3503         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3504     },
3505
3506     getParams : function(){
3507         var bp = this.form.baseParams;
3508         var p = this.options.params;
3509         if(p){
3510             if(typeof p == "object"){
3511                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3512             }else if(typeof p == 'string' && bp){
3513                 p += '&' + Roo.urlEncode(bp);
3514             }
3515         }else if(bp){
3516             p = Roo.urlEncode(bp);
3517         }
3518         return p;
3519     },
3520
3521     createCallback : function(){
3522         return {
3523             success: this.success,
3524             failure: this.failure,
3525             scope: this,
3526             timeout: (this.form.timeout*1000),
3527             upload: this.form.fileUpload ? this.success : undefined
3528         };
3529     }
3530 };
3531
3532 Roo.form.Action.Submit = function(form, options){
3533     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3534 };
3535
3536 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3537     type : 'submit',
3538
3539     haveProgress : false,
3540     uploadComplete : false,
3541     
3542     // uploadProgress indicator.
3543     uploadProgress : function()
3544     {
3545         if (!this.form.progressUrl) {
3546             return;
3547         }
3548         
3549         if (!this.haveProgress) {
3550             Roo.MessageBox.progress("Uploading", "Uploading");
3551         }
3552         if (this.uploadComplete) {
3553            Roo.MessageBox.hide();
3554            return;
3555         }
3556         
3557         this.haveProgress = true;
3558    
3559         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3560         
3561         var c = new Roo.data.Connection();
3562         c.request({
3563             url : this.form.progressUrl,
3564             params: {
3565                 id : uid
3566             },
3567             method: 'GET',
3568             success : function(req){
3569                //console.log(data);
3570                 var rdata = false;
3571                 var edata;
3572                 try  {
3573                    rdata = Roo.decode(req.responseText)
3574                 } catch (e) {
3575                     Roo.log("Invalid data from server..");
3576                     Roo.log(edata);
3577                     return;
3578                 }
3579                 if (!rdata || !rdata.success) {
3580                     Roo.log(rdata);
3581                     Roo.MessageBox.alert(Roo.encode(rdata));
3582                     return;
3583                 }
3584                 var data = rdata.data;
3585                 
3586                 if (this.uploadComplete) {
3587                    Roo.MessageBox.hide();
3588                    return;
3589                 }
3590                    
3591                 if (data){
3592                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3593                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3594                     );
3595                 }
3596                 this.uploadProgress.defer(2000,this);
3597             },
3598        
3599             failure: function(data) {
3600                 Roo.log('progress url failed ');
3601                 Roo.log(data);
3602             },
3603             scope : this
3604         });
3605            
3606     },
3607     
3608     
3609     run : function()
3610     {
3611         // run get Values on the form, so it syncs any secondary forms.
3612         this.form.getValues();
3613         
3614         var o = this.options;
3615         var method = this.getMethod();
3616         var isPost = method == 'POST';
3617         if(o.clientValidation === false || this.form.isValid()){
3618             
3619             if (this.form.progressUrl) {
3620                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3621                     (new Date() * 1) + '' + Math.random());
3622                     
3623             } 
3624             
3625             
3626             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3627                 form:this.form.el.dom,
3628                 url:this.getUrl(!isPost),
3629                 method: method,
3630                 params:isPost ? this.getParams() : null,
3631                 isUpload: this.form.fileUpload
3632             }));
3633             
3634             this.uploadProgress();
3635
3636         }else if (o.clientValidation !== false){ // client validation failed
3637             this.failureType = Roo.form.Action.CLIENT_INVALID;
3638             this.form.afterAction(this, false);
3639         }
3640     },
3641
3642     success : function(response)
3643     {
3644         this.uploadComplete= true;
3645         if (this.haveProgress) {
3646             Roo.MessageBox.hide();
3647         }
3648         
3649         
3650         var result = this.processResponse(response);
3651         if(result === true || result.success){
3652             this.form.afterAction(this, true);
3653             return;
3654         }
3655         if(result.errors){
3656             this.form.markInvalid(result.errors);
3657             this.failureType = Roo.form.Action.SERVER_INVALID;
3658         }
3659         this.form.afterAction(this, false);
3660     },
3661     failure : function(response)
3662     {
3663         this.uploadComplete= true;
3664         if (this.haveProgress) {
3665             Roo.MessageBox.hide();
3666         }
3667         
3668         this.response = response;
3669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3670         this.form.afterAction(this, false);
3671     },
3672     
3673     handleResponse : function(response){
3674         if(this.form.errorReader){
3675             var rs = this.form.errorReader.read(response);
3676             var errors = [];
3677             if(rs.records){
3678                 for(var i = 0, len = rs.records.length; i < len; i++) {
3679                     var r = rs.records[i];
3680                     errors[i] = r.data;
3681                 }
3682             }
3683             if(errors.length < 1){
3684                 errors = null;
3685             }
3686             return {
3687                 success : rs.success,
3688                 errors : errors
3689             };
3690         }
3691         var ret = false;
3692         try {
3693             ret = Roo.decode(response.responseText);
3694         } catch (e) {
3695             ret = {
3696                 success: false,
3697                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3698                 errors : []
3699             };
3700         }
3701         return ret;
3702         
3703     }
3704 });
3705
3706
3707 Roo.form.Action.Load = function(form, options){
3708     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3709     this.reader = this.form.reader;
3710 };
3711
3712 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3713     type : 'load',
3714
3715     run : function(){
3716         
3717         Roo.Ajax.request(Roo.apply(
3718                 this.createCallback(), {
3719                     method:this.getMethod(),
3720                     url:this.getUrl(false),
3721                     params:this.getParams()
3722         }));
3723     },
3724
3725     success : function(response){
3726         
3727         var result = this.processResponse(response);
3728         if(result === true || !result.success || !result.data){
3729             this.failureType = Roo.form.Action.LOAD_FAILURE;
3730             this.form.afterAction(this, false);
3731             return;
3732         }
3733         this.form.clearInvalid();
3734         this.form.setValues(result.data);
3735         this.form.afterAction(this, true);
3736     },
3737
3738     handleResponse : function(response){
3739         if(this.form.reader){
3740             var rs = this.form.reader.read(response);
3741             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3742             return {
3743                 success : rs.success,
3744                 data : data
3745             };
3746         }
3747         return Roo.decode(response.responseText);
3748     }
3749 });
3750
3751 Roo.form.Action.ACTION_TYPES = {
3752     'load' : Roo.form.Action.Load,
3753     'submit' : Roo.form.Action.Submit
3754 };/*
3755  * - LGPL
3756  *
3757  * form
3758  * 
3759  */
3760
3761 /**
3762  * @class Roo.bootstrap.Form
3763  * @extends Roo.bootstrap.Component
3764  * Bootstrap Form class
3765  * @cfg {String} method  GET | POST (default POST)
3766  * @cfg {String} labelAlign top | left (default top)
3767   * @cfg {String} align left  | right - for navbars
3768
3769  * 
3770  * @constructor
3771  * Create a new Form
3772  * @param {Object} config The config object
3773  */
3774
3775
3776 Roo.bootstrap.Form = function(config){
3777     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3778     this.addEvents({
3779         /**
3780          * @event clientvalidation
3781          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3782          * @param {Form} this
3783          * @param {Boolean} valid true if the form has passed client-side validation
3784          */
3785         clientvalidation: true,
3786         /**
3787          * @event beforeaction
3788          * Fires before any action is performed. Return false to cancel the action.
3789          * @param {Form} this
3790          * @param {Action} action The action to be performed
3791          */
3792         beforeaction: true,
3793         /**
3794          * @event actionfailed
3795          * Fires when an action fails.
3796          * @param {Form} this
3797          * @param {Action} action The action that failed
3798          */
3799         actionfailed : true,
3800         /**
3801          * @event actioncomplete
3802          * Fires when an action is completed.
3803          * @param {Form} this
3804          * @param {Action} action The action that completed
3805          */
3806         actioncomplete : true
3807     });
3808     
3809 };
3810
3811 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3812       
3813      /**
3814      * @cfg {String} method
3815      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3816      */
3817     method : 'POST',
3818     /**
3819      * @cfg {String} url
3820      * The URL to use for form actions if one isn't supplied in the action options.
3821      */
3822     /**
3823      * @cfg {Boolean} fileUpload
3824      * Set to true if this form is a file upload.
3825      */
3826      
3827     /**
3828      * @cfg {Object} baseParams
3829      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3830      */
3831       
3832     /**
3833      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3834      */
3835     timeout: 30,
3836     /**
3837      * @cfg {Sting} align (left|right) for navbar forms
3838      */
3839     align : 'left',
3840
3841     // private
3842     activeAction : null,
3843  
3844     /**
3845      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3846      * element by passing it or its id or mask the form itself by passing in true.
3847      * @type Mixed
3848      */
3849     waitMsgTarget : false,
3850     
3851      
3852     
3853     /**
3854      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3855      * element by passing it or its id or mask the form itself by passing in true.
3856      * @type Mixed
3857      */
3858     
3859     getAutoCreate : function(){
3860         
3861         var cfg = {
3862             tag: 'form',
3863             method : this.method || 'POST',
3864             id : this.id || Roo.id(),
3865             cls : ''
3866         }
3867         if (this.parent().xtype.match(/^Nav/)) {
3868             cfg.cls = 'navbar-form navbar-' + this.align;
3869             
3870         }
3871         
3872         if (this.labelAlign == 'left' ) {
3873             cfg.cls += ' form-horizontal';
3874         }
3875         
3876         
3877         return cfg;
3878     },
3879     initEvents : function()
3880     {
3881         this.el.on('submit', this.onSubmit, this);
3882         
3883         
3884     },
3885     // private
3886     onSubmit : function(e){
3887         e.stopEvent();
3888     },
3889     
3890      /**
3891      * Returns true if client-side validation on the form is successful.
3892      * @return Boolean
3893      */
3894     isValid : function(){
3895         var items = this.getItems();
3896         var valid = true;
3897         items.each(function(f){
3898            if(!f.validate()){
3899                valid = false;
3900                
3901            }
3902         });
3903         return valid;
3904     },
3905     /**
3906      * Returns true if any fields in this form have changed since their original load.
3907      * @return Boolean
3908      */
3909     isDirty : function(){
3910         var dirty = false;
3911         var items = this.getItems();
3912         items.each(function(f){
3913            if(f.isDirty()){
3914                dirty = true;
3915                return false;
3916            }
3917            return true;
3918         });
3919         return dirty;
3920     },
3921      /**
3922      * Performs a predefined action (submit or load) or custom actions you define on this form.
3923      * @param {String} actionName The name of the action type
3924      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3925      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3926      * accept other config options):
3927      * <pre>
3928 Property          Type             Description
3929 ----------------  ---------------  ----------------------------------------------------------------------------------
3930 url               String           The url for the action (defaults to the form's url)
3931 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3932 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3933 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3934                                    validate the form on the client (defaults to false)
3935      * </pre>
3936      * @return {BasicForm} this
3937      */
3938     doAction : function(action, options){
3939         if(typeof action == 'string'){
3940             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3941         }
3942         if(this.fireEvent('beforeaction', this, action) !== false){
3943             this.beforeAction(action);
3944             action.run.defer(100, action);
3945         }
3946         return this;
3947     },
3948     
3949     // private
3950     beforeAction : function(action){
3951         var o = action.options;
3952         
3953         // not really supported yet.. ??
3954         
3955         //if(this.waitMsgTarget === true){
3956             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3957         //}else if(this.waitMsgTarget){
3958         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3959         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3960         //}else {
3961         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3962        // }
3963          
3964     },
3965
3966     // private
3967     afterAction : function(action, success){
3968         this.activeAction = null;
3969         var o = action.options;
3970         
3971         //if(this.waitMsgTarget === true){
3972             this.el.unmask();
3973         //}else if(this.waitMsgTarget){
3974         //    this.waitMsgTarget.unmask();
3975         //}else{
3976         //    Roo.MessageBox.updateProgress(1);
3977         //    Roo.MessageBox.hide();
3978        // }
3979         // 
3980         if(success){
3981             if(o.reset){
3982                 this.reset();
3983             }
3984             Roo.callback(o.success, o.scope, [this, action]);
3985             this.fireEvent('actioncomplete', this, action);
3986             
3987         }else{
3988             
3989             // failure condition..
3990             // we have a scenario where updates need confirming.
3991             // eg. if a locking scenario exists..
3992             // we look for { errors : { needs_confirm : true }} in the response.
3993             if (
3994                 (typeof(action.result) != 'undefined')  &&
3995                 (typeof(action.result.errors) != 'undefined')  &&
3996                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3997            ){
3998                 var _t = this;
3999                 Roo.log("not supported yet");
4000                  /*
4001                 
4002                 Roo.MessageBox.confirm(
4003                     "Change requires confirmation",
4004                     action.result.errorMsg,
4005                     function(r) {
4006                         if (r != 'yes') {
4007                             return;
4008                         }
4009                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4010                     }
4011                     
4012                 );
4013                 */
4014                 
4015                 
4016                 return;
4017             }
4018             
4019             Roo.callback(o.failure, o.scope, [this, action]);
4020             // show an error message if no failed handler is set..
4021             if (!this.hasListener('actionfailed')) {
4022                 Roo.log("need to add dialog support");
4023                 /*
4024                 Roo.MessageBox.alert("Error",
4025                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4026                         action.result.errorMsg :
4027                         "Saving Failed, please check your entries or try again"
4028                 );
4029                 */
4030             }
4031             
4032             this.fireEvent('actionfailed', this, action);
4033         }
4034         
4035     },
4036     /**
4037      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4038      * @param {String} id The value to search for
4039      * @return Field
4040      */
4041     findField : function(id){
4042         var items = this.getItems();
4043         var field = items.get(id);
4044         if(!field){
4045              items.each(function(f){
4046                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4047                     field = f;
4048                     return false;
4049                 }
4050                 return true;
4051             });
4052         }
4053         return field || null;
4054     },
4055      /**
4056      * Mark fields in this form invalid in bulk.
4057      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4058      * @return {BasicForm} this
4059      */
4060     markInvalid : function(errors){
4061         if(errors instanceof Array){
4062             for(var i = 0, len = errors.length; i < len; i++){
4063                 var fieldError = errors[i];
4064                 var f = this.findField(fieldError.id);
4065                 if(f){
4066                     f.markInvalid(fieldError.msg);
4067                 }
4068             }
4069         }else{
4070             var field, id;
4071             for(id in errors){
4072                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4073                     field.markInvalid(errors[id]);
4074                 }
4075             }
4076         }
4077         //Roo.each(this.childForms || [], function (f) {
4078         //    f.markInvalid(errors);
4079         //});
4080         
4081         return this;
4082     },
4083
4084     /**
4085      * Set values for fields in this form in bulk.
4086      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4087      * @return {BasicForm} this
4088      */
4089     setValues : function(values){
4090         if(values instanceof Array){ // array of objects
4091             for(var i = 0, len = values.length; i < len; i++){
4092                 var v = values[i];
4093                 var f = this.findField(v.id);
4094                 if(f){
4095                     f.setValue(v.value);
4096                     if(this.trackResetOnLoad){
4097                         f.originalValue = f.getValue();
4098                     }
4099                 }
4100             }
4101         }else{ // object hash
4102             var field, id;
4103             for(id in values){
4104                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4105                     
4106                     if (field.setFromData && 
4107                         field.valueField && 
4108                         field.displayField &&
4109                         // combos' with local stores can 
4110                         // be queried via setValue()
4111                         // to set their value..
4112                         (field.store && !field.store.isLocal)
4113                         ) {
4114                         // it's a combo
4115                         var sd = { };
4116                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4117                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4118                         field.setFromData(sd);
4119                         
4120                     } else {
4121                         field.setValue(values[id]);
4122                     }
4123                     
4124                     
4125                     if(this.trackResetOnLoad){
4126                         field.originalValue = field.getValue();
4127                     }
4128                 }
4129             }
4130         }
4131          
4132         //Roo.each(this.childForms || [], function (f) {
4133         //    f.setValues(values);
4134         //});
4135                 
4136         return this;
4137     },
4138
4139     /**
4140      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4141      * they are returned as an array.
4142      * @param {Boolean} asString
4143      * @return {Object}
4144      */
4145     getValues : function(asString){
4146         //if (this.childForms) {
4147             // copy values from the child forms
4148         //    Roo.each(this.childForms, function (f) {
4149         //        this.setValues(f.getValues());
4150         //    }, this);
4151         //}
4152         
4153         
4154         
4155         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4156         if(asString === true){
4157             return fs;
4158         }
4159         return Roo.urlDecode(fs);
4160     },
4161     
4162     /**
4163      * Returns the fields in this form as an object with key/value pairs. 
4164      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4165      * @return {Object}
4166      */
4167     getFieldValues : function(with_hidden)
4168     {
4169         var items = this.getItems();
4170         var ret = {};
4171         items.each(function(f){
4172             if (!f.getName()) {
4173                 return;
4174             }
4175             var v = f.getValue();
4176             if (f.inputType =='radio') {
4177                 if (typeof(ret[f.getName()]) == 'undefined') {
4178                     ret[f.getName()] = ''; // empty..
4179                 }
4180                 
4181                 if (!f.el.dom.checked) {
4182                     return;
4183                     
4184                 }
4185                 v = f.el.dom.value;
4186                 
4187             }
4188             
4189             // not sure if this supported any more..
4190             if ((typeof(v) == 'object') && f.getRawValue) {
4191                 v = f.getRawValue() ; // dates..
4192             }
4193             // combo boxes where name != hiddenName...
4194             if (f.name != f.getName()) {
4195                 ret[f.name] = f.getRawValue();
4196             }
4197             ret[f.getName()] = v;
4198         });
4199         
4200         return ret;
4201     },
4202
4203     /**
4204      * Clears all invalid messages in this form.
4205      * @return {BasicForm} this
4206      */
4207     clearInvalid : function(){
4208         var items = this.getItems();
4209         
4210         items.each(function(f){
4211            f.clearInvalid();
4212         });
4213         
4214         
4215         
4216         return this;
4217     },
4218
4219     /**
4220      * Resets this form.
4221      * @return {BasicForm} this
4222      */
4223     reset : function(){
4224         var items = this.getItems();
4225         items.each(function(f){
4226             f.reset();
4227         });
4228         
4229         Roo.each(this.childForms || [], function (f) {
4230             f.reset();
4231         });
4232        
4233         
4234         return this;
4235     },
4236     getItems : function()
4237     {
4238         var r=new Roo.util.MixedCollection(false, function(o){
4239             return o.id || (o.id = Roo.id());
4240         });
4241         var iter = function(el) {
4242             if (el.inputEl) {
4243                 r.add(el);
4244             }
4245             if (!el.items) {
4246                 return;
4247             }
4248             Roo.each(el.items,function(e) {
4249                 iter(e);
4250             });
4251             
4252             
4253         };
4254         iter(this);
4255         return r;
4256         
4257         
4258         
4259         
4260     }
4261     
4262 });
4263
4264  
4265 /*
4266  * Based on:
4267  * Ext JS Library 1.1.1
4268  * Copyright(c) 2006-2007, Ext JS, LLC.
4269  *
4270  * Originally Released Under LGPL - original licence link has changed is not relivant.
4271  *
4272  * Fork - LGPL
4273  * <script type="text/javascript">
4274  */
4275 /**
4276  * @class Roo.form.VTypes
4277  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4278  * @singleton
4279  */
4280 Roo.form.VTypes = function(){
4281     // closure these in so they are only created once.
4282     var alpha = /^[a-zA-Z_]+$/;
4283     var alphanum = /^[a-zA-Z0-9_]+$/;
4284     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4285     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4286
4287     // All these messages and functions are configurable
4288     return {
4289         /**
4290          * The function used to validate email addresses
4291          * @param {String} value The email address
4292          */
4293         'email' : function(v){
4294             return email.test(v);
4295         },
4296         /**
4297          * The error text to display when the email validation function returns false
4298          * @type String
4299          */
4300         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4301         /**
4302          * The keystroke filter mask to be applied on email input
4303          * @type RegExp
4304          */
4305         'emailMask' : /[a-z0-9_\.\-@]/i,
4306
4307         /**
4308          * The function used to validate URLs
4309          * @param {String} value The URL
4310          */
4311         'url' : function(v){
4312             return url.test(v);
4313         },
4314         /**
4315          * The error text to display when the url validation function returns false
4316          * @type String
4317          */
4318         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4319         
4320         /**
4321          * The function used to validate alpha values
4322          * @param {String} value The value
4323          */
4324         'alpha' : function(v){
4325             return alpha.test(v);
4326         },
4327         /**
4328          * The error text to display when the alpha validation function returns false
4329          * @type String
4330          */
4331         'alphaText' : 'This field should only contain letters and _',
4332         /**
4333          * The keystroke filter mask to be applied on alpha input
4334          * @type RegExp
4335          */
4336         'alphaMask' : /[a-z_]/i,
4337
4338         /**
4339          * The function used to validate alphanumeric values
4340          * @param {String} value The value
4341          */
4342         'alphanum' : function(v){
4343             return alphanum.test(v);
4344         },
4345         /**
4346          * The error text to display when the alphanumeric validation function returns false
4347          * @type String
4348          */
4349         'alphanumText' : 'This field should only contain letters, numbers and _',
4350         /**
4351          * The keystroke filter mask to be applied on alphanumeric input
4352          * @type RegExp
4353          */
4354         'alphanumMask' : /[a-z0-9_]/i
4355     };
4356 }();/*
4357  * - LGPL
4358  *
4359  * Input
4360  * 
4361  */
4362
4363 /**
4364  * @class Roo.bootstrap.Input
4365  * @extends Roo.bootstrap.Component
4366  * Bootstrap Input class
4367  * @cfg {Boolean} disabled is it disabled
4368  * @cfg {String} fieldLabel - the label associated
4369  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4370  * @cfg {String} name name of the input
4371  * @cfg {string} fieldLabel - the label associated
4372  * @cfg {string}  inputType - input / file submit ...
4373  * @cfg {string} placeholder - placeholder to put in text.
4374  * @cfg {string}  before - input group add on before
4375  * @cfg {string} after - input group add on after
4376  * @cfg {string} size - (lg|sm) or leave empty..
4377  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4378  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4379  * @cfg {Number} md colspan out of 12 for computer-sized screens
4380  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4381  * @cfg {string} value default value of the input
4382  * @cfg {Number} labelWidth set the width of label (0-12)
4383  * @cfg {String} labelAlign (top|left)
4384  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4385  * 
4386  * 
4387  * @constructor
4388  * Create a new Input
4389  * @param {Object} config The config object
4390  */
4391
4392 Roo.bootstrap.Input = function(config){
4393     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4394    
4395         this.addEvents({
4396             /**
4397              * @event focus
4398              * Fires when this field receives input focus.
4399              * @param {Roo.form.Field} this
4400              */
4401             focus : true,
4402             /**
4403              * @event blur
4404              * Fires when this field loses input focus.
4405              * @param {Roo.form.Field} this
4406              */
4407             blur : true,
4408             /**
4409              * @event specialkey
4410              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4411              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4412              * @param {Roo.form.Field} this
4413              * @param {Roo.EventObject} e The event object
4414              */
4415             specialkey : true,
4416             /**
4417              * @event change
4418              * Fires just before the field blurs if the field value has changed.
4419              * @param {Roo.form.Field} this
4420              * @param {Mixed} newValue The new value
4421              * @param {Mixed} oldValue The original value
4422              */
4423             change : true,
4424             /**
4425              * @event invalid
4426              * Fires after the field has been marked as invalid.
4427              * @param {Roo.form.Field} this
4428              * @param {String} msg The validation message
4429              */
4430             invalid : true,
4431             /**
4432              * @event valid
4433              * Fires after the field has been validated with no errors.
4434              * @param {Roo.form.Field} this
4435              */
4436             valid : true,
4437              /**
4438              * @event keyup
4439              * Fires after the key up
4440              * @param {Roo.form.Field} this
4441              * @param {Roo.EventObject}  e The event Object
4442              */
4443             keyup : true
4444         });
4445 };
4446
4447 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4448      /**
4449      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4450       automatic validation (defaults to "keyup").
4451      */
4452     validationEvent : "keyup",
4453      /**
4454      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4455      */
4456     validateOnBlur : true,
4457     /**
4458      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4459      */
4460     validationDelay : 250,
4461      /**
4462      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4463      */
4464     focusClass : "x-form-focus",  // not needed???
4465     
4466        
4467     /**
4468      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4469      */
4470     invalidClass : "has-error",
4471     
4472     /**
4473      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4474      */
4475     selectOnFocus : false,
4476     
4477      /**
4478      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4479      */
4480     maskRe : null,
4481        /**
4482      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4483      */
4484     vtype : null,
4485     
4486       /**
4487      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4488      */
4489     disableKeyFilter : false,
4490     
4491        /**
4492      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4493      */
4494     disabled : false,
4495      /**
4496      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4497      */
4498     allowBlank : true,
4499     /**
4500      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4501      */
4502     blankText : "This field is required",
4503     
4504      /**
4505      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4506      */
4507     minLength : 0,
4508     /**
4509      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4510      */
4511     maxLength : Number.MAX_VALUE,
4512     /**
4513      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4514      */
4515     minLengthText : "The minimum length for this field is {0}",
4516     /**
4517      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4518      */
4519     maxLengthText : "The maximum length for this field is {0}",
4520   
4521     
4522     /**
4523      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4524      * If available, this function will be called only after the basic validators all return true, and will be passed the
4525      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4526      */
4527     validator : null,
4528     /**
4529      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4530      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4531      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4532      */
4533     regex : null,
4534     /**
4535      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4536      */
4537     regexText : "",
4538     
4539     
4540     
4541     fieldLabel : '',
4542     inputType : 'text',
4543     
4544     name : false,
4545     placeholder: false,
4546     before : false,
4547     after : false,
4548     size : false,
4549     // private
4550     hasFocus : false,
4551     preventMark: false,
4552     isFormField : true,
4553     value : '',
4554     labelWidth : 2,
4555     labelAlign : false,
4556     readOnly : false,
4557     
4558     parentLabelAlign : function()
4559     {
4560         var parent = this;
4561         while (parent.parent()) {
4562             parent = parent.parent();
4563             if (typeof(parent.labelAlign) !='undefined') {
4564                 return parent.labelAlign;
4565             }
4566         }
4567         return 'left';
4568         
4569     },
4570     
4571     getAutoCreate : function(){
4572         
4573         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4574         
4575         var id = Roo.id();
4576         
4577         var cfg = {};
4578         
4579         if(this.inputType != 'hidden'){
4580             cfg.cls = 'form-group' //input-group
4581         }
4582         
4583         var input =  {
4584             tag: 'input',
4585             id : id,
4586             type : this.inputType,
4587             value : this.value,
4588             cls : 'form-control',
4589             placeholder : this.placeholder || ''
4590             
4591         };
4592         
4593         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4594             input.maxLength = this.maxLength;
4595         }
4596         
4597         if (this.disabled) {
4598             input.disabled=true;
4599         }
4600         
4601         if (this.readOnly) {
4602             input.readonly=true;
4603         }
4604         
4605         if (this.name) {
4606             input.name = this.name;
4607         }
4608         if (this.size) {
4609             input.cls += ' input-' + this.size;
4610         }
4611         var settings=this;
4612         ['xs','sm','md','lg'].map(function(size){
4613             if (settings[size]) {
4614                 cfg.cls += ' col-' + size + '-' + settings[size];
4615             }
4616         });
4617         
4618         var inputblock = input;
4619         
4620         if (this.before || this.after) {
4621             
4622             inputblock = {
4623                 cls : 'input-group',
4624                 cn :  [] 
4625             };
4626             if (this.before) {
4627                 inputblock.cn.push({
4628                     tag :'span',
4629                     cls : 'input-group-addon',
4630                     html : this.before
4631                 });
4632             }
4633             inputblock.cn.push(input);
4634             if (this.after) {
4635                 inputblock.cn.push({
4636                     tag :'span',
4637                     cls : 'input-group-addon',
4638                     html : this.after
4639                 });
4640             }
4641             
4642         };
4643         
4644         if (align ==='left' && this.fieldLabel.length) {
4645                 Roo.log("left and has label");
4646                 cfg.cn = [
4647                     
4648                     {
4649                         tag: 'label',
4650                         'for' :  id,
4651                         cls : 'control-label col-sm-' + this.labelWidth,
4652                         html : this.fieldLabel
4653                         
4654                     },
4655                     {
4656                         cls : "col-sm-" + (12 - this.labelWidth), 
4657                         cn: [
4658                             inputblock
4659                         ]
4660                     }
4661                     
4662                 ];
4663         } else if ( this.fieldLabel.length) {
4664                 Roo.log(" label");
4665                  cfg.cn = [
4666                    
4667                     {
4668                         tag: 'label',
4669                         //cls : 'input-group-addon',
4670                         html : this.fieldLabel
4671                         
4672                     },
4673                     
4674                     inputblock
4675                     
4676                 ];
4677
4678         } else {
4679             
4680                 Roo.log(" no label && no align");
4681                 cfg.cn = [
4682                     
4683                         inputblock
4684                     
4685                 ];
4686                 
4687                 
4688         };
4689         Roo.log('input-parentType: ' + this.parentType);
4690         
4691         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4692            cfg.cls += ' navbar-form';
4693            Roo.log(cfg);
4694         }
4695         
4696         return cfg;
4697         
4698     },
4699     /**
4700      * return the real input element.
4701      */
4702     inputEl: function ()
4703     {
4704         return this.el.select('input.form-control',true).first();
4705     },
4706     setDisabled : function(v)
4707     {
4708         var i  = this.inputEl().dom;
4709         if (!v) {
4710             i.removeAttribute('disabled');
4711             return;
4712             
4713         }
4714         i.setAttribute('disabled','true');
4715     },
4716     initEvents : function()
4717     {
4718         
4719         this.inputEl().on("keydown" , this.fireKey,  this);
4720         this.inputEl().on("focus", this.onFocus,  this);
4721         this.inputEl().on("blur", this.onBlur,  this);
4722         
4723         this.inputEl().relayEvent('keyup', this);
4724
4725         // reference to original value for reset
4726         this.originalValue = this.getValue();
4727         //Roo.form.TextField.superclass.initEvents.call(this);
4728         if(this.validationEvent == 'keyup'){
4729             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4730             this.inputEl().on('keyup', this.filterValidation, this);
4731         }
4732         else if(this.validationEvent !== false){
4733             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4734         }
4735         
4736         if(this.selectOnFocus){
4737             this.on("focus", this.preFocus, this);
4738             
4739         }
4740         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4741             this.inputEl().on("keypress", this.filterKeys, this);
4742         }
4743        /* if(this.grow){
4744             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4745             this.el.on("click", this.autoSize,  this);
4746         }
4747         */
4748         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4749             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4750         }
4751         
4752     },
4753     filterValidation : function(e){
4754         if(!e.isNavKeyPress()){
4755             this.validationTask.delay(this.validationDelay);
4756         }
4757     },
4758      /**
4759      * Validates the field value
4760      * @return {Boolean} True if the value is valid, else false
4761      */
4762     validate : function(){
4763         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4764         if(this.disabled || this.validateValue(this.getRawValue())){
4765             this.clearInvalid();
4766             return true;
4767         }
4768         return false;
4769     },
4770     
4771     
4772     /**
4773      * Validates a value according to the field's validation rules and marks the field as invalid
4774      * if the validation fails
4775      * @param {Mixed} value The value to validate
4776      * @return {Boolean} True if the value is valid, else false
4777      */
4778     validateValue : function(value){
4779         if(value.length < 1)  { // if it's blank
4780              if(this.allowBlank){
4781                 this.clearInvalid();
4782                 return true;
4783              }else{
4784                 this.markInvalid(this.blankText);
4785                 return false;
4786              }
4787         }
4788         if(value.length < this.minLength){
4789             this.markInvalid(String.format(this.minLengthText, this.minLength));
4790             return false;
4791         }
4792         if(value.length > this.maxLength){
4793             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4794             return false;
4795         }
4796         if(this.vtype){
4797             var vt = Roo.form.VTypes;
4798             if(!vt[this.vtype](value, this)){
4799                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4800                 return false;
4801             }
4802         }
4803         if(typeof this.validator == "function"){
4804             var msg = this.validator(value);
4805             if(msg !== true){
4806                 this.markInvalid(msg);
4807                 return false;
4808             }
4809         }
4810         if(this.regex && !this.regex.test(value)){
4811             this.markInvalid(this.regexText);
4812             return false;
4813         }
4814         return true;
4815     },
4816
4817     
4818     
4819      // private
4820     fireKey : function(e){
4821         //Roo.log('field ' + e.getKey());
4822         if(e.isNavKeyPress()){
4823             this.fireEvent("specialkey", this, e);
4824         }
4825     },
4826     focus : function (selectText){
4827         if(this.rendered){
4828             this.inputEl().focus();
4829             if(selectText === true){
4830                 this.inputEl().dom.select();
4831             }
4832         }
4833         return this;
4834     } ,
4835     
4836     onFocus : function(){
4837         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4838            // this.el.addClass(this.focusClass);
4839         }
4840         if(!this.hasFocus){
4841             this.hasFocus = true;
4842             this.startValue = this.getValue();
4843             this.fireEvent("focus", this);
4844         }
4845     },
4846     
4847     beforeBlur : Roo.emptyFn,
4848
4849     
4850     // private
4851     onBlur : function(){
4852         this.beforeBlur();
4853         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4854             //this.el.removeClass(this.focusClass);
4855         }
4856         this.hasFocus = false;
4857         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4858             this.validate();
4859         }
4860         var v = this.getValue();
4861         if(String(v) !== String(this.startValue)){
4862             this.fireEvent('change', this, v, this.startValue);
4863         }
4864         this.fireEvent("blur", this);
4865     },
4866     
4867     /**
4868      * Resets the current field value to the originally loaded value and clears any validation messages
4869      */
4870     reset : function(){
4871         this.setValue(this.originalValue);
4872         this.clearInvalid();
4873     },
4874      /**
4875      * Returns the name of the field
4876      * @return {Mixed} name The name field
4877      */
4878     getName: function(){
4879         return this.name;
4880     },
4881      /**
4882      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4883      * @return {Mixed} value The field value
4884      */
4885     getValue : function(){
4886         return this.inputEl().getValue();
4887     },
4888     /**
4889      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4890      * @return {Mixed} value The field value
4891      */
4892     getRawValue : function(){
4893         var v = this.inputEl().getValue();
4894         
4895         return v;
4896     },
4897     
4898     /**
4899      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4900      * @param {Mixed} value The value to set
4901      */
4902     setRawValue : function(v){
4903         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4904     },
4905     
4906     selectText : function(start, end){
4907         var v = this.getRawValue();
4908         if(v.length > 0){
4909             start = start === undefined ? 0 : start;
4910             end = end === undefined ? v.length : end;
4911             var d = this.inputEl().dom;
4912             if(d.setSelectionRange){
4913                 d.setSelectionRange(start, end);
4914             }else if(d.createTextRange){
4915                 var range = d.createTextRange();
4916                 range.moveStart("character", start);
4917                 range.moveEnd("character", v.length-end);
4918                 range.select();
4919             }
4920         }
4921     },
4922     
4923     /**
4924      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4925      * @param {Mixed} value The value to set
4926      */
4927     setValue : function(v){
4928         this.value = v;
4929         if(this.rendered){
4930             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4931             this.validate();
4932         }
4933     },
4934     
4935     /*
4936     processValue : function(value){
4937         if(this.stripCharsRe){
4938             var newValue = value.replace(this.stripCharsRe, '');
4939             if(newValue !== value){
4940                 this.setRawValue(newValue);
4941                 return newValue;
4942             }
4943         }
4944         return value;
4945     },
4946   */
4947     preFocus : function(){
4948         
4949         if(this.selectOnFocus){
4950             this.inputEl().dom.select();
4951         }
4952     },
4953     filterKeys : function(e){
4954         var k = e.getKey();
4955         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4956             return;
4957         }
4958         var c = e.getCharCode(), cc = String.fromCharCode(c);
4959         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4960             return;
4961         }
4962         if(!this.maskRe.test(cc)){
4963             e.stopEvent();
4964         }
4965     },
4966      /**
4967      * Clear any invalid styles/messages for this field
4968      */
4969     clearInvalid : function(){
4970         
4971         if(!this.el || this.preventMark){ // not rendered
4972             return;
4973         }
4974         this.el.removeClass(this.invalidClass);
4975         /*
4976         switch(this.msgTarget){
4977             case 'qtip':
4978                 this.el.dom.qtip = '';
4979                 break;
4980             case 'title':
4981                 this.el.dom.title = '';
4982                 break;
4983             case 'under':
4984                 if(this.errorEl){
4985                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4986                 }
4987                 break;
4988             case 'side':
4989                 if(this.errorIcon){
4990                     this.errorIcon.dom.qtip = '';
4991                     this.errorIcon.hide();
4992                     this.un('resize', this.alignErrorIcon, this);
4993                 }
4994                 break;
4995             default:
4996                 var t = Roo.getDom(this.msgTarget);
4997                 t.innerHTML = '';
4998                 t.style.display = 'none';
4999                 break;
5000         }
5001         */
5002         this.fireEvent('valid', this);
5003     },
5004      /**
5005      * Mark this field as invalid
5006      * @param {String} msg The validation message
5007      */
5008     markInvalid : function(msg){
5009         if(!this.el  || this.preventMark){ // not rendered
5010             return;
5011         }
5012         this.el.addClass(this.invalidClass);
5013         /*
5014         msg = msg || this.invalidText;
5015         switch(this.msgTarget){
5016             case 'qtip':
5017                 this.el.dom.qtip = msg;
5018                 this.el.dom.qclass = 'x-form-invalid-tip';
5019                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5020                     Roo.QuickTips.enable();
5021                 }
5022                 break;
5023             case 'title':
5024                 this.el.dom.title = msg;
5025                 break;
5026             case 'under':
5027                 if(!this.errorEl){
5028                     var elp = this.el.findParent('.x-form-element', 5, true);
5029                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5030                     this.errorEl.setWidth(elp.getWidth(true)-20);
5031                 }
5032                 this.errorEl.update(msg);
5033                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5034                 break;
5035             case 'side':
5036                 if(!this.errorIcon){
5037                     var elp = this.el.findParent('.x-form-element', 5, true);
5038                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5039                 }
5040                 this.alignErrorIcon();
5041                 this.errorIcon.dom.qtip = msg;
5042                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5043                 this.errorIcon.show();
5044                 this.on('resize', this.alignErrorIcon, this);
5045                 break;
5046             default:
5047                 var t = Roo.getDom(this.msgTarget);
5048                 t.innerHTML = msg;
5049                 t.style.display = this.msgDisplay;
5050                 break;
5051         }
5052         */
5053         this.fireEvent('invalid', this, msg);
5054     },
5055     // private
5056     SafariOnKeyDown : function(event)
5057     {
5058         // this is a workaround for a password hang bug on chrome/ webkit.
5059         
5060         var isSelectAll = false;
5061         
5062         if(this.inputEl().dom.selectionEnd > 0){
5063             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5064         }
5065         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5066             event.preventDefault();
5067             this.setValue('');
5068             return;
5069         }
5070         
5071         if(isSelectAll){ // backspace and delete key
5072             
5073             event.preventDefault();
5074             // this is very hacky as keydown always get's upper case.
5075             //
5076             var cc = String.fromCharCode(event.getCharCode());
5077             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5078             
5079         }
5080     },
5081     adjustWidth : function(tag, w){
5082         tag = tag.toLowerCase();
5083         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5084             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5085                 if(tag == 'input'){
5086                     return w + 2;
5087                 }
5088                 if(tag == 'textarea'){
5089                     return w-2;
5090                 }
5091             }else if(Roo.isOpera){
5092                 if(tag == 'input'){
5093                     return w + 2;
5094                 }
5095                 if(tag == 'textarea'){
5096                     return w-2;
5097                 }
5098             }
5099         }
5100         return w;
5101     }
5102     
5103 });
5104
5105  
5106 /*
5107  * - LGPL
5108  *
5109  * Input
5110  * 
5111  */
5112
5113 /**
5114  * @class Roo.bootstrap.TextArea
5115  * @extends Roo.bootstrap.Input
5116  * Bootstrap TextArea class
5117  * @cfg {Number} cols Specifies the visible width of a text area
5118  * @cfg {Number} rows Specifies the visible number of lines in a text area
5119  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5120  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5121  * @cfg {string} html text
5122  * 
5123  * @constructor
5124  * Create a new TextArea
5125  * @param {Object} config The config object
5126  */
5127
5128 Roo.bootstrap.TextArea = function(config){
5129     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5130    
5131 };
5132
5133 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5134      
5135     cols : false,
5136     rows : 5,
5137     readOnly : false,
5138     warp : 'soft',
5139     resize : false,
5140     value: false,
5141     html: false,
5142     
5143     getAutoCreate : function(){
5144         
5145         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5146         
5147         var id = Roo.id();
5148         
5149         var cfg = {};
5150         
5151         var input =  {
5152             tag: 'textarea',
5153             id : id,
5154             warp : this.warp,
5155             rows : this.rows,
5156             value : this.value || '',
5157             html: this.html || '',
5158             cls : 'form-control',
5159             placeholder : this.placeholder || '' 
5160             
5161         };
5162         
5163         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5164             input.maxLength = this.maxLength;
5165         }
5166         
5167         if(this.resize){
5168             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5169         }
5170         
5171         if(this.cols){
5172             input.cols = this.cols;
5173         }
5174         
5175         if (this.readOnly) {
5176             input.readonly = true;
5177         }
5178         
5179         if (this.name) {
5180             input.name = this.name;
5181         }
5182         
5183         if (this.size) {
5184             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5185         }
5186         
5187         var settings=this;
5188         ['xs','sm','md','lg'].map(function(size){
5189             if (settings[size]) {
5190                 cfg.cls += ' col-' + size + '-' + settings[size];
5191             }
5192         });
5193         
5194         var inputblock = input;
5195         
5196         if (this.before || this.after) {
5197             
5198             inputblock = {
5199                 cls : 'input-group',
5200                 cn :  [] 
5201             };
5202             if (this.before) {
5203                 inputblock.cn.push({
5204                     tag :'span',
5205                     cls : 'input-group-addon',
5206                     html : this.before
5207                 });
5208             }
5209             inputblock.cn.push(input);
5210             if (this.after) {
5211                 inputblock.cn.push({
5212                     tag :'span',
5213                     cls : 'input-group-addon',
5214                     html : this.after
5215                 });
5216             }
5217             
5218         }
5219         
5220         if (align ==='left' && this.fieldLabel.length) {
5221                 Roo.log("left and has label");
5222                 cfg.cn = [
5223                     
5224                     {
5225                         tag: 'label',
5226                         'for' :  id,
5227                         cls : 'control-label col-sm-' + this.labelWidth,
5228                         html : this.fieldLabel
5229                         
5230                     },
5231                     {
5232                         cls : "col-sm-" + (12 - this.labelWidth), 
5233                         cn: [
5234                             inputblock
5235                         ]
5236                     }
5237                     
5238                 ];
5239         } else if ( this.fieldLabel.length) {
5240                 Roo.log(" label");
5241                  cfg.cn = [
5242                    
5243                     {
5244                         tag: 'label',
5245                         //cls : 'input-group-addon',
5246                         html : this.fieldLabel
5247                         
5248                     },
5249                     
5250                     inputblock
5251                     
5252                 ];
5253
5254         } else {
5255             
5256                    Roo.log(" no label && no align");
5257                 cfg.cn = [
5258                     
5259                         inputblock
5260                     
5261                 ];
5262                 
5263                 
5264         }
5265         
5266         if (this.disabled) {
5267             input.disabled=true;
5268         }
5269         
5270         return cfg;
5271         
5272     },
5273     /**
5274      * return the real textarea element.
5275      */
5276     inputEl: function ()
5277     {
5278         return this.el.select('textarea.form-control',true).first();
5279     }
5280 });
5281
5282  
5283 /*
5284  * - LGPL
5285  *
5286  * trigger field - base class for combo..
5287  * 
5288  */
5289  
5290 /**
5291  * @class Roo.bootstrap.TriggerField
5292  * @extends Roo.bootstrap.Input
5293  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5294  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5295  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5296  * for which you can provide a custom implementation.  For example:
5297  * <pre><code>
5298 var trigger = new Roo.bootstrap.TriggerField();
5299 trigger.onTriggerClick = myTriggerFn;
5300 trigger.applyTo('my-field');
5301 </code></pre>
5302  *
5303  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5304  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5305  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5306  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5307  * @constructor
5308  * Create a new TriggerField.
5309  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5310  * to the base TextField)
5311  */
5312 Roo.bootstrap.TriggerField = function(config){
5313     this.mimicing = false;
5314     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5315 };
5316
5317 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5318     /**
5319      * @cfg {String} triggerClass A CSS class to apply to the trigger
5320      */
5321      /**
5322      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5323      */
5324     hideTrigger:false,
5325
5326     /** @cfg {Boolean} grow @hide */
5327     /** @cfg {Number} growMin @hide */
5328     /** @cfg {Number} growMax @hide */
5329
5330     /**
5331      * @hide 
5332      * @method
5333      */
5334     autoSize: Roo.emptyFn,
5335     // private
5336     monitorTab : true,
5337     // private
5338     deferHeight : true,
5339
5340     
5341     actionMode : 'wrap',
5342     
5343     
5344     
5345     getAutoCreate : function(){
5346        
5347         var parent = this.parent();
5348         
5349         var align = this.parentLabelAlign();
5350         
5351         var id = Roo.id();
5352         
5353         var cfg = {
5354             cls: 'form-group' //input-group
5355         };
5356         
5357         
5358         var input =  {
5359             tag: 'input',
5360             id : id,
5361             type : this.inputType,
5362             cls : 'form-control',
5363             autocomplete: 'off',
5364             placeholder : this.placeholder || '' 
5365             
5366         };
5367         if (this.name) {
5368             input.name = this.name;
5369         }
5370         if (this.size) {
5371             input.cls += ' input-' + this.size;
5372         }
5373         
5374         if (this.disabled) {
5375             input.disabled=true;
5376         }
5377         
5378         var inputblock = input;
5379         
5380         if (this.before || this.after) {
5381             
5382             inputblock = {
5383                 cls : 'input-group',
5384                 cn :  [] 
5385             };
5386             if (this.before) {
5387                 inputblock.cn.push({
5388                     tag :'span',
5389                     cls : 'input-group-addon',
5390                     html : this.before
5391                 });
5392             }
5393             inputblock.cn.push(input);
5394             if (this.after) {
5395                 inputblock.cn.push({
5396                     tag :'span',
5397                     cls : 'input-group-addon',
5398                     html : this.after
5399                 });
5400             }
5401             
5402         };
5403         
5404         var box = {
5405             tag: 'div',
5406             cn: [
5407                 {
5408                     tag: 'input',
5409                     type : 'hidden',
5410                     cls: 'form-hidden-field'
5411                 },
5412                 inputblock
5413             ]
5414             
5415         };
5416         
5417         if(this.multiple){
5418             Roo.log('multiple');
5419             
5420             box = {
5421                 tag: 'div',
5422                 cn: [
5423                     {
5424                         tag: 'input',
5425                         type : 'hidden',
5426                         cls: 'form-hidden-field'
5427                     },
5428                     {
5429                         tag: 'ul',
5430                         cls: 'select2-choices',
5431                         cn:[
5432                             {
5433                                 tag: 'li',
5434                                 cls: 'select2-search-field',
5435                                 cn: [
5436
5437                                     inputblock
5438                                 ]
5439                             }
5440                         ]
5441                     }
5442                 ]
5443             }
5444         };
5445         
5446         var combobox = {
5447             cls: 'select2-container input-group',
5448             cn: [
5449                 box,
5450                 {
5451                     tag: 'ul',
5452                     cls: 'typeahead typeahead-long dropdown-menu',
5453                     style: 'display:none'
5454                 }
5455             ]
5456         };
5457         
5458         if(!this.multiple){
5459             combobox.cn.push({
5460                 tag :'span',
5461                 cls : 'input-group-addon btn dropdown-toggle',
5462                 cn : [
5463                     {
5464                         tag: 'span',
5465                         cls: 'caret'
5466                     },
5467                     {
5468                         tag: 'span',
5469                         cls: 'combobox-clear',
5470                         cn  : [
5471                             {
5472                                 tag : 'i',
5473                                 cls: 'icon-remove'
5474                             }
5475                         ]
5476                     }
5477                 ]
5478
5479             })
5480         }
5481         
5482         if(this.multiple){
5483             combobox.cls += ' select2-container-multi';
5484         }
5485         
5486         if (align ==='left' && this.fieldLabel.length) {
5487             
5488                 Roo.log("left and has label");
5489                 cfg.cn = [
5490                     
5491                     {
5492                         tag: 'label',
5493                         'for' :  id,
5494                         cls : 'control-label col-sm-' + this.labelWidth,
5495                         html : this.fieldLabel
5496                         
5497                     },
5498                     {
5499                         cls : "col-sm-" + (12 - this.labelWidth), 
5500                         cn: [
5501                             combobox
5502                         ]
5503                     }
5504                     
5505                 ];
5506         } else if ( this.fieldLabel.length) {
5507                 Roo.log(" label");
5508                  cfg.cn = [
5509                    
5510                     {
5511                         tag: 'label',
5512                         //cls : 'input-group-addon',
5513                         html : this.fieldLabel
5514                         
5515                     },
5516                     
5517                     combobox
5518                     
5519                 ];
5520
5521         } else {
5522             
5523                 Roo.log(" no label && no align");
5524                 cfg = combobox
5525                      
5526                 
5527         }
5528          
5529         var settings=this;
5530         ['xs','sm','md','lg'].map(function(size){
5531             if (settings[size]) {
5532                 cfg.cls += ' col-' + size + '-' + settings[size];
5533             }
5534         });
5535         
5536         return cfg;
5537         
5538     },
5539     
5540     
5541     
5542     // private
5543     onResize : function(w, h){
5544 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5545 //        if(typeof w == 'number'){
5546 //            var x = w - this.trigger.getWidth();
5547 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5548 //            this.trigger.setStyle('left', x+'px');
5549 //        }
5550     },
5551
5552     // private
5553     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5554
5555     // private
5556     getResizeEl : function(){
5557         return this.inputEl();
5558     },
5559
5560     // private
5561     getPositionEl : function(){
5562         return this.inputEl();
5563     },
5564
5565     // private
5566     alignErrorIcon : function(){
5567         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5568     },
5569
5570     // private
5571     initEvents : function(){
5572         
5573         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5574         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5575         if(!this.multiple){
5576             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5577             if(this.hideTrigger){
5578                 this.trigger.setDisplayed(false);
5579             }
5580             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5581         }
5582         
5583         if(this.multiple){
5584             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5585         }
5586         
5587         //this.trigger.addClassOnOver('x-form-trigger-over');
5588         //this.trigger.addClassOnClick('x-form-trigger-click');
5589         
5590         //if(!this.width){
5591         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5592         //}
5593     },
5594
5595     // private
5596     initTrigger : function(){
5597        
5598     },
5599
5600     // private
5601     onDestroy : function(){
5602         if(this.trigger){
5603             this.trigger.removeAllListeners();
5604           //  this.trigger.remove();
5605         }
5606         //if(this.wrap){
5607         //    this.wrap.remove();
5608         //}
5609         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5610     },
5611
5612     // private
5613     onFocus : function(){
5614         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5615         /*
5616         if(!this.mimicing){
5617             this.wrap.addClass('x-trigger-wrap-focus');
5618             this.mimicing = true;
5619             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5620             if(this.monitorTab){
5621                 this.el.on("keydown", this.checkTab, this);
5622             }
5623         }
5624         */
5625     },
5626
5627     // private
5628     checkTab : function(e){
5629         if(e.getKey() == e.TAB){
5630             this.triggerBlur();
5631         }
5632     },
5633
5634     // private
5635     onBlur : function(){
5636         // do nothing
5637     },
5638
5639     // private
5640     mimicBlur : function(e, t){
5641         /*
5642         if(!this.wrap.contains(t) && this.validateBlur()){
5643             this.triggerBlur();
5644         }
5645         */
5646     },
5647
5648     // private
5649     triggerBlur : function(){
5650         this.mimicing = false;
5651         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5652         if(this.monitorTab){
5653             this.el.un("keydown", this.checkTab, this);
5654         }
5655         //this.wrap.removeClass('x-trigger-wrap-focus');
5656         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5657     },
5658
5659     // private
5660     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5661     validateBlur : function(e, t){
5662         return true;
5663     },
5664
5665     // private
5666     onDisable : function(){
5667         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5668         //if(this.wrap){
5669         //    this.wrap.addClass('x-item-disabled');
5670         //}
5671     },
5672
5673     // private
5674     onEnable : function(){
5675         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5676         //if(this.wrap){
5677         //    this.el.removeClass('x-item-disabled');
5678         //}
5679     },
5680
5681     // private
5682     onShow : function(){
5683         var ae = this.getActionEl();
5684         
5685         if(ae){
5686             ae.dom.style.display = '';
5687             ae.dom.style.visibility = 'visible';
5688         }
5689     },
5690
5691     // private
5692     
5693     onHide : function(){
5694         var ae = this.getActionEl();
5695         ae.dom.style.display = 'none';
5696     },
5697
5698     /**
5699      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5700      * by an implementing function.
5701      * @method
5702      * @param {EventObject} e
5703      */
5704     onTriggerClick : Roo.emptyFn
5705 });
5706  /*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716
5717
5718 /**
5719  * @class Roo.data.SortTypes
5720  * @singleton
5721  * Defines the default sorting (casting?) comparison functions used when sorting data.
5722  */
5723 Roo.data.SortTypes = {
5724     /**
5725      * Default sort that does nothing
5726      * @param {Mixed} s The value being converted
5727      * @return {Mixed} The comparison value
5728      */
5729     none : function(s){
5730         return s;
5731     },
5732     
5733     /**
5734      * The regular expression used to strip tags
5735      * @type {RegExp}
5736      * @property
5737      */
5738     stripTagsRE : /<\/?[^>]+>/gi,
5739     
5740     /**
5741      * Strips all HTML tags to sort on text only
5742      * @param {Mixed} s The value being converted
5743      * @return {String} The comparison value
5744      */
5745     asText : function(s){
5746         return String(s).replace(this.stripTagsRE, "");
5747     },
5748     
5749     /**
5750      * Strips all HTML tags to sort on text only - Case insensitive
5751      * @param {Mixed} s The value being converted
5752      * @return {String} The comparison value
5753      */
5754     asUCText : function(s){
5755         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5756     },
5757     
5758     /**
5759      * Case insensitive string
5760      * @param {Mixed} s The value being converted
5761      * @return {String} The comparison value
5762      */
5763     asUCString : function(s) {
5764         return String(s).toUpperCase();
5765     },
5766     
5767     /**
5768      * Date sorting
5769      * @param {Mixed} s The value being converted
5770      * @return {Number} The comparison value
5771      */
5772     asDate : function(s) {
5773         if(!s){
5774             return 0;
5775         }
5776         if(s instanceof Date){
5777             return s.getTime();
5778         }
5779         return Date.parse(String(s));
5780     },
5781     
5782     /**
5783      * Float sorting
5784      * @param {Mixed} s The value being converted
5785      * @return {Float} The comparison value
5786      */
5787     asFloat : function(s) {
5788         var val = parseFloat(String(s).replace(/,/g, ""));
5789         if(isNaN(val)) val = 0;
5790         return val;
5791     },
5792     
5793     /**
5794      * Integer sorting
5795      * @param {Mixed} s The value being converted
5796      * @return {Number} The comparison value
5797      */
5798     asInt : function(s) {
5799         var val = parseInt(String(s).replace(/,/g, ""));
5800         if(isNaN(val)) val = 0;
5801         return val;
5802     }
5803 };/*
5804  * Based on:
5805  * Ext JS Library 1.1.1
5806  * Copyright(c) 2006-2007, Ext JS, LLC.
5807  *
5808  * Originally Released Under LGPL - original licence link has changed is not relivant.
5809  *
5810  * Fork - LGPL
5811  * <script type="text/javascript">
5812  */
5813
5814 /**
5815 * @class Roo.data.Record
5816  * Instances of this class encapsulate both record <em>definition</em> information, and record
5817  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5818  * to access Records cached in an {@link Roo.data.Store} object.<br>
5819  * <p>
5820  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5821  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5822  * objects.<br>
5823  * <p>
5824  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5825  * @constructor
5826  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5827  * {@link #create}. The parameters are the same.
5828  * @param {Array} data An associative Array of data values keyed by the field name.
5829  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5830  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5831  * not specified an integer id is generated.
5832  */
5833 Roo.data.Record = function(data, id){
5834     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5835     this.data = data;
5836 };
5837
5838 /**
5839  * Generate a constructor for a specific record layout.
5840  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5841  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5842  * Each field definition object may contain the following properties: <ul>
5843  * <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,
5844  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5845  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5846  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5847  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5848  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5849  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5850  * this may be omitted.</p></li>
5851  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5852  * <ul><li>auto (Default, implies no conversion)</li>
5853  * <li>string</li>
5854  * <li>int</li>
5855  * <li>float</li>
5856  * <li>boolean</li>
5857  * <li>date</li></ul></p></li>
5858  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5859  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5860  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5861  * by the Reader into an object that will be stored in the Record. It is passed the
5862  * following parameters:<ul>
5863  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5864  * </ul></p></li>
5865  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5866  * </ul>
5867  * <br>usage:<br><pre><code>
5868 var TopicRecord = Roo.data.Record.create(
5869     {name: 'title', mapping: 'topic_title'},
5870     {name: 'author', mapping: 'username'},
5871     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5872     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5873     {name: 'lastPoster', mapping: 'user2'},
5874     {name: 'excerpt', mapping: 'post_text'}
5875 );
5876
5877 var myNewRecord = new TopicRecord({
5878     title: 'Do my job please',
5879     author: 'noobie',
5880     totalPosts: 1,
5881     lastPost: new Date(),
5882     lastPoster: 'Animal',
5883     excerpt: 'No way dude!'
5884 });
5885 myStore.add(myNewRecord);
5886 </code></pre>
5887  * @method create
5888  * @static
5889  */
5890 Roo.data.Record.create = function(o){
5891     var f = function(){
5892         f.superclass.constructor.apply(this, arguments);
5893     };
5894     Roo.extend(f, Roo.data.Record);
5895     var p = f.prototype;
5896     p.fields = new Roo.util.MixedCollection(false, function(field){
5897         return field.name;
5898     });
5899     for(var i = 0, len = o.length; i < len; i++){
5900         p.fields.add(new Roo.data.Field(o[i]));
5901     }
5902     f.getField = function(name){
5903         return p.fields.get(name);  
5904     };
5905     return f;
5906 };
5907
5908 Roo.data.Record.AUTO_ID = 1000;
5909 Roo.data.Record.EDIT = 'edit';
5910 Roo.data.Record.REJECT = 'reject';
5911 Roo.data.Record.COMMIT = 'commit';
5912
5913 Roo.data.Record.prototype = {
5914     /**
5915      * Readonly flag - true if this record has been modified.
5916      * @type Boolean
5917      */
5918     dirty : false,
5919     editing : false,
5920     error: null,
5921     modified: null,
5922
5923     // private
5924     join : function(store){
5925         this.store = store;
5926     },
5927
5928     /**
5929      * Set the named field to the specified value.
5930      * @param {String} name The name of the field to set.
5931      * @param {Object} value The value to set the field to.
5932      */
5933     set : function(name, value){
5934         if(this.data[name] == value){
5935             return;
5936         }
5937         this.dirty = true;
5938         if(!this.modified){
5939             this.modified = {};
5940         }
5941         if(typeof this.modified[name] == 'undefined'){
5942             this.modified[name] = this.data[name];
5943         }
5944         this.data[name] = value;
5945         if(!this.editing && this.store){
5946             this.store.afterEdit(this);
5947         }       
5948     },
5949
5950     /**
5951      * Get the value of the named field.
5952      * @param {String} name The name of the field to get the value of.
5953      * @return {Object} The value of the field.
5954      */
5955     get : function(name){
5956         return this.data[name]; 
5957     },
5958
5959     // private
5960     beginEdit : function(){
5961         this.editing = true;
5962         this.modified = {}; 
5963     },
5964
5965     // private
5966     cancelEdit : function(){
5967         this.editing = false;
5968         delete this.modified;
5969     },
5970
5971     // private
5972     endEdit : function(){
5973         this.editing = false;
5974         if(this.dirty && this.store){
5975             this.store.afterEdit(this);
5976         }
5977     },
5978
5979     /**
5980      * Usually called by the {@link Roo.data.Store} which owns the Record.
5981      * Rejects all changes made to the Record since either creation, or the last commit operation.
5982      * Modified fields are reverted to their original values.
5983      * <p>
5984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5985      * of reject operations.
5986      */
5987     reject : function(){
5988         var m = this.modified;
5989         for(var n in m){
5990             if(typeof m[n] != "function"){
5991                 this.data[n] = m[n];
5992             }
5993         }
5994         this.dirty = false;
5995         delete this.modified;
5996         this.editing = false;
5997         if(this.store){
5998             this.store.afterReject(this);
5999         }
6000     },
6001
6002     /**
6003      * Usually called by the {@link Roo.data.Store} which owns the Record.
6004      * Commits all changes made to the Record since either creation, or the last commit operation.
6005      * <p>
6006      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6007      * of commit operations.
6008      */
6009     commit : function(){
6010         this.dirty = false;
6011         delete this.modified;
6012         this.editing = false;
6013         if(this.store){
6014             this.store.afterCommit(this);
6015         }
6016     },
6017
6018     // private
6019     hasError : function(){
6020         return this.error != null;
6021     },
6022
6023     // private
6024     clearError : function(){
6025         this.error = null;
6026     },
6027
6028     /**
6029      * Creates a copy of this record.
6030      * @param {String} id (optional) A new record id if you don't want to use this record's id
6031      * @return {Record}
6032      */
6033     copy : function(newId) {
6034         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6035     }
6036 };/*
6037  * Based on:
6038  * Ext JS Library 1.1.1
6039  * Copyright(c) 2006-2007, Ext JS, LLC.
6040  *
6041  * Originally Released Under LGPL - original licence link has changed is not relivant.
6042  *
6043  * Fork - LGPL
6044  * <script type="text/javascript">
6045  */
6046
6047
6048
6049 /**
6050  * @class Roo.data.Store
6051  * @extends Roo.util.Observable
6052  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6053  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6054  * <p>
6055  * 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
6056  * has no knowledge of the format of the data returned by the Proxy.<br>
6057  * <p>
6058  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6059  * instances from the data object. These records are cached and made available through accessor functions.
6060  * @constructor
6061  * Creates a new Store.
6062  * @param {Object} config A config object containing the objects needed for the Store to access data,
6063  * and read the data into Records.
6064  */
6065 Roo.data.Store = function(config){
6066     this.data = new Roo.util.MixedCollection(false);
6067     this.data.getKey = function(o){
6068         return o.id;
6069     };
6070     this.baseParams = {};
6071     // private
6072     this.paramNames = {
6073         "start" : "start",
6074         "limit" : "limit",
6075         "sort" : "sort",
6076         "dir" : "dir",
6077         "multisort" : "_multisort"
6078     };
6079
6080     if(config && config.data){
6081         this.inlineData = config.data;
6082         delete config.data;
6083     }
6084
6085     Roo.apply(this, config);
6086     
6087     if(this.reader){ // reader passed
6088         this.reader = Roo.factory(this.reader, Roo.data);
6089         this.reader.xmodule = this.xmodule || false;
6090         if(!this.recordType){
6091             this.recordType = this.reader.recordType;
6092         }
6093         if(this.reader.onMetaChange){
6094             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6095         }
6096     }
6097
6098     if(this.recordType){
6099         this.fields = this.recordType.prototype.fields;
6100     }
6101     this.modified = [];
6102
6103     this.addEvents({
6104         /**
6105          * @event datachanged
6106          * Fires when the data cache has changed, and a widget which is using this Store
6107          * as a Record cache should refresh its view.
6108          * @param {Store} this
6109          */
6110         datachanged : true,
6111         /**
6112          * @event metachange
6113          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6114          * @param {Store} this
6115          * @param {Object} meta The JSON metadata
6116          */
6117         metachange : true,
6118         /**
6119          * @event add
6120          * Fires when Records have been added to the Store
6121          * @param {Store} this
6122          * @param {Roo.data.Record[]} records The array of Records added
6123          * @param {Number} index The index at which the record(s) were added
6124          */
6125         add : true,
6126         /**
6127          * @event remove
6128          * Fires when a Record has been removed from the Store
6129          * @param {Store} this
6130          * @param {Roo.data.Record} record The Record that was removed
6131          * @param {Number} index The index at which the record was removed
6132          */
6133         remove : true,
6134         /**
6135          * @event update
6136          * Fires when a Record has been updated
6137          * @param {Store} this
6138          * @param {Roo.data.Record} record The Record that was updated
6139          * @param {String} operation The update operation being performed.  Value may be one of:
6140          * <pre><code>
6141  Roo.data.Record.EDIT
6142  Roo.data.Record.REJECT
6143  Roo.data.Record.COMMIT
6144          * </code></pre>
6145          */
6146         update : true,
6147         /**
6148          * @event clear
6149          * Fires when the data cache has been cleared.
6150          * @param {Store} this
6151          */
6152         clear : true,
6153         /**
6154          * @event beforeload
6155          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6156          * the load action will be canceled.
6157          * @param {Store} this
6158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6159          */
6160         beforeload : true,
6161         /**
6162          * @event beforeloadadd
6163          * Fires after a new set of Records has been loaded.
6164          * @param {Store} this
6165          * @param {Roo.data.Record[]} records The Records that were loaded
6166          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6167          */
6168         beforeloadadd : true,
6169         /**
6170          * @event load
6171          * Fires after a new set of Records has been loaded, before they are added to the store.
6172          * @param {Store} this
6173          * @param {Roo.data.Record[]} records The Records that were loaded
6174          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6175          * @params {Object} return from reader
6176          */
6177         load : true,
6178         /**
6179          * @event loadexception
6180          * Fires if an exception occurs in the Proxy during loading.
6181          * Called with the signature of the Proxy's "loadexception" event.
6182          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6183          * 
6184          * @param {Proxy} 
6185          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6186          * @param {Object} load options 
6187          * @param {Object} jsonData from your request (normally this contains the Exception)
6188          */
6189         loadexception : true
6190     });
6191     
6192     if(this.proxy){
6193         this.proxy = Roo.factory(this.proxy, Roo.data);
6194         this.proxy.xmodule = this.xmodule || false;
6195         this.relayEvents(this.proxy,  ["loadexception"]);
6196     }
6197     this.sortToggle = {};
6198     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6199
6200     Roo.data.Store.superclass.constructor.call(this);
6201
6202     if(this.inlineData){
6203         this.loadData(this.inlineData);
6204         delete this.inlineData;
6205     }
6206 };
6207
6208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6209      /**
6210     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6211     * without a remote query - used by combo/forms at present.
6212     */
6213     
6214     /**
6215     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6216     */
6217     /**
6218     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6219     */
6220     /**
6221     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6222     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6223     */
6224     /**
6225     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6226     * on any HTTP request
6227     */
6228     /**
6229     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6230     */
6231     /**
6232     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6233     */
6234     multiSort: false,
6235     /**
6236     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6237     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6238     */
6239     remoteSort : false,
6240
6241     /**
6242     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6243      * loaded or when a record is removed. (defaults to false).
6244     */
6245     pruneModifiedRecords : false,
6246
6247     // private
6248     lastOptions : null,
6249
6250     /**
6251      * Add Records to the Store and fires the add event.
6252      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6253      */
6254     add : function(records){
6255         records = [].concat(records);
6256         for(var i = 0, len = records.length; i < len; i++){
6257             records[i].join(this);
6258         }
6259         var index = this.data.length;
6260         this.data.addAll(records);
6261         this.fireEvent("add", this, records, index);
6262     },
6263
6264     /**
6265      * Remove a Record from the Store and fires the remove event.
6266      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6267      */
6268     remove : function(record){
6269         var index = this.data.indexOf(record);
6270         this.data.removeAt(index);
6271         if(this.pruneModifiedRecords){
6272             this.modified.remove(record);
6273         }
6274         this.fireEvent("remove", this, record, index);
6275     },
6276
6277     /**
6278      * Remove all Records from the Store and fires the clear event.
6279      */
6280     removeAll : function(){
6281         this.data.clear();
6282         if(this.pruneModifiedRecords){
6283             this.modified = [];
6284         }
6285         this.fireEvent("clear", this);
6286     },
6287
6288     /**
6289      * Inserts Records to the Store at the given index and fires the add event.
6290      * @param {Number} index The start index at which to insert the passed Records.
6291      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6292      */
6293     insert : function(index, records){
6294         records = [].concat(records);
6295         for(var i = 0, len = records.length; i < len; i++){
6296             this.data.insert(index, records[i]);
6297             records[i].join(this);
6298         }
6299         this.fireEvent("add", this, records, index);
6300     },
6301
6302     /**
6303      * Get the index within the cache of the passed Record.
6304      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6305      * @return {Number} The index of the passed Record. Returns -1 if not found.
6306      */
6307     indexOf : function(record){
6308         return this.data.indexOf(record);
6309     },
6310
6311     /**
6312      * Get the index within the cache of the Record with the passed id.
6313      * @param {String} id The id of the Record to find.
6314      * @return {Number} The index of the Record. Returns -1 if not found.
6315      */
6316     indexOfId : function(id){
6317         return this.data.indexOfKey(id);
6318     },
6319
6320     /**
6321      * Get the Record with the specified id.
6322      * @param {String} id The id of the Record to find.
6323      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6324      */
6325     getById : function(id){
6326         return this.data.key(id);
6327     },
6328
6329     /**
6330      * Get the Record at the specified index.
6331      * @param {Number} index The index of the Record to find.
6332      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6333      */
6334     getAt : function(index){
6335         return this.data.itemAt(index);
6336     },
6337
6338     /**
6339      * Returns a range of Records between specified indices.
6340      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6341      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6342      * @return {Roo.data.Record[]} An array of Records
6343      */
6344     getRange : function(start, end){
6345         return this.data.getRange(start, end);
6346     },
6347
6348     // private
6349     storeOptions : function(o){
6350         o = Roo.apply({}, o);
6351         delete o.callback;
6352         delete o.scope;
6353         this.lastOptions = o;
6354     },
6355
6356     /**
6357      * Loads the Record cache from the configured Proxy using the configured Reader.
6358      * <p>
6359      * If using remote paging, then the first load call must specify the <em>start</em>
6360      * and <em>limit</em> properties in the options.params property to establish the initial
6361      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6362      * <p>
6363      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6364      * and this call will return before the new data has been loaded. Perform any post-processing
6365      * in a callback function, or in a "load" event handler.</strong>
6366      * <p>
6367      * @param {Object} options An object containing properties which control loading options:<ul>
6368      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6369      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6370      * passed the following arguments:<ul>
6371      * <li>r : Roo.data.Record[]</li>
6372      * <li>options: Options object from the load call</li>
6373      * <li>success: Boolean success indicator</li></ul></li>
6374      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6375      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6376      * </ul>
6377      */
6378     load : function(options){
6379         options = options || {};
6380         if(this.fireEvent("beforeload", this, options) !== false){
6381             this.storeOptions(options);
6382             var p = Roo.apply(options.params || {}, this.baseParams);
6383             // if meta was not loaded from remote source.. try requesting it.
6384             if (!this.reader.metaFromRemote) {
6385                 p._requestMeta = 1;
6386             }
6387             if(this.sortInfo && this.remoteSort){
6388                 var pn = this.paramNames;
6389                 p[pn["sort"]] = this.sortInfo.field;
6390                 p[pn["dir"]] = this.sortInfo.direction;
6391             }
6392             if (this.multiSort) {
6393                 var pn = this.paramNames;
6394                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6395             }
6396             
6397             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6398         }
6399     },
6400
6401     /**
6402      * Reloads the Record cache from the configured Proxy using the configured Reader and
6403      * the options from the last load operation performed.
6404      * @param {Object} options (optional) An object containing properties which may override the options
6405      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6406      * the most recently used options are reused).
6407      */
6408     reload : function(options){
6409         this.load(Roo.applyIf(options||{}, this.lastOptions));
6410     },
6411
6412     // private
6413     // Called as a callback by the Reader during a load operation.
6414     loadRecords : function(o, options, success){
6415         if(!o || success === false){
6416             if(success !== false){
6417                 this.fireEvent("load", this, [], options, o);
6418             }
6419             if(options.callback){
6420                 options.callback.call(options.scope || this, [], options, false);
6421             }
6422             return;
6423         }
6424         // if data returned failure - throw an exception.
6425         if (o.success === false) {
6426             // show a message if no listener is registered.
6427             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6428                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6429             }
6430             // loadmask wil be hooked into this..
6431             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6432             return;
6433         }
6434         var r = o.records, t = o.totalRecords || r.length;
6435         
6436         this.fireEvent("beforeloadadd", this, r, options, o);
6437         
6438         if(!options || options.add !== true){
6439             if(this.pruneModifiedRecords){
6440                 this.modified = [];
6441             }
6442             for(var i = 0, len = r.length; i < len; i++){
6443                 r[i].join(this);
6444             }
6445             if(this.snapshot){
6446                 this.data = this.snapshot;
6447                 delete this.snapshot;
6448             }
6449             this.data.clear();
6450             this.data.addAll(r);
6451             this.totalLength = t;
6452             this.applySort();
6453             this.fireEvent("datachanged", this);
6454         }else{
6455             this.totalLength = Math.max(t, this.data.length+r.length);
6456             this.add(r);
6457         }
6458         this.fireEvent("load", this, r, options, o);
6459         if(options.callback){
6460             options.callback.call(options.scope || this, r, options, true);
6461         }
6462     },
6463
6464
6465     /**
6466      * Loads data from a passed data block. A Reader which understands the format of the data
6467      * must have been configured in the constructor.
6468      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6469      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6470      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6471      */
6472     loadData : function(o, append){
6473         var r = this.reader.readRecords(o);
6474         this.loadRecords(r, {add: append}, true);
6475     },
6476
6477     /**
6478      * Gets the number of cached records.
6479      * <p>
6480      * <em>If using paging, this may not be the total size of the dataset. If the data object
6481      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6482      * the data set size</em>
6483      */
6484     getCount : function(){
6485         return this.data.length || 0;
6486     },
6487
6488     /**
6489      * Gets the total number of records in the dataset as returned by the server.
6490      * <p>
6491      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6492      * the dataset size</em>
6493      */
6494     getTotalCount : function(){
6495         return this.totalLength || 0;
6496     },
6497
6498     /**
6499      * Returns the sort state of the Store as an object with two properties:
6500      * <pre><code>
6501  field {String} The name of the field by which the Records are sorted
6502  direction {String} The sort order, "ASC" or "DESC"
6503      * </code></pre>
6504      */
6505     getSortState : function(){
6506         return this.sortInfo;
6507     },
6508
6509     // private
6510     applySort : function(){
6511         if(this.sortInfo && !this.remoteSort){
6512             var s = this.sortInfo, f = s.field;
6513             var st = this.fields.get(f).sortType;
6514             var fn = function(r1, r2){
6515                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6516                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6517             };
6518             this.data.sort(s.direction, fn);
6519             if(this.snapshot && this.snapshot != this.data){
6520                 this.snapshot.sort(s.direction, fn);
6521             }
6522         }
6523     },
6524
6525     /**
6526      * Sets the default sort column and order to be used by the next load operation.
6527      * @param {String} fieldName The name of the field to sort by.
6528      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6529      */
6530     setDefaultSort : function(field, dir){
6531         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6532     },
6533
6534     /**
6535      * Sort the Records.
6536      * If remote sorting is used, the sort is performed on the server, and the cache is
6537      * reloaded. If local sorting is used, the cache is sorted internally.
6538      * @param {String} fieldName The name of the field to sort by.
6539      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6540      */
6541     sort : function(fieldName, dir){
6542         var f = this.fields.get(fieldName);
6543         if(!dir){
6544             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6545             
6546             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6547                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6548             }else{
6549                 dir = f.sortDir;
6550             }
6551         }
6552         this.sortToggle[f.name] = dir;
6553         this.sortInfo = {field: f.name, direction: dir};
6554         if(!this.remoteSort){
6555             this.applySort();
6556             this.fireEvent("datachanged", this);
6557         }else{
6558             this.load(this.lastOptions);
6559         }
6560     },
6561
6562     /**
6563      * Calls the specified function for each of the Records in the cache.
6564      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6565      * Returning <em>false</em> aborts and exits the iteration.
6566      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6567      */
6568     each : function(fn, scope){
6569         this.data.each(fn, scope);
6570     },
6571
6572     /**
6573      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6574      * (e.g., during paging).
6575      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6576      */
6577     getModifiedRecords : function(){
6578         return this.modified;
6579     },
6580
6581     // private
6582     createFilterFn : function(property, value, anyMatch){
6583         if(!value.exec){ // not a regex
6584             value = String(value);
6585             if(value.length == 0){
6586                 return false;
6587             }
6588             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6589         }
6590         return function(r){
6591             return value.test(r.data[property]);
6592         };
6593     },
6594
6595     /**
6596      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6597      * @param {String} property A field on your records
6598      * @param {Number} start The record index to start at (defaults to 0)
6599      * @param {Number} end The last record index to include (defaults to length - 1)
6600      * @return {Number} The sum
6601      */
6602     sum : function(property, start, end){
6603         var rs = this.data.items, v = 0;
6604         start = start || 0;
6605         end = (end || end === 0) ? end : rs.length-1;
6606
6607         for(var i = start; i <= end; i++){
6608             v += (rs[i].data[property] || 0);
6609         }
6610         return v;
6611     },
6612
6613     /**
6614      * Filter the records by a specified property.
6615      * @param {String} field A field on your records
6616      * @param {String/RegExp} value Either a string that the field
6617      * should start with or a RegExp to test against the field
6618      * @param {Boolean} anyMatch True to match any part not just the beginning
6619      */
6620     filter : function(property, value, anyMatch){
6621         var fn = this.createFilterFn(property, value, anyMatch);
6622         return fn ? this.filterBy(fn) : this.clearFilter();
6623     },
6624
6625     /**
6626      * Filter by a function. The specified function will be called with each
6627      * record in this data source. If the function returns true the record is included,
6628      * otherwise it is filtered.
6629      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6630      * @param {Object} scope (optional) The scope of the function (defaults to this)
6631      */
6632     filterBy : function(fn, scope){
6633         this.snapshot = this.snapshot || this.data;
6634         this.data = this.queryBy(fn, scope||this);
6635         this.fireEvent("datachanged", this);
6636     },
6637
6638     /**
6639      * Query the records by a specified property.
6640      * @param {String} field A field on your records
6641      * @param {String/RegExp} value Either a string that the field
6642      * should start with or a RegExp to test against the field
6643      * @param {Boolean} anyMatch True to match any part not just the beginning
6644      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6645      */
6646     query : function(property, value, anyMatch){
6647         var fn = this.createFilterFn(property, value, anyMatch);
6648         return fn ? this.queryBy(fn) : this.data.clone();
6649     },
6650
6651     /**
6652      * Query by a function. The specified function will be called with each
6653      * record in this data source. If the function returns true the record is included
6654      * in the results.
6655      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6656      * @param {Object} scope (optional) The scope of the function (defaults to this)
6657       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6658      **/
6659     queryBy : function(fn, scope){
6660         var data = this.snapshot || this.data;
6661         return data.filterBy(fn, scope||this);
6662     },
6663
6664     /**
6665      * Collects unique values for a particular dataIndex from this store.
6666      * @param {String} dataIndex The property to collect
6667      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6668      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6669      * @return {Array} An array of the unique values
6670      **/
6671     collect : function(dataIndex, allowNull, bypassFilter){
6672         var d = (bypassFilter === true && this.snapshot) ?
6673                 this.snapshot.items : this.data.items;
6674         var v, sv, r = [], l = {};
6675         for(var i = 0, len = d.length; i < len; i++){
6676             v = d[i].data[dataIndex];
6677             sv = String(v);
6678             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6679                 l[sv] = true;
6680                 r[r.length] = v;
6681             }
6682         }
6683         return r;
6684     },
6685
6686     /**
6687      * Revert to a view of the Record cache with no filtering applied.
6688      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6689      */
6690     clearFilter : function(suppressEvent){
6691         if(this.snapshot && this.snapshot != this.data){
6692             this.data = this.snapshot;
6693             delete this.snapshot;
6694             if(suppressEvent !== true){
6695                 this.fireEvent("datachanged", this);
6696             }
6697         }
6698     },
6699
6700     // private
6701     afterEdit : function(record){
6702         if(this.modified.indexOf(record) == -1){
6703             this.modified.push(record);
6704         }
6705         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6706     },
6707     
6708     // private
6709     afterReject : function(record){
6710         this.modified.remove(record);
6711         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6712     },
6713
6714     // private
6715     afterCommit : function(record){
6716         this.modified.remove(record);
6717         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6718     },
6719
6720     /**
6721      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6722      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6723      */
6724     commitChanges : function(){
6725         var m = this.modified.slice(0);
6726         this.modified = [];
6727         for(var i = 0, len = m.length; i < len; i++){
6728             m[i].commit();
6729         }
6730     },
6731
6732     /**
6733      * Cancel outstanding changes on all changed records.
6734      */
6735     rejectChanges : function(){
6736         var m = this.modified.slice(0);
6737         this.modified = [];
6738         for(var i = 0, len = m.length; i < len; i++){
6739             m[i].reject();
6740         }
6741     },
6742
6743     onMetaChange : function(meta, rtype, o){
6744         this.recordType = rtype;
6745         this.fields = rtype.prototype.fields;
6746         delete this.snapshot;
6747         this.sortInfo = meta.sortInfo || this.sortInfo;
6748         this.modified = [];
6749         this.fireEvent('metachange', this, this.reader.meta);
6750     },
6751     
6752     moveIndex : function(data, type)
6753     {
6754         var index = this.indexOf(data);
6755         
6756         var newIndex = index + type;
6757         
6758         this.remove(data);
6759         
6760         this.insert(newIndex, data);
6761         
6762     }
6763 });/*
6764  * Based on:
6765  * Ext JS Library 1.1.1
6766  * Copyright(c) 2006-2007, Ext JS, LLC.
6767  *
6768  * Originally Released Under LGPL - original licence link has changed is not relivant.
6769  *
6770  * Fork - LGPL
6771  * <script type="text/javascript">
6772  */
6773
6774 /**
6775  * @class Roo.data.SimpleStore
6776  * @extends Roo.data.Store
6777  * Small helper class to make creating Stores from Array data easier.
6778  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6779  * @cfg {Array} fields An array of field definition objects, or field name strings.
6780  * @cfg {Array} data The multi-dimensional array of data
6781  * @constructor
6782  * @param {Object} config
6783  */
6784 Roo.data.SimpleStore = function(config){
6785     Roo.data.SimpleStore.superclass.constructor.call(this, {
6786         isLocal : true,
6787         reader: new Roo.data.ArrayReader({
6788                 id: config.id
6789             },
6790             Roo.data.Record.create(config.fields)
6791         ),
6792         proxy : new Roo.data.MemoryProxy(config.data)
6793     });
6794     this.load();
6795 };
6796 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6797  * Based on:
6798  * Ext JS Library 1.1.1
6799  * Copyright(c) 2006-2007, Ext JS, LLC.
6800  *
6801  * Originally Released Under LGPL - original licence link has changed is not relivant.
6802  *
6803  * Fork - LGPL
6804  * <script type="text/javascript">
6805  */
6806
6807 /**
6808 /**
6809  * @extends Roo.data.Store
6810  * @class Roo.data.JsonStore
6811  * Small helper class to make creating Stores for JSON data easier. <br/>
6812 <pre><code>
6813 var store = new Roo.data.JsonStore({
6814     url: 'get-images.php',
6815     root: 'images',
6816     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6817 });
6818 </code></pre>
6819  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6820  * JsonReader and HttpProxy (unless inline data is provided).</b>
6821  * @cfg {Array} fields An array of field definition objects, or field name strings.
6822  * @constructor
6823  * @param {Object} config
6824  */
6825 Roo.data.JsonStore = function(c){
6826     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6827         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6828         reader: new Roo.data.JsonReader(c, c.fields)
6829     }));
6830 };
6831 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6832  * Based on:
6833  * Ext JS Library 1.1.1
6834  * Copyright(c) 2006-2007, Ext JS, LLC.
6835  *
6836  * Originally Released Under LGPL - original licence link has changed is not relivant.
6837  *
6838  * Fork - LGPL
6839  * <script type="text/javascript">
6840  */
6841
6842  
6843 Roo.data.Field = function(config){
6844     if(typeof config == "string"){
6845         config = {name: config};
6846     }
6847     Roo.apply(this, config);
6848     
6849     if(!this.type){
6850         this.type = "auto";
6851     }
6852     
6853     var st = Roo.data.SortTypes;
6854     // named sortTypes are supported, here we look them up
6855     if(typeof this.sortType == "string"){
6856         this.sortType = st[this.sortType];
6857     }
6858     
6859     // set default sortType for strings and dates
6860     if(!this.sortType){
6861         switch(this.type){
6862             case "string":
6863                 this.sortType = st.asUCString;
6864                 break;
6865             case "date":
6866                 this.sortType = st.asDate;
6867                 break;
6868             default:
6869                 this.sortType = st.none;
6870         }
6871     }
6872
6873     // define once
6874     var stripRe = /[\$,%]/g;
6875
6876     // prebuilt conversion function for this field, instead of
6877     // switching every time we're reading a value
6878     if(!this.convert){
6879         var cv, dateFormat = this.dateFormat;
6880         switch(this.type){
6881             case "":
6882             case "auto":
6883             case undefined:
6884                 cv = function(v){ return v; };
6885                 break;
6886             case "string":
6887                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6888                 break;
6889             case "int":
6890                 cv = function(v){
6891                     return v !== undefined && v !== null && v !== '' ?
6892                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6893                     };
6894                 break;
6895             case "float":
6896                 cv = function(v){
6897                     return v !== undefined && v !== null && v !== '' ?
6898                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6899                     };
6900                 break;
6901             case "bool":
6902             case "boolean":
6903                 cv = function(v){ return v === true || v === "true" || v == 1; };
6904                 break;
6905             case "date":
6906                 cv = function(v){
6907                     if(!v){
6908                         return '';
6909                     }
6910                     if(v instanceof Date){
6911                         return v;
6912                     }
6913                     if(dateFormat){
6914                         if(dateFormat == "timestamp"){
6915                             return new Date(v*1000);
6916                         }
6917                         return Date.parseDate(v, dateFormat);
6918                     }
6919                     var parsed = Date.parse(v);
6920                     return parsed ? new Date(parsed) : null;
6921                 };
6922              break;
6923             
6924         }
6925         this.convert = cv;
6926     }
6927 };
6928
6929 Roo.data.Field.prototype = {
6930     dateFormat: null,
6931     defaultValue: "",
6932     mapping: null,
6933     sortType : null,
6934     sortDir : "ASC"
6935 };/*
6936  * Based on:
6937  * Ext JS Library 1.1.1
6938  * Copyright(c) 2006-2007, Ext JS, LLC.
6939  *
6940  * Originally Released Under LGPL - original licence link has changed is not relivant.
6941  *
6942  * Fork - LGPL
6943  * <script type="text/javascript">
6944  */
6945  
6946 // Base class for reading structured data from a data source.  This class is intended to be
6947 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6948
6949 /**
6950  * @class Roo.data.DataReader
6951  * Base class for reading structured data from a data source.  This class is intended to be
6952  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6953  */
6954
6955 Roo.data.DataReader = function(meta, recordType){
6956     
6957     this.meta = meta;
6958     
6959     this.recordType = recordType instanceof Array ? 
6960         Roo.data.Record.create(recordType) : recordType;
6961 };
6962
6963 Roo.data.DataReader.prototype = {
6964      /**
6965      * Create an empty record
6966      * @param {Object} data (optional) - overlay some values
6967      * @return {Roo.data.Record} record created.
6968      */
6969     newRow :  function(d) {
6970         var da =  {};
6971         this.recordType.prototype.fields.each(function(c) {
6972             switch( c.type) {
6973                 case 'int' : da[c.name] = 0; break;
6974                 case 'date' : da[c.name] = new Date(); break;
6975                 case 'float' : da[c.name] = 0.0; break;
6976                 case 'boolean' : da[c.name] = false; break;
6977                 default : da[c.name] = ""; break;
6978             }
6979             
6980         });
6981         return new this.recordType(Roo.apply(da, d));
6982     }
6983     
6984 };/*
6985  * Based on:
6986  * Ext JS Library 1.1.1
6987  * Copyright(c) 2006-2007, Ext JS, LLC.
6988  *
6989  * Originally Released Under LGPL - original licence link has changed is not relivant.
6990  *
6991  * Fork - LGPL
6992  * <script type="text/javascript">
6993  */
6994
6995 /**
6996  * @class Roo.data.DataProxy
6997  * @extends Roo.data.Observable
6998  * This class is an abstract base class for implementations which provide retrieval of
6999  * unformatted data objects.<br>
7000  * <p>
7001  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7002  * (of the appropriate type which knows how to parse the data object) to provide a block of
7003  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7004  * <p>
7005  * Custom implementations must implement the load method as described in
7006  * {@link Roo.data.HttpProxy#load}.
7007  */
7008 Roo.data.DataProxy = function(){
7009     this.addEvents({
7010         /**
7011          * @event beforeload
7012          * Fires before a network request is made to retrieve a data object.
7013          * @param {Object} This DataProxy object.
7014          * @param {Object} params The params parameter to the load function.
7015          */
7016         beforeload : true,
7017         /**
7018          * @event load
7019          * Fires before the load method's callback is called.
7020          * @param {Object} This DataProxy object.
7021          * @param {Object} o The data object.
7022          * @param {Object} arg The callback argument object passed to the load function.
7023          */
7024         load : true,
7025         /**
7026          * @event loadexception
7027          * Fires if an Exception occurs during data retrieval.
7028          * @param {Object} This DataProxy object.
7029          * @param {Object} o The data object.
7030          * @param {Object} arg The callback argument object passed to the load function.
7031          * @param {Object} e The Exception.
7032          */
7033         loadexception : true
7034     });
7035     Roo.data.DataProxy.superclass.constructor.call(this);
7036 };
7037
7038 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7039
7040     /**
7041      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7042      */
7043 /*
7044  * Based on:
7045  * Ext JS Library 1.1.1
7046  * Copyright(c) 2006-2007, Ext JS, LLC.
7047  *
7048  * Originally Released Under LGPL - original licence link has changed is not relivant.
7049  *
7050  * Fork - LGPL
7051  * <script type="text/javascript">
7052  */
7053 /**
7054  * @class Roo.data.MemoryProxy
7055  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7056  * to the Reader when its load method is called.
7057  * @constructor
7058  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7059  */
7060 Roo.data.MemoryProxy = function(data){
7061     if (data.data) {
7062         data = data.data;
7063     }
7064     Roo.data.MemoryProxy.superclass.constructor.call(this);
7065     this.data = data;
7066 };
7067
7068 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7069     /**
7070      * Load data from the requested source (in this case an in-memory
7071      * data object passed to the constructor), read the data object into
7072      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7073      * process that block using the passed callback.
7074      * @param {Object} params This parameter is not used by the MemoryProxy class.
7075      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7076      * object into a block of Roo.data.Records.
7077      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7078      * The function must be passed <ul>
7079      * <li>The Record block object</li>
7080      * <li>The "arg" argument from the load function</li>
7081      * <li>A boolean success indicator</li>
7082      * </ul>
7083      * @param {Object} scope The scope in which to call the callback
7084      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7085      */
7086     load : function(params, reader, callback, scope, arg){
7087         params = params || {};
7088         var result;
7089         try {
7090             result = reader.readRecords(this.data);
7091         }catch(e){
7092             this.fireEvent("loadexception", this, arg, null, e);
7093             callback.call(scope, null, arg, false);
7094             return;
7095         }
7096         callback.call(scope, result, arg, true);
7097     },
7098     
7099     // private
7100     update : function(params, records){
7101         
7102     }
7103 });/*
7104  * Based on:
7105  * Ext JS Library 1.1.1
7106  * Copyright(c) 2006-2007, Ext JS, LLC.
7107  *
7108  * Originally Released Under LGPL - original licence link has changed is not relivant.
7109  *
7110  * Fork - LGPL
7111  * <script type="text/javascript">
7112  */
7113 /**
7114  * @class Roo.data.HttpProxy
7115  * @extends Roo.data.DataProxy
7116  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7117  * configured to reference a certain URL.<br><br>
7118  * <p>
7119  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7120  * from which the running page was served.<br><br>
7121  * <p>
7122  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7123  * <p>
7124  * Be aware that to enable the browser to parse an XML document, the server must set
7125  * the Content-Type header in the HTTP response to "text/xml".
7126  * @constructor
7127  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7128  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7129  * will be used to make the request.
7130  */
7131 Roo.data.HttpProxy = function(conn){
7132     Roo.data.HttpProxy.superclass.constructor.call(this);
7133     // is conn a conn config or a real conn?
7134     this.conn = conn;
7135     this.useAjax = !conn || !conn.events;
7136   
7137 };
7138
7139 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7140     // thse are take from connection...
7141     
7142     /**
7143      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7144      */
7145     /**
7146      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7147      * extra parameters to each request made by this object. (defaults to undefined)
7148      */
7149     /**
7150      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7151      *  to each request made by this object. (defaults to undefined)
7152      */
7153     /**
7154      * @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)
7155      */
7156     /**
7157      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7158      */
7159      /**
7160      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7161      * @type Boolean
7162      */
7163   
7164
7165     /**
7166      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7167      * @type Boolean
7168      */
7169     /**
7170      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7171      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7172      * a finer-grained basis than the DataProxy events.
7173      */
7174     getConnection : function(){
7175         return this.useAjax ? Roo.Ajax : this.conn;
7176     },
7177
7178     /**
7179      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7180      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7181      * process that block using the passed callback.
7182      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7183      * for the request to the remote server.
7184      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7185      * object into a block of Roo.data.Records.
7186      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7187      * The function must be passed <ul>
7188      * <li>The Record block object</li>
7189      * <li>The "arg" argument from the load function</li>
7190      * <li>A boolean success indicator</li>
7191      * </ul>
7192      * @param {Object} scope The scope in which to call the callback
7193      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7194      */
7195     load : function(params, reader, callback, scope, arg){
7196         if(this.fireEvent("beforeload", this, params) !== false){
7197             var  o = {
7198                 params : params || {},
7199                 request: {
7200                     callback : callback,
7201                     scope : scope,
7202                     arg : arg
7203                 },
7204                 reader: reader,
7205                 callback : this.loadResponse,
7206                 scope: this
7207             };
7208             if(this.useAjax){
7209                 Roo.applyIf(o, this.conn);
7210                 if(this.activeRequest){
7211                     Roo.Ajax.abort(this.activeRequest);
7212                 }
7213                 this.activeRequest = Roo.Ajax.request(o);
7214             }else{
7215                 this.conn.request(o);
7216             }
7217         }else{
7218             callback.call(scope||this, null, arg, false);
7219         }
7220     },
7221
7222     // private
7223     loadResponse : function(o, success, response){
7224         delete this.activeRequest;
7225         if(!success){
7226             this.fireEvent("loadexception", this, o, response);
7227             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7228             return;
7229         }
7230         var result;
7231         try {
7232             result = o.reader.read(response);
7233         }catch(e){
7234             this.fireEvent("loadexception", this, o, response, e);
7235             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7236             return;
7237         }
7238         
7239         this.fireEvent("load", this, o, o.request.arg);
7240         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7241     },
7242
7243     // private
7244     update : function(dataSet){
7245
7246     },
7247
7248     // private
7249     updateResponse : function(dataSet){
7250
7251     }
7252 });/*
7253  * Based on:
7254  * Ext JS Library 1.1.1
7255  * Copyright(c) 2006-2007, Ext JS, LLC.
7256  *
7257  * Originally Released Under LGPL - original licence link has changed is not relivant.
7258  *
7259  * Fork - LGPL
7260  * <script type="text/javascript">
7261  */
7262
7263 /**
7264  * @class Roo.data.ScriptTagProxy
7265  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7266  * other than the originating domain of the running page.<br><br>
7267  * <p>
7268  * <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
7269  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7270  * <p>
7271  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7272  * source code that is used as the source inside a &lt;script> tag.<br><br>
7273  * <p>
7274  * In order for the browser to process the returned data, the server must wrap the data object
7275  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7276  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7277  * depending on whether the callback name was passed:
7278  * <p>
7279  * <pre><code>
7280 boolean scriptTag = false;
7281 String cb = request.getParameter("callback");
7282 if (cb != null) {
7283     scriptTag = true;
7284     response.setContentType("text/javascript");
7285 } else {
7286     response.setContentType("application/x-json");
7287 }
7288 Writer out = response.getWriter();
7289 if (scriptTag) {
7290     out.write(cb + "(");
7291 }
7292 out.print(dataBlock.toJsonString());
7293 if (scriptTag) {
7294     out.write(");");
7295 }
7296 </pre></code>
7297  *
7298  * @constructor
7299  * @param {Object} config A configuration object.
7300  */
7301 Roo.data.ScriptTagProxy = function(config){
7302     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7303     Roo.apply(this, config);
7304     this.head = document.getElementsByTagName("head")[0];
7305 };
7306
7307 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7308
7309 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7310     /**
7311      * @cfg {String} url The URL from which to request the data object.
7312      */
7313     /**
7314      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7315      */
7316     timeout : 30000,
7317     /**
7318      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7319      * the server the name of the callback function set up by the load call to process the returned data object.
7320      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7321      * javascript output which calls this named function passing the data object as its only parameter.
7322      */
7323     callbackParam : "callback",
7324     /**
7325      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7326      * name to the request.
7327      */
7328     nocache : true,
7329
7330     /**
7331      * Load data from the configured URL, read the data object into
7332      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7333      * process that block using the passed callback.
7334      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7335      * for the request to the remote server.
7336      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7337      * object into a block of Roo.data.Records.
7338      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7339      * The function must be passed <ul>
7340      * <li>The Record block object</li>
7341      * <li>The "arg" argument from the load function</li>
7342      * <li>A boolean success indicator</li>
7343      * </ul>
7344      * @param {Object} scope The scope in which to call the callback
7345      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7346      */
7347     load : function(params, reader, callback, scope, arg){
7348         if(this.fireEvent("beforeload", this, params) !== false){
7349
7350             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7351
7352             var url = this.url;
7353             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7354             if(this.nocache){
7355                 url += "&_dc=" + (new Date().getTime());
7356             }
7357             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7358             var trans = {
7359                 id : transId,
7360                 cb : "stcCallback"+transId,
7361                 scriptId : "stcScript"+transId,
7362                 params : params,
7363                 arg : arg,
7364                 url : url,
7365                 callback : callback,
7366                 scope : scope,
7367                 reader : reader
7368             };
7369             var conn = this;
7370
7371             window[trans.cb] = function(o){
7372                 conn.handleResponse(o, trans);
7373             };
7374
7375             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7376
7377             if(this.autoAbort !== false){
7378                 this.abort();
7379             }
7380
7381             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7382
7383             var script = document.createElement("script");
7384             script.setAttribute("src", url);
7385             script.setAttribute("type", "text/javascript");
7386             script.setAttribute("id", trans.scriptId);
7387             this.head.appendChild(script);
7388
7389             this.trans = trans;
7390         }else{
7391             callback.call(scope||this, null, arg, false);
7392         }
7393     },
7394
7395     // private
7396     isLoading : function(){
7397         return this.trans ? true : false;
7398     },
7399
7400     /**
7401      * Abort the current server request.
7402      */
7403     abort : function(){
7404         if(this.isLoading()){
7405             this.destroyTrans(this.trans);
7406         }
7407     },
7408
7409     // private
7410     destroyTrans : function(trans, isLoaded){
7411         this.head.removeChild(document.getElementById(trans.scriptId));
7412         clearTimeout(trans.timeoutId);
7413         if(isLoaded){
7414             window[trans.cb] = undefined;
7415             try{
7416                 delete window[trans.cb];
7417             }catch(e){}
7418         }else{
7419             // if hasn't been loaded, wait for load to remove it to prevent script error
7420             window[trans.cb] = function(){
7421                 window[trans.cb] = undefined;
7422                 try{
7423                     delete window[trans.cb];
7424                 }catch(e){}
7425             };
7426         }
7427     },
7428
7429     // private
7430     handleResponse : function(o, trans){
7431         this.trans = false;
7432         this.destroyTrans(trans, true);
7433         var result;
7434         try {
7435             result = trans.reader.readRecords(o);
7436         }catch(e){
7437             this.fireEvent("loadexception", this, o, trans.arg, e);
7438             trans.callback.call(trans.scope||window, null, trans.arg, false);
7439             return;
7440         }
7441         this.fireEvent("load", this, o, trans.arg);
7442         trans.callback.call(trans.scope||window, result, trans.arg, true);
7443     },
7444
7445     // private
7446     handleFailure : function(trans){
7447         this.trans = false;
7448         this.destroyTrans(trans, false);
7449         this.fireEvent("loadexception", this, null, trans.arg);
7450         trans.callback.call(trans.scope||window, null, trans.arg, false);
7451     }
7452 });/*
7453  * Based on:
7454  * Ext JS Library 1.1.1
7455  * Copyright(c) 2006-2007, Ext JS, LLC.
7456  *
7457  * Originally Released Under LGPL - original licence link has changed is not relivant.
7458  *
7459  * Fork - LGPL
7460  * <script type="text/javascript">
7461  */
7462
7463 /**
7464  * @class Roo.data.JsonReader
7465  * @extends Roo.data.DataReader
7466  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7467  * based on mappings in a provided Roo.data.Record constructor.
7468  * 
7469  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7470  * in the reply previously. 
7471  * 
7472  * <p>
7473  * Example code:
7474  * <pre><code>
7475 var RecordDef = Roo.data.Record.create([
7476     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7477     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7478 ]);
7479 var myReader = new Roo.data.JsonReader({
7480     totalProperty: "results",    // The property which contains the total dataset size (optional)
7481     root: "rows",                // The property which contains an Array of row objects
7482     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7483 }, RecordDef);
7484 </code></pre>
7485  * <p>
7486  * This would consume a JSON file like this:
7487  * <pre><code>
7488 { 'results': 2, 'rows': [
7489     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7490     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7491 }
7492 </code></pre>
7493  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7494  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7495  * paged from the remote server.
7496  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7497  * @cfg {String} root name of the property which contains the Array of row objects.
7498  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7499  * @constructor
7500  * Create a new JsonReader
7501  * @param {Object} meta Metadata configuration options
7502  * @param {Object} recordType Either an Array of field definition objects,
7503  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7504  */
7505 Roo.data.JsonReader = function(meta, recordType){
7506     
7507     meta = meta || {};
7508     // set some defaults:
7509     Roo.applyIf(meta, {
7510         totalProperty: 'total',
7511         successProperty : 'success',
7512         root : 'data',
7513         id : 'id'
7514     });
7515     
7516     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7517 };
7518 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7519     
7520     /**
7521      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7522      * Used by Store query builder to append _requestMeta to params.
7523      * 
7524      */
7525     metaFromRemote : false,
7526     /**
7527      * This method is only used by a DataProxy which has retrieved data from a remote server.
7528      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7529      * @return {Object} data A data block which is used by an Roo.data.Store object as
7530      * a cache of Roo.data.Records.
7531      */
7532     read : function(response){
7533         var json = response.responseText;
7534        
7535         var o = /* eval:var:o */ eval("("+json+")");
7536         if(!o) {
7537             throw {message: "JsonReader.read: Json object not found"};
7538         }
7539         
7540         if(o.metaData){
7541             
7542             delete this.ef;
7543             this.metaFromRemote = true;
7544             this.meta = o.metaData;
7545             this.recordType = Roo.data.Record.create(o.metaData.fields);
7546             this.onMetaChange(this.meta, this.recordType, o);
7547         }
7548         return this.readRecords(o);
7549     },
7550
7551     // private function a store will implement
7552     onMetaChange : function(meta, recordType, o){
7553
7554     },
7555
7556     /**
7557          * @ignore
7558          */
7559     simpleAccess: function(obj, subsc) {
7560         return obj[subsc];
7561     },
7562
7563         /**
7564          * @ignore
7565          */
7566     getJsonAccessor: function(){
7567         var re = /[\[\.]/;
7568         return function(expr) {
7569             try {
7570                 return(re.test(expr))
7571                     ? new Function("obj", "return obj." + expr)
7572                     : function(obj){
7573                         return obj[expr];
7574                     };
7575             } catch(e){}
7576             return Roo.emptyFn;
7577         };
7578     }(),
7579
7580     /**
7581      * Create a data block containing Roo.data.Records from an XML document.
7582      * @param {Object} o An object which contains an Array of row objects in the property specified
7583      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7584      * which contains the total size of the dataset.
7585      * @return {Object} data A data block which is used by an Roo.data.Store object as
7586      * a cache of Roo.data.Records.
7587      */
7588     readRecords : function(o){
7589         /**
7590          * After any data loads, the raw JSON data is available for further custom processing.
7591          * @type Object
7592          */
7593         this.o = o;
7594         var s = this.meta, Record = this.recordType,
7595             f = Record.prototype.fields, fi = f.items, fl = f.length;
7596
7597 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7598         if (!this.ef) {
7599             if(s.totalProperty) {
7600                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7601                 }
7602                 if(s.successProperty) {
7603                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7604                 }
7605                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7606                 if (s.id) {
7607                         var g = this.getJsonAccessor(s.id);
7608                         this.getId = function(rec) {
7609                                 var r = g(rec);
7610                                 return (r === undefined || r === "") ? null : r;
7611                         };
7612                 } else {
7613                         this.getId = function(){return null;};
7614                 }
7615             this.ef = [];
7616             for(var jj = 0; jj < fl; jj++){
7617                 f = fi[jj];
7618                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7619                 this.ef[jj] = this.getJsonAccessor(map);
7620             }
7621         }
7622
7623         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7624         if(s.totalProperty){
7625             var vt = parseInt(this.getTotal(o), 10);
7626             if(!isNaN(vt)){
7627                 totalRecords = vt;
7628             }
7629         }
7630         if(s.successProperty){
7631             var vs = this.getSuccess(o);
7632             if(vs === false || vs === 'false'){
7633                 success = false;
7634             }
7635         }
7636         var records = [];
7637             for(var i = 0; i < c; i++){
7638                     var n = root[i];
7639                 var values = {};
7640                 var id = this.getId(n);
7641                 for(var j = 0; j < fl; j++){
7642                     f = fi[j];
7643                 var v = this.ef[j](n);
7644                 if (!f.convert) {
7645                     Roo.log('missing convert for ' + f.name);
7646                     Roo.log(f);
7647                     continue;
7648                 }
7649                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7650                 }
7651                 var record = new Record(values, id);
7652                 record.json = n;
7653                 records[i] = record;
7654             }
7655             return {
7656             raw : o,
7657                 success : success,
7658                 records : records,
7659                 totalRecords : totalRecords
7660             };
7661     }
7662 });/*
7663  * Based on:
7664  * Ext JS Library 1.1.1
7665  * Copyright(c) 2006-2007, Ext JS, LLC.
7666  *
7667  * Originally Released Under LGPL - original licence link has changed is not relivant.
7668  *
7669  * Fork - LGPL
7670  * <script type="text/javascript">
7671  */
7672
7673 /**
7674  * @class Roo.data.ArrayReader
7675  * @extends Roo.data.DataReader
7676  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7677  * Each element of that Array represents a row of data fields. The
7678  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7679  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7680  * <p>
7681  * Example code:.
7682  * <pre><code>
7683 var RecordDef = Roo.data.Record.create([
7684     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7685     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7686 ]);
7687 var myReader = new Roo.data.ArrayReader({
7688     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7689 }, RecordDef);
7690 </code></pre>
7691  * <p>
7692  * This would consume an Array like this:
7693  * <pre><code>
7694 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7695   </code></pre>
7696  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7697  * @constructor
7698  * Create a new JsonReader
7699  * @param {Object} meta Metadata configuration options.
7700  * @param {Object} recordType Either an Array of field definition objects
7701  * as specified to {@link Roo.data.Record#create},
7702  * or an {@link Roo.data.Record} object
7703  * created using {@link Roo.data.Record#create}.
7704  */
7705 Roo.data.ArrayReader = function(meta, recordType){
7706     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7707 };
7708
7709 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7710     /**
7711      * Create a data block containing Roo.data.Records from an XML document.
7712      * @param {Object} o An Array of row objects which represents the dataset.
7713      * @return {Object} data A data block which is used by an Roo.data.Store object as
7714      * a cache of Roo.data.Records.
7715      */
7716     readRecords : function(o){
7717         var sid = this.meta ? this.meta.id : null;
7718         var recordType = this.recordType, fields = recordType.prototype.fields;
7719         var records = [];
7720         var root = o;
7721             for(var i = 0; i < root.length; i++){
7722                     var n = root[i];
7723                 var values = {};
7724                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7725                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7726                 var f = fields.items[j];
7727                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7728                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7729                 v = f.convert(v);
7730                 values[f.name] = v;
7731             }
7732                 var record = new recordType(values, id);
7733                 record.json = n;
7734                 records[records.length] = record;
7735             }
7736             return {
7737                 records : records,
7738                 totalRecords : records.length
7739             };
7740     }
7741 });/*
7742  * - LGPL
7743  * * 
7744  */
7745
7746 /**
7747  * @class Roo.bootstrap.ComboBox
7748  * @extends Roo.bootstrap.TriggerField
7749  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7750  * @cfg {Boolean} append (true|false) default false
7751  * @constructor
7752  * Create a new ComboBox.
7753  * @param {Object} config Configuration options
7754  */
7755 Roo.bootstrap.ComboBox = function(config){
7756     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7757     this.addEvents({
7758         /**
7759          * @event expand
7760          * Fires when the dropdown list is expanded
7761              * @param {Roo.bootstrap.ComboBox} combo This combo box
7762              */
7763         'expand' : true,
7764         /**
7765          * @event collapse
7766          * Fires when the dropdown list is collapsed
7767              * @param {Roo.bootstrap.ComboBox} combo This combo box
7768              */
7769         'collapse' : true,
7770         /**
7771          * @event beforeselect
7772          * Fires before a list item is selected. Return false to cancel the selection.
7773              * @param {Roo.bootstrap.ComboBox} combo This combo box
7774              * @param {Roo.data.Record} record The data record returned from the underlying store
7775              * @param {Number} index The index of the selected item in the dropdown list
7776              */
7777         'beforeselect' : true,
7778         /**
7779          * @event select
7780          * Fires when a list item is selected
7781              * @param {Roo.bootstrap.ComboBox} combo This combo box
7782              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7783              * @param {Number} index The index of the selected item in the dropdown list
7784              */
7785         'select' : true,
7786         /**
7787          * @event beforequery
7788          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7789          * The event object passed has these properties:
7790              * @param {Roo.bootstrap.ComboBox} combo This combo box
7791              * @param {String} query The query
7792              * @param {Boolean} forceAll true to force "all" query
7793              * @param {Boolean} cancel true to cancel the query
7794              * @param {Object} e The query event object
7795              */
7796         'beforequery': true,
7797          /**
7798          * @event add
7799          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7800              * @param {Roo.bootstrap.ComboBox} combo This combo box
7801              */
7802         'add' : true,
7803         /**
7804          * @event edit
7805          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7806              * @param {Roo.bootstrap.ComboBox} combo This combo box
7807              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7808              */
7809         'edit' : true,
7810         /**
7811          * @event remove
7812          * Fires when the remove value from the combobox array
7813              * @param {Roo.bootstrap.ComboBox} combo This combo box
7814              */
7815         'remove' : true
7816         
7817     });
7818     
7819     
7820     this.selectedIndex = -1;
7821     if(this.mode == 'local'){
7822         if(config.queryDelay === undefined){
7823             this.queryDelay = 10;
7824         }
7825         if(config.minChars === undefined){
7826             this.minChars = 0;
7827         }
7828     }
7829 };
7830
7831 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7832      
7833     /**
7834      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7835      * rendering into an Roo.Editor, defaults to false)
7836      */
7837     /**
7838      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7839      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7840      */
7841     /**
7842      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7843      */
7844     /**
7845      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7846      * the dropdown list (defaults to undefined, with no header element)
7847      */
7848
7849      /**
7850      * @cfg {String/Roo.Template} tpl The template to use to render the output
7851      */
7852      
7853      /**
7854      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7855      */
7856     listWidth: undefined,
7857     /**
7858      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7859      * mode = 'remote' or 'text' if mode = 'local')
7860      */
7861     displayField: undefined,
7862     /**
7863      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7864      * mode = 'remote' or 'value' if mode = 'local'). 
7865      * Note: use of a valueField requires the user make a selection
7866      * in order for a value to be mapped.
7867      */
7868     valueField: undefined,
7869     
7870     
7871     /**
7872      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7873      * field's data value (defaults to the underlying DOM element's name)
7874      */
7875     hiddenName: undefined,
7876     /**
7877      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7878      */
7879     listClass: '',
7880     /**
7881      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7882      */
7883     selectedClass: 'active',
7884     
7885     /**
7886      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7887      */
7888     shadow:'sides',
7889     /**
7890      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7891      * anchor positions (defaults to 'tl-bl')
7892      */
7893     listAlign: 'tl-bl?',
7894     /**
7895      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7896      */
7897     maxHeight: 300,
7898     /**
7899      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7900      * query specified by the allQuery config option (defaults to 'query')
7901      */
7902     triggerAction: 'query',
7903     /**
7904      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7905      * (defaults to 4, does not apply if editable = false)
7906      */
7907     minChars : 4,
7908     /**
7909      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7910      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7911      */
7912     typeAhead: false,
7913     /**
7914      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7915      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7916      */
7917     queryDelay: 500,
7918     /**
7919      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7920      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7921      */
7922     pageSize: 0,
7923     /**
7924      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7925      * when editable = true (defaults to false)
7926      */
7927     selectOnFocus:false,
7928     /**
7929      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7930      */
7931     queryParam: 'query',
7932     /**
7933      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7934      * when mode = 'remote' (defaults to 'Loading...')
7935      */
7936     loadingText: 'Loading...',
7937     /**
7938      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7939      */
7940     resizable: false,
7941     /**
7942      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7943      */
7944     handleHeight : 8,
7945     /**
7946      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7947      * traditional select (defaults to true)
7948      */
7949     editable: true,
7950     /**
7951      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7952      */
7953     allQuery: '',
7954     /**
7955      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7956      */
7957     mode: 'remote',
7958     /**
7959      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7960      * listWidth has a higher value)
7961      */
7962     minListWidth : 70,
7963     /**
7964      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7965      * allow the user to set arbitrary text into the field (defaults to false)
7966      */
7967     forceSelection:false,
7968     /**
7969      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7970      * if typeAhead = true (defaults to 250)
7971      */
7972     typeAheadDelay : 250,
7973     /**
7974      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7975      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7976      */
7977     valueNotFoundText : undefined,
7978     /**
7979      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7980      */
7981     blockFocus : false,
7982     
7983     /**
7984      * @cfg {Boolean} disableClear Disable showing of clear button.
7985      */
7986     disableClear : false,
7987     /**
7988      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7989      */
7990     alwaysQuery : false,
7991     
7992     /**
7993      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7994      */
7995     multiple : false,
7996     
7997     //private
7998     addicon : false,
7999     editicon: false,
8000     
8001     page: 0,
8002     hasQuery: false,
8003     append: false,
8004     loadNext: false,
8005     item: [],
8006     
8007     // element that contains real text value.. (when hidden is used..)
8008      
8009     // private
8010     initEvents: function(){
8011         
8012         if (!this.store) {
8013             throw "can not find store for combo";
8014         }
8015         this.store = Roo.factory(this.store, Roo.data);
8016         
8017         
8018         
8019         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8020         
8021         
8022         if(this.hiddenName){
8023             
8024             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8025             
8026             this.hiddenField.dom.value =
8027                 this.hiddenValue !== undefined ? this.hiddenValue :
8028                 this.value !== undefined ? this.value : '';
8029
8030             // prevent input submission
8031             this.el.dom.removeAttribute('name');
8032             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8033              
8034              
8035         }
8036         //if(Roo.isGecko){
8037         //    this.el.dom.setAttribute('autocomplete', 'off');
8038         //}
8039
8040         var cls = 'x-combo-list';
8041         this.list = this.el.select('ul.dropdown-menu',true).first();
8042
8043         //this.list = new Roo.Layer({
8044         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8045         //});
8046         
8047         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8048         this.list.setWidth(lw);
8049         
8050         this.list.on('mouseover', this.onViewOver, this);
8051         this.list.on('mousemove', this.onViewMove, this);
8052         
8053         this.list.on('scroll', this.onViewScroll, this);
8054         
8055         /*
8056         this.list.swallowEvent('mousewheel');
8057         this.assetHeight = 0;
8058
8059         if(this.title){
8060             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8061             this.assetHeight += this.header.getHeight();
8062         }
8063
8064         this.innerList = this.list.createChild({cls:cls+'-inner'});
8065         this.innerList.on('mouseover', this.onViewOver, this);
8066         this.innerList.on('mousemove', this.onViewMove, this);
8067         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8068         
8069         if(this.allowBlank && !this.pageSize && !this.disableClear){
8070             this.footer = this.list.createChild({cls:cls+'-ft'});
8071             this.pageTb = new Roo.Toolbar(this.footer);
8072            
8073         }
8074         if(this.pageSize){
8075             this.footer = this.list.createChild({cls:cls+'-ft'});
8076             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8077                     {pageSize: this.pageSize});
8078             
8079         }
8080         
8081         if (this.pageTb && this.allowBlank && !this.disableClear) {
8082             var _this = this;
8083             this.pageTb.add(new Roo.Toolbar.Fill(), {
8084                 cls: 'x-btn-icon x-btn-clear',
8085                 text: '&#160;',
8086                 handler: function()
8087                 {
8088                     _this.collapse();
8089                     _this.clearValue();
8090                     _this.onSelect(false, -1);
8091                 }
8092             });
8093         }
8094         if (this.footer) {
8095             this.assetHeight += this.footer.getHeight();
8096         }
8097         */
8098             
8099         if(!this.tpl){
8100             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8101         }
8102
8103         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8104             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8105         });
8106         //this.view.wrapEl.setDisplayed(false);
8107         this.view.on('click', this.onViewClick, this);
8108         
8109         
8110         
8111         this.store.on('beforeload', this.onBeforeLoad, this);
8112         this.store.on('load', this.onLoad, this);
8113         this.store.on('loadexception', this.onLoadException, this);
8114         /*
8115         if(this.resizable){
8116             this.resizer = new Roo.Resizable(this.list,  {
8117                pinned:true, handles:'se'
8118             });
8119             this.resizer.on('resize', function(r, w, h){
8120                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8121                 this.listWidth = w;
8122                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8123                 this.restrictHeight();
8124             }, this);
8125             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8126         }
8127         */
8128         if(!this.editable){
8129             this.editable = true;
8130             this.setEditable(false);
8131         }
8132         
8133         /*
8134         
8135         if (typeof(this.events.add.listeners) != 'undefined') {
8136             
8137             this.addicon = this.wrap.createChild(
8138                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8139        
8140             this.addicon.on('click', function(e) {
8141                 this.fireEvent('add', this);
8142             }, this);
8143         }
8144         if (typeof(this.events.edit.listeners) != 'undefined') {
8145             
8146             this.editicon = this.wrap.createChild(
8147                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8148             if (this.addicon) {
8149                 this.editicon.setStyle('margin-left', '40px');
8150             }
8151             this.editicon.on('click', function(e) {
8152                 
8153                 // we fire even  if inothing is selected..
8154                 this.fireEvent('edit', this, this.lastData );
8155                 
8156             }, this);
8157         }
8158         */
8159         
8160         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8161             "up" : function(e){
8162                 this.inKeyMode = true;
8163                 this.selectPrev();
8164             },
8165
8166             "down" : function(e){
8167                 if(!this.isExpanded()){
8168                     this.onTriggerClick();
8169                 }else{
8170                     this.inKeyMode = true;
8171                     this.selectNext();
8172                 }
8173             },
8174
8175             "enter" : function(e){
8176                 this.onViewClick();
8177                 //return true;
8178             },
8179
8180             "esc" : function(e){
8181                 this.collapse();
8182             },
8183
8184             "tab" : function(e){
8185                 this.collapse();
8186                 
8187                 if(this.fireEvent("specialkey", this, e)){
8188                     this.onViewClick(false);
8189                 }
8190                 
8191                 return true;
8192             },
8193
8194             scope : this,
8195
8196             doRelay : function(foo, bar, hname){
8197                 if(hname == 'down' || this.scope.isExpanded()){
8198                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8199                 }
8200                 return true;
8201             },
8202
8203             forceKeyDown: true
8204         });
8205         
8206         
8207         this.queryDelay = Math.max(this.queryDelay || 10,
8208                 this.mode == 'local' ? 10 : 250);
8209         
8210         
8211         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8212         
8213         if(this.typeAhead){
8214             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8215         }
8216         if(this.editable !== false){
8217             this.inputEl().on("keyup", this.onKeyUp, this);
8218         }
8219         if(this.forceSelection){
8220             this.on('blur', this.doForce, this);
8221         }
8222         
8223         if(this.multiple){
8224             this.choices = this.el.select('ul.select2-choices', true).first();
8225             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8226         }
8227     },
8228
8229     onDestroy : function(){
8230         if(this.view){
8231             this.view.setStore(null);
8232             this.view.el.removeAllListeners();
8233             this.view.el.remove();
8234             this.view.purgeListeners();
8235         }
8236         if(this.list){
8237             this.list.dom.innerHTML  = '';
8238         }
8239         if(this.store){
8240             this.store.un('beforeload', this.onBeforeLoad, this);
8241             this.store.un('load', this.onLoad, this);
8242             this.store.un('loadexception', this.onLoadException, this);
8243         }
8244         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8245     },
8246
8247     // private
8248     fireKey : function(e){
8249         if(e.isNavKeyPress() && !this.list.isVisible()){
8250             this.fireEvent("specialkey", this, e);
8251         }
8252     },
8253
8254     // private
8255     onResize: function(w, h){
8256 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8257 //        
8258 //        if(typeof w != 'number'){
8259 //            // we do not handle it!?!?
8260 //            return;
8261 //        }
8262 //        var tw = this.trigger.getWidth();
8263 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8264 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8265 //        var x = w - tw;
8266 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8267 //            
8268 //        //this.trigger.setStyle('left', x+'px');
8269 //        
8270 //        if(this.list && this.listWidth === undefined){
8271 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8272 //            this.list.setWidth(lw);
8273 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8274 //        }
8275         
8276     
8277         
8278     },
8279
8280     /**
8281      * Allow or prevent the user from directly editing the field text.  If false is passed,
8282      * the user will only be able to select from the items defined in the dropdown list.  This method
8283      * is the runtime equivalent of setting the 'editable' config option at config time.
8284      * @param {Boolean} value True to allow the user to directly edit the field text
8285      */
8286     setEditable : function(value){
8287         if(value == this.editable){
8288             return;
8289         }
8290         this.editable = value;
8291         if(!value){
8292             this.inputEl().dom.setAttribute('readOnly', true);
8293             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8294             this.inputEl().addClass('x-combo-noedit');
8295         }else{
8296             this.inputEl().dom.setAttribute('readOnly', false);
8297             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8298             this.inputEl().removeClass('x-combo-noedit');
8299         }
8300     },
8301
8302     // private
8303     
8304     onBeforeLoad : function(combo,opts){
8305         if(!this.hasFocus){
8306             return;
8307         }
8308          if (!opts.add) {
8309             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8310          }
8311         this.restrictHeight();
8312         this.selectedIndex = -1;
8313     },
8314
8315     // private
8316     onLoad : function(){
8317         
8318         this.hasQuery = false;
8319         
8320         if(!this.hasFocus){
8321             return;
8322         }
8323         
8324         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8325             this.loading.hide();
8326         }
8327         
8328         if(this.store.getCount() > 0){
8329             this.expand();
8330             this.restrictHeight();
8331             if(this.lastQuery == this.allQuery){
8332                 if(this.editable){
8333                     this.inputEl().dom.select();
8334                 }
8335                 if(!this.selectByValue(this.value, true)){
8336                     this.select(0, true);
8337                 }
8338             }else{
8339                 this.selectNext();
8340                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8341                     this.taTask.delay(this.typeAheadDelay);
8342                 }
8343             }
8344         }else{
8345             this.onEmptyResults();
8346         }
8347         
8348         //this.el.focus();
8349     },
8350     // private
8351     onLoadException : function()
8352     {
8353         this.hasQuery = false;
8354         
8355         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8356             this.loading.hide();
8357         }
8358         
8359         this.collapse();
8360         Roo.log(this.store.reader.jsonData);
8361         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8362             // fixme
8363             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8364         }
8365         
8366         
8367     },
8368     // private
8369     onTypeAhead : function(){
8370         if(this.store.getCount() > 0){
8371             var r = this.store.getAt(0);
8372             var newValue = r.data[this.displayField];
8373             var len = newValue.length;
8374             var selStart = this.getRawValue().length;
8375             
8376             if(selStart != len){
8377                 this.setRawValue(newValue);
8378                 this.selectText(selStart, newValue.length);
8379             }
8380         }
8381     },
8382
8383     // private
8384     onSelect : function(record, index){
8385         
8386         if(this.fireEvent('beforeselect', this, record, index) !== false){
8387         
8388             this.setFromData(index > -1 ? record.data : false);
8389             
8390             this.collapse();
8391             this.fireEvent('select', this, record, index);
8392         }
8393     },
8394
8395     /**
8396      * Returns the currently selected field value or empty string if no value is set.
8397      * @return {String} value The selected value
8398      */
8399     getValue : function(){
8400         
8401         if(this.multiple){
8402             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8403         }
8404         
8405         if(this.valueField){
8406             return typeof this.value != 'undefined' ? this.value : '';
8407         }else{
8408             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8409         }
8410     },
8411
8412     /**
8413      * Clears any text/value currently set in the field
8414      */
8415     clearValue : function(){
8416         if(this.hiddenField){
8417             this.hiddenField.dom.value = '';
8418         }
8419         this.value = '';
8420         this.setRawValue('');
8421         this.lastSelectionText = '';
8422         
8423     },
8424
8425     /**
8426      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8427      * will be displayed in the field.  If the value does not match the data value of an existing item,
8428      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8429      * Otherwise the field will be blank (although the value will still be set).
8430      * @param {String} value The value to match
8431      */
8432     setValue : function(v){
8433         if(this.multiple){
8434             this.syncValue();
8435             return;
8436         }
8437         
8438         var text = v;
8439         if(this.valueField){
8440             var r = this.findRecord(this.valueField, v);
8441             if(r){
8442                 text = r.data[this.displayField];
8443             }else if(this.valueNotFoundText !== undefined){
8444                 text = this.valueNotFoundText;
8445             }
8446         }
8447         this.lastSelectionText = text;
8448         if(this.hiddenField){
8449             this.hiddenField.dom.value = v;
8450         }
8451         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8452         this.value = v;
8453     },
8454     /**
8455      * @property {Object} the last set data for the element
8456      */
8457     
8458     lastData : false,
8459     /**
8460      * Sets the value of the field based on a object which is related to the record format for the store.
8461      * @param {Object} value the value to set as. or false on reset?
8462      */
8463     setFromData : function(o){
8464         
8465         if(this.multiple){
8466             this.addItem(o);
8467             return;
8468         }
8469             
8470         var dv = ''; // display value
8471         var vv = ''; // value value..
8472         this.lastData = o;
8473         if (this.displayField) {
8474             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8475         } else {
8476             // this is an error condition!!!
8477             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8478         }
8479         
8480         if(this.valueField){
8481             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8482         }
8483         
8484         if(this.hiddenField){
8485             this.hiddenField.dom.value = vv;
8486             
8487             this.lastSelectionText = dv;
8488             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8489             this.value = vv;
8490             return;
8491         }
8492         // no hidden field.. - we store the value in 'value', but still display
8493         // display field!!!!
8494         this.lastSelectionText = dv;
8495         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8496         this.value = vv;
8497         
8498         
8499     },
8500     // private
8501     reset : function(){
8502         // overridden so that last data is reset..
8503         this.setValue(this.originalValue);
8504         this.clearInvalid();
8505         this.lastData = false;
8506         if (this.view) {
8507             this.view.clearSelections();
8508         }
8509     },
8510     // private
8511     findRecord : function(prop, value){
8512         var record;
8513         if(this.store.getCount() > 0){
8514             this.store.each(function(r){
8515                 if(r.data[prop] == value){
8516                     record = r;
8517                     return false;
8518                 }
8519                 return true;
8520             });
8521         }
8522         return record;
8523     },
8524     
8525     getName: function()
8526     {
8527         // returns hidden if it's set..
8528         if (!this.rendered) {return ''};
8529         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8530         
8531     },
8532     // private
8533     onViewMove : function(e, t){
8534         this.inKeyMode = false;
8535     },
8536
8537     // private
8538     onViewOver : function(e, t){
8539         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8540             return;
8541         }
8542         var item = this.view.findItemFromChild(t);
8543         if(item){
8544             var index = this.view.indexOf(item);
8545             this.select(index, false);
8546         }
8547     },
8548
8549     // private
8550     onViewClick : function(doFocus)
8551     {
8552         var index = this.view.getSelectedIndexes()[0];
8553         var r = this.store.getAt(index);
8554         if(r){
8555             this.onSelect(r, index);
8556         }
8557         if(doFocus !== false && !this.blockFocus){
8558             this.inputEl().focus();
8559         }
8560     },
8561
8562     // private
8563     restrictHeight : function(){
8564         //this.innerList.dom.style.height = '';
8565         //var inner = this.innerList.dom;
8566         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8567         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8568         //this.list.beginUpdate();
8569         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8570         this.list.alignTo(this.inputEl(), this.listAlign);
8571         //this.list.endUpdate();
8572     },
8573
8574     // private
8575     onEmptyResults : function(){
8576         this.collapse();
8577     },
8578
8579     /**
8580      * Returns true if the dropdown list is expanded, else false.
8581      */
8582     isExpanded : function(){
8583         return this.list.isVisible();
8584     },
8585
8586     /**
8587      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8588      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8589      * @param {String} value The data value of the item to select
8590      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8591      * selected item if it is not currently in view (defaults to true)
8592      * @return {Boolean} True if the value matched an item in the list, else false
8593      */
8594     selectByValue : function(v, scrollIntoView){
8595         if(v !== undefined && v !== null){
8596             var r = this.findRecord(this.valueField || this.displayField, v);
8597             if(r){
8598                 this.select(this.store.indexOf(r), scrollIntoView);
8599                 return true;
8600             }
8601         }
8602         return false;
8603     },
8604
8605     /**
8606      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8607      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8608      * @param {Number} index The zero-based index of the list item to select
8609      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8610      * selected item if it is not currently in view (defaults to true)
8611      */
8612     select : function(index, scrollIntoView){
8613         this.selectedIndex = index;
8614         this.view.select(index);
8615         if(scrollIntoView !== false){
8616             var el = this.view.getNode(index);
8617             if(el){
8618                 //this.innerList.scrollChildIntoView(el, false);
8619                 
8620             }
8621         }
8622     },
8623
8624     // private
8625     selectNext : function(){
8626         var ct = this.store.getCount();
8627         if(ct > 0){
8628             if(this.selectedIndex == -1){
8629                 this.select(0);
8630             }else if(this.selectedIndex < ct-1){
8631                 this.select(this.selectedIndex+1);
8632             }
8633         }
8634     },
8635
8636     // private
8637     selectPrev : function(){
8638         var ct = this.store.getCount();
8639         if(ct > 0){
8640             if(this.selectedIndex == -1){
8641                 this.select(0);
8642             }else if(this.selectedIndex != 0){
8643                 this.select(this.selectedIndex-1);
8644             }
8645         }
8646     },
8647
8648     // private
8649     onKeyUp : function(e){
8650         if(this.editable !== false && !e.isSpecialKey()){
8651             this.lastKey = e.getKey();
8652             this.dqTask.delay(this.queryDelay);
8653         }
8654     },
8655
8656     // private
8657     validateBlur : function(){
8658         return !this.list || !this.list.isVisible();   
8659     },
8660
8661     // private
8662     initQuery : function(){
8663         this.doQuery(this.getRawValue());
8664     },
8665
8666     // private
8667     doForce : function(){
8668         if(this.el.dom.value.length > 0){
8669             this.el.dom.value =
8670                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8671              
8672         }
8673     },
8674
8675     /**
8676      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8677      * query allowing the query action to be canceled if needed.
8678      * @param {String} query The SQL query to execute
8679      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8680      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8681      * saved in the current store (defaults to false)
8682      */
8683     doQuery : function(q, forceAll){
8684         
8685         if(q === undefined || q === null){
8686             q = '';
8687         }
8688         var qe = {
8689             query: q,
8690             forceAll: forceAll,
8691             combo: this,
8692             cancel:false
8693         };
8694         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8695             return false;
8696         }
8697         q = qe.query;
8698         
8699         forceAll = qe.forceAll;
8700         if(forceAll === true || (q.length >= this.minChars)){
8701             
8702             this.hasQuery = true;
8703             
8704             if(this.lastQuery != q || this.alwaysQuery){
8705                 this.lastQuery = q;
8706                 if(this.mode == 'local'){
8707                     this.selectedIndex = -1;
8708                     if(forceAll){
8709                         this.store.clearFilter();
8710                     }else{
8711                         this.store.filter(this.displayField, q);
8712                     }
8713                     this.onLoad();
8714                 }else{
8715                     this.store.baseParams[this.queryParam] = q;
8716                     
8717                     var options = {params : this.getParams(q)};
8718                     
8719                     if(this.loadNext){
8720                         options.add = true;
8721                         options.params.start = this.page * this.pageSize;
8722                     }
8723                     
8724                     this.store.load(options);
8725                     this.expand();
8726                 }
8727             }else{
8728                 this.selectedIndex = -1;
8729                 this.onLoad();   
8730             }
8731         }
8732         
8733         this.loadNext = false;
8734     },
8735
8736     // private
8737     getParams : function(q){
8738         var p = {};
8739         //p[this.queryParam] = q;
8740         
8741         if(this.pageSize){
8742             p.start = 0;
8743             p.limit = this.pageSize;
8744         }
8745         return p;
8746     },
8747
8748     /**
8749      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8750      */
8751     collapse : function(){
8752         if(!this.isExpanded()){
8753             return;
8754         }
8755         
8756         this.list.hide();
8757         Roo.get(document).un('mousedown', this.collapseIf, this);
8758         Roo.get(document).un('mousewheel', this.collapseIf, this);
8759         if (!this.editable) {
8760             Roo.get(document).un('keydown', this.listKeyPress, this);
8761         }
8762         this.fireEvent('collapse', this);
8763     },
8764
8765     // private
8766     collapseIf : function(e){
8767         var in_combo  = e.within(this.el);
8768         var in_list =  e.within(this.list);
8769         
8770         if (in_combo || in_list) {
8771             //e.stopPropagation();
8772             return;
8773         }
8774
8775         this.collapse();
8776         
8777     },
8778
8779     /**
8780      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8781      */
8782     expand : function(){
8783        
8784         if(this.isExpanded() || !this.hasFocus){
8785             return;
8786         }
8787          Roo.log('expand');
8788         this.list.alignTo(this.inputEl(), this.listAlign);
8789         this.list.show();
8790         Roo.get(document).on('mousedown', this.collapseIf, this);
8791         Roo.get(document).on('mousewheel', this.collapseIf, this);
8792         if (!this.editable) {
8793             Roo.get(document).on('keydown', this.listKeyPress, this);
8794         }
8795         
8796         this.fireEvent('expand', this);
8797     },
8798
8799     // private
8800     // Implements the default empty TriggerField.onTriggerClick function
8801     onTriggerClick : function()
8802     {
8803         Roo.log('trigger click');
8804         
8805         if(this.disabled){
8806             return;
8807         }
8808         
8809         this.page = 0;
8810         this.loadNext = false;
8811         
8812         if(this.isExpanded()){
8813             this.collapse();
8814             if (!this.blockFocus) {
8815                 this.inputEl().focus();
8816             }
8817             
8818         }else {
8819             this.hasFocus = true;
8820             if(this.triggerAction == 'all') {
8821                 this.doQuery(this.allQuery, true);
8822             } else {
8823                 this.doQuery(this.getRawValue());
8824             }
8825             if (!this.blockFocus) {
8826                 this.inputEl().focus();
8827             }
8828         }
8829     },
8830     listKeyPress : function(e)
8831     {
8832         //Roo.log('listkeypress');
8833         // scroll to first matching element based on key pres..
8834         if (e.isSpecialKey()) {
8835             return false;
8836         }
8837         var k = String.fromCharCode(e.getKey()).toUpperCase();
8838         //Roo.log(k);
8839         var match  = false;
8840         var csel = this.view.getSelectedNodes();
8841         var cselitem = false;
8842         if (csel.length) {
8843             var ix = this.view.indexOf(csel[0]);
8844             cselitem  = this.store.getAt(ix);
8845             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8846                 cselitem = false;
8847             }
8848             
8849         }
8850         
8851         this.store.each(function(v) { 
8852             if (cselitem) {
8853                 // start at existing selection.
8854                 if (cselitem.id == v.id) {
8855                     cselitem = false;
8856                 }
8857                 return true;
8858             }
8859                 
8860             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8861                 match = this.store.indexOf(v);
8862                 return false;
8863             }
8864             return true;
8865         }, this);
8866         
8867         if (match === false) {
8868             return true; // no more action?
8869         }
8870         // scroll to?
8871         this.view.select(match);
8872         var sn = Roo.get(this.view.getSelectedNodes()[0])
8873         //sn.scrollIntoView(sn.dom.parentNode, false);
8874     },
8875     
8876     onViewScroll : function(e, t){
8877         
8878         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8879             return;
8880         }
8881         
8882         this.hasQuery = true;
8883         
8884         this.loading = this.list.select('.loading', true).first();
8885         
8886         if(this.loading === null){
8887             this.list.createChild({
8888                 tag: 'div',
8889                 cls: 'loading select2-more-results select2-active',
8890                 html: 'Loading more results...'
8891             })
8892             
8893             this.loading = this.list.select('.loading', true).first();
8894             
8895             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8896             
8897             this.loading.hide();
8898         }
8899         
8900         this.loading.show();
8901         
8902         var _combo = this;
8903         
8904         this.page++;
8905         this.loadNext = true;
8906         
8907         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8908         
8909         return;
8910     },
8911     
8912     addItem : function(o)
8913     {   
8914         var dv = ''; // display value
8915         
8916         if (this.displayField) {
8917             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8918         } else {
8919             // this is an error condition!!!
8920             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8921         }
8922         
8923         if(!dv.length){
8924             return;
8925         }
8926         
8927         var choice = this.choices.createChild({
8928             tag: 'li',
8929             cls: 'select2-search-choice',
8930             cn: [
8931                 {
8932                     tag: 'div',
8933                     html: dv
8934                 },
8935                 {
8936                     tag: 'a',
8937                     href: '#',
8938                     cls: 'select2-search-choice-close',
8939                     tabindex: '-1'
8940                 }
8941             ]
8942             
8943         }, this.searchField);
8944         
8945         var close = choice.select('a.select2-search-choice-close', true).first()
8946         
8947         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8948         
8949         this.item.push(o);
8950         this.lastData = o;
8951         
8952         this.syncValue();
8953         
8954         this.inputEl().dom.value = '';
8955         
8956     },
8957     
8958     onRemoveItem : function(e, _self, o)
8959     {
8960         Roo.log('remove item');
8961         var index = this.item.indexOf(o.data) * 1;
8962         
8963         if( index < 0){
8964             Roo.log('not this item?!');
8965             return;
8966         }
8967         
8968         this.item.splice(index, 1);
8969         o.item.remove();
8970         
8971         this.syncValue();
8972         
8973         this.fireEvent('remove', this);
8974         
8975     },
8976     
8977     syncValue : function()
8978     {
8979         if(!this.item.length){
8980             this.clearValue();
8981             return;
8982         }
8983             
8984         var value = [];
8985         var _this = this;
8986         Roo.each(this.item, function(i){
8987             if(_this.valueField){
8988                 value.push(i[_this.valueField]);
8989                 return;
8990             }
8991
8992             value.push(i);
8993         });
8994
8995         this.value = value.join(',');
8996
8997         if(this.hiddenField){
8998             this.hiddenField.dom.value = this.value;
8999         }
9000     },
9001     
9002     clearItem : function()
9003     {
9004         if(!this.multiple){
9005             return;
9006         }
9007         
9008         this.item = [];
9009         
9010         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9011            c.remove();
9012         });
9013         
9014         this.syncValue();
9015     }
9016     
9017     
9018
9019     /** 
9020     * @cfg {Boolean} grow 
9021     * @hide 
9022     */
9023     /** 
9024     * @cfg {Number} growMin 
9025     * @hide 
9026     */
9027     /** 
9028     * @cfg {Number} growMax 
9029     * @hide 
9030     */
9031     /**
9032      * @hide
9033      * @method autoSize
9034      */
9035 });
9036 /*
9037  * Based on:
9038  * Ext JS Library 1.1.1
9039  * Copyright(c) 2006-2007, Ext JS, LLC.
9040  *
9041  * Originally Released Under LGPL - original licence link has changed is not relivant.
9042  *
9043  * Fork - LGPL
9044  * <script type="text/javascript">
9045  */
9046
9047 /**
9048  * @class Roo.View
9049  * @extends Roo.util.Observable
9050  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9051  * This class also supports single and multi selection modes. <br>
9052  * Create a data model bound view:
9053  <pre><code>
9054  var store = new Roo.data.Store(...);
9055
9056  var view = new Roo.View({
9057     el : "my-element",
9058     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9059  
9060     singleSelect: true,
9061     selectedClass: "ydataview-selected",
9062     store: store
9063  });
9064
9065  // listen for node click?
9066  view.on("click", function(vw, index, node, e){
9067  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9068  });
9069
9070  // load XML data
9071  dataModel.load("foobar.xml");
9072  </code></pre>
9073  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9074  * <br><br>
9075  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9076  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9077  * 
9078  * Note: old style constructor is still suported (container, template, config)
9079  * 
9080  * @constructor
9081  * Create a new View
9082  * @param {Object} config The config object
9083  * 
9084  */
9085 Roo.View = function(config, depreciated_tpl, depreciated_config){
9086     
9087     if (typeof(depreciated_tpl) == 'undefined') {
9088         // new way.. - universal constructor.
9089         Roo.apply(this, config);
9090         this.el  = Roo.get(this.el);
9091     } else {
9092         // old format..
9093         this.el  = Roo.get(config);
9094         this.tpl = depreciated_tpl;
9095         Roo.apply(this, depreciated_config);
9096     }
9097     this.wrapEl  = this.el.wrap().wrap();
9098     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9099     
9100     
9101     if(typeof(this.tpl) == "string"){
9102         this.tpl = new Roo.Template(this.tpl);
9103     } else {
9104         // support xtype ctors..
9105         this.tpl = new Roo.factory(this.tpl, Roo);
9106     }
9107     
9108     
9109     this.tpl.compile();
9110    
9111   
9112     
9113      
9114     /** @private */
9115     this.addEvents({
9116         /**
9117          * @event beforeclick
9118          * Fires before a click is processed. Returns false to cancel the default action.
9119          * @param {Roo.View} this
9120          * @param {Number} index The index of the target node
9121          * @param {HTMLElement} node The target node
9122          * @param {Roo.EventObject} e The raw event object
9123          */
9124             "beforeclick" : true,
9125         /**
9126          * @event click
9127          * Fires when a template node is clicked.
9128          * @param {Roo.View} this
9129          * @param {Number} index The index of the target node
9130          * @param {HTMLElement} node The target node
9131          * @param {Roo.EventObject} e The raw event object
9132          */
9133             "click" : true,
9134         /**
9135          * @event dblclick
9136          * Fires when a template node is double clicked.
9137          * @param {Roo.View} this
9138          * @param {Number} index The index of the target node
9139          * @param {HTMLElement} node The target node
9140          * @param {Roo.EventObject} e The raw event object
9141          */
9142             "dblclick" : true,
9143         /**
9144          * @event contextmenu
9145          * Fires when a template node is right clicked.
9146          * @param {Roo.View} this
9147          * @param {Number} index The index of the target node
9148          * @param {HTMLElement} node The target node
9149          * @param {Roo.EventObject} e The raw event object
9150          */
9151             "contextmenu" : true,
9152         /**
9153          * @event selectionchange
9154          * Fires when the selected nodes change.
9155          * @param {Roo.View} this
9156          * @param {Array} selections Array of the selected nodes
9157          */
9158             "selectionchange" : true,
9159     
9160         /**
9161          * @event beforeselect
9162          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9163          * @param {Roo.View} this
9164          * @param {HTMLElement} node The node to be selected
9165          * @param {Array} selections Array of currently selected nodes
9166          */
9167             "beforeselect" : true,
9168         /**
9169          * @event preparedata
9170          * Fires on every row to render, to allow you to change the data.
9171          * @param {Roo.View} this
9172          * @param {Object} data to be rendered (change this)
9173          */
9174           "preparedata" : true
9175           
9176           
9177         });
9178
9179
9180
9181     this.el.on({
9182         "click": this.onClick,
9183         "dblclick": this.onDblClick,
9184         "contextmenu": this.onContextMenu,
9185         scope:this
9186     });
9187
9188     this.selections = [];
9189     this.nodes = [];
9190     this.cmp = new Roo.CompositeElementLite([]);
9191     if(this.store){
9192         this.store = Roo.factory(this.store, Roo.data);
9193         this.setStore(this.store, true);
9194     }
9195     
9196     if ( this.footer && this.footer.xtype) {
9197            
9198          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9199         
9200         this.footer.dataSource = this.store
9201         this.footer.container = fctr;
9202         this.footer = Roo.factory(this.footer, Roo);
9203         fctr.insertFirst(this.el);
9204         
9205         // this is a bit insane - as the paging toolbar seems to detach the el..
9206 //        dom.parentNode.parentNode.parentNode
9207          // they get detached?
9208     }
9209     
9210     
9211     Roo.View.superclass.constructor.call(this);
9212     
9213     
9214 };
9215
9216 Roo.extend(Roo.View, Roo.util.Observable, {
9217     
9218      /**
9219      * @cfg {Roo.data.Store} store Data store to load data from.
9220      */
9221     store : false,
9222     
9223     /**
9224      * @cfg {String|Roo.Element} el The container element.
9225      */
9226     el : '',
9227     
9228     /**
9229      * @cfg {String|Roo.Template} tpl The template used by this View 
9230      */
9231     tpl : false,
9232     /**
9233      * @cfg {String} dataName the named area of the template to use as the data area
9234      *                          Works with domtemplates roo-name="name"
9235      */
9236     dataName: false,
9237     /**
9238      * @cfg {String} selectedClass The css class to add to selected nodes
9239      */
9240     selectedClass : "x-view-selected",
9241      /**
9242      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9243      */
9244     emptyText : "",
9245     
9246     /**
9247      * @cfg {String} text to display on mask (default Loading)
9248      */
9249     mask : false,
9250     /**
9251      * @cfg {Boolean} multiSelect Allow multiple selection
9252      */
9253     multiSelect : false,
9254     /**
9255      * @cfg {Boolean} singleSelect Allow single selection
9256      */
9257     singleSelect:  false,
9258     
9259     /**
9260      * @cfg {Boolean} toggleSelect - selecting 
9261      */
9262     toggleSelect : false,
9263     
9264     /**
9265      * Returns the element this view is bound to.
9266      * @return {Roo.Element}
9267      */
9268     getEl : function(){
9269         return this.wrapEl;
9270     },
9271     
9272     
9273
9274     /**
9275      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9276      */
9277     refresh : function(){
9278         Roo.log('refresh');
9279         var t = this.tpl;
9280         
9281         // if we are using something like 'domtemplate', then
9282         // the what gets used is:
9283         // t.applySubtemplate(NAME, data, wrapping data..)
9284         // the outer template then get' applied with
9285         //     the store 'extra data'
9286         // and the body get's added to the
9287         //      roo-name="data" node?
9288         //      <span class='roo-tpl-{name}'></span> ?????
9289         
9290         
9291         
9292         this.clearSelections();
9293         this.el.update("");
9294         var html = [];
9295         var records = this.store.getRange();
9296         if(records.length < 1) {
9297             
9298             // is this valid??  = should it render a template??
9299             
9300             this.el.update(this.emptyText);
9301             return;
9302         }
9303         var el = this.el;
9304         if (this.dataName) {
9305             this.el.update(t.apply(this.store.meta)); //????
9306             el = this.el.child('.roo-tpl-' + this.dataName);
9307         }
9308         
9309         for(var i = 0, len = records.length; i < len; i++){
9310             var data = this.prepareData(records[i].data, i, records[i]);
9311             this.fireEvent("preparedata", this, data, i, records[i]);
9312             html[html.length] = Roo.util.Format.trim(
9313                 this.dataName ?
9314                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9315                     t.apply(data)
9316             );
9317         }
9318         
9319         
9320         
9321         el.update(html.join(""));
9322         this.nodes = el.dom.childNodes;
9323         this.updateIndexes(0);
9324     },
9325     
9326
9327     /**
9328      * Function to override to reformat the data that is sent to
9329      * the template for each node.
9330      * DEPRICATED - use the preparedata event handler.
9331      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9332      * a JSON object for an UpdateManager bound view).
9333      */
9334     prepareData : function(data, index, record)
9335     {
9336         this.fireEvent("preparedata", this, data, index, record);
9337         return data;
9338     },
9339
9340     onUpdate : function(ds, record){
9341          Roo.log('on update');   
9342         this.clearSelections();
9343         var index = this.store.indexOf(record);
9344         var n = this.nodes[index];
9345         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9346         n.parentNode.removeChild(n);
9347         this.updateIndexes(index, index);
9348     },
9349
9350     
9351     
9352 // --------- FIXME     
9353     onAdd : function(ds, records, index)
9354     {
9355         Roo.log(['on Add', ds, records, index] );        
9356         this.clearSelections();
9357         if(this.nodes.length == 0){
9358             this.refresh();
9359             return;
9360         }
9361         var n = this.nodes[index];
9362         for(var i = 0, len = records.length; i < len; i++){
9363             var d = this.prepareData(records[i].data, i, records[i]);
9364             if(n){
9365                 this.tpl.insertBefore(n, d);
9366             }else{
9367                 
9368                 this.tpl.append(this.el, d);
9369             }
9370         }
9371         this.updateIndexes(index);
9372     },
9373
9374     onRemove : function(ds, record, index){
9375         Roo.log('onRemove');
9376         this.clearSelections();
9377         var el = this.dataName  ?
9378             this.el.child('.roo-tpl-' + this.dataName) :
9379             this.el; 
9380         
9381         el.dom.removeChild(this.nodes[index]);
9382         this.updateIndexes(index);
9383     },
9384
9385     /**
9386      * Refresh an individual node.
9387      * @param {Number} index
9388      */
9389     refreshNode : function(index){
9390         this.onUpdate(this.store, this.store.getAt(index));
9391     },
9392
9393     updateIndexes : function(startIndex, endIndex){
9394         var ns = this.nodes;
9395         startIndex = startIndex || 0;
9396         endIndex = endIndex || ns.length - 1;
9397         for(var i = startIndex; i <= endIndex; i++){
9398             ns[i].nodeIndex = i;
9399         }
9400     },
9401
9402     /**
9403      * Changes the data store this view uses and refresh the view.
9404      * @param {Store} store
9405      */
9406     setStore : function(store, initial){
9407         if(!initial && this.store){
9408             this.store.un("datachanged", this.refresh);
9409             this.store.un("add", this.onAdd);
9410             this.store.un("remove", this.onRemove);
9411             this.store.un("update", this.onUpdate);
9412             this.store.un("clear", this.refresh);
9413             this.store.un("beforeload", this.onBeforeLoad);
9414             this.store.un("load", this.onLoad);
9415             this.store.un("loadexception", this.onLoad);
9416         }
9417         if(store){
9418           
9419             store.on("datachanged", this.refresh, this);
9420             store.on("add", this.onAdd, this);
9421             store.on("remove", this.onRemove, this);
9422             store.on("update", this.onUpdate, this);
9423             store.on("clear", this.refresh, this);
9424             store.on("beforeload", this.onBeforeLoad, this);
9425             store.on("load", this.onLoad, this);
9426             store.on("loadexception", this.onLoad, this);
9427         }
9428         
9429         if(store){
9430             this.refresh();
9431         }
9432     },
9433     /**
9434      * onbeforeLoad - masks the loading area.
9435      *
9436      */
9437     onBeforeLoad : function(store,opts)
9438     {
9439          Roo.log('onBeforeLoad');   
9440         if (!opts.add) {
9441             this.el.update("");
9442         }
9443         this.el.mask(this.mask ? this.mask : "Loading" ); 
9444     },
9445     onLoad : function ()
9446     {
9447         this.el.unmask();
9448     },
9449     
9450
9451     /**
9452      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9453      * @param {HTMLElement} node
9454      * @return {HTMLElement} The template node
9455      */
9456     findItemFromChild : function(node){
9457         var el = this.dataName  ?
9458             this.el.child('.roo-tpl-' + this.dataName,true) :
9459             this.el.dom; 
9460         
9461         if(!node || node.parentNode == el){
9462                     return node;
9463             }
9464             var p = node.parentNode;
9465             while(p && p != el){
9466             if(p.parentNode == el){
9467                 return p;
9468             }
9469             p = p.parentNode;
9470         }
9471             return null;
9472     },
9473
9474     /** @ignore */
9475     onClick : function(e){
9476         var item = this.findItemFromChild(e.getTarget());
9477         if(item){
9478             var index = this.indexOf(item);
9479             if(this.onItemClick(item, index, e) !== false){
9480                 this.fireEvent("click", this, index, item, e);
9481             }
9482         }else{
9483             this.clearSelections();
9484         }
9485     },
9486
9487     /** @ignore */
9488     onContextMenu : function(e){
9489         var item = this.findItemFromChild(e.getTarget());
9490         if(item){
9491             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9492         }
9493     },
9494
9495     /** @ignore */
9496     onDblClick : function(e){
9497         var item = this.findItemFromChild(e.getTarget());
9498         if(item){
9499             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9500         }
9501     },
9502
9503     onItemClick : function(item, index, e)
9504     {
9505         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9506             return false;
9507         }
9508         if (this.toggleSelect) {
9509             var m = this.isSelected(item) ? 'unselect' : 'select';
9510             Roo.log(m);
9511             var _t = this;
9512             _t[m](item, true, false);
9513             return true;
9514         }
9515         if(this.multiSelect || this.singleSelect){
9516             if(this.multiSelect && e.shiftKey && this.lastSelection){
9517                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9518             }else{
9519                 this.select(item, this.multiSelect && e.ctrlKey);
9520                 this.lastSelection = item;
9521             }
9522             e.preventDefault();
9523         }
9524         return true;
9525     },
9526
9527     /**
9528      * Get the number of selected nodes.
9529      * @return {Number}
9530      */
9531     getSelectionCount : function(){
9532         return this.selections.length;
9533     },
9534
9535     /**
9536      * Get the currently selected nodes.
9537      * @return {Array} An array of HTMLElements
9538      */
9539     getSelectedNodes : function(){
9540         return this.selections;
9541     },
9542
9543     /**
9544      * Get the indexes of the selected nodes.
9545      * @return {Array}
9546      */
9547     getSelectedIndexes : function(){
9548         var indexes = [], s = this.selections;
9549         for(var i = 0, len = s.length; i < len; i++){
9550             indexes.push(s[i].nodeIndex);
9551         }
9552         return indexes;
9553     },
9554
9555     /**
9556      * Clear all selections
9557      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9558      */
9559     clearSelections : function(suppressEvent){
9560         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9561             this.cmp.elements = this.selections;
9562             this.cmp.removeClass(this.selectedClass);
9563             this.selections = [];
9564             if(!suppressEvent){
9565                 this.fireEvent("selectionchange", this, this.selections);
9566             }
9567         }
9568     },
9569
9570     /**
9571      * Returns true if the passed node is selected
9572      * @param {HTMLElement/Number} node The node or node index
9573      * @return {Boolean}
9574      */
9575     isSelected : function(node){
9576         var s = this.selections;
9577         if(s.length < 1){
9578             return false;
9579         }
9580         node = this.getNode(node);
9581         return s.indexOf(node) !== -1;
9582     },
9583
9584     /**
9585      * Selects nodes.
9586      * @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
9587      * @param {Boolean} keepExisting (optional) true to keep existing selections
9588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9589      */
9590     select : function(nodeInfo, keepExisting, suppressEvent){
9591         if(nodeInfo instanceof Array){
9592             if(!keepExisting){
9593                 this.clearSelections(true);
9594             }
9595             for(var i = 0, len = nodeInfo.length; i < len; i++){
9596                 this.select(nodeInfo[i], true, true);
9597             }
9598             return;
9599         } 
9600         var node = this.getNode(nodeInfo);
9601         if(!node || this.isSelected(node)){
9602             return; // already selected.
9603         }
9604         if(!keepExisting){
9605             this.clearSelections(true);
9606         }
9607         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9608             Roo.fly(node).addClass(this.selectedClass);
9609             this.selections.push(node);
9610             if(!suppressEvent){
9611                 this.fireEvent("selectionchange", this, this.selections);
9612             }
9613         }
9614         
9615         
9616     },
9617       /**
9618      * Unselects nodes.
9619      * @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
9620      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9621      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9622      */
9623     unselect : function(nodeInfo, keepExisting, suppressEvent)
9624     {
9625         if(nodeInfo instanceof Array){
9626             Roo.each(this.selections, function(s) {
9627                 this.unselect(s, nodeInfo);
9628             }, this);
9629             return;
9630         }
9631         var node = this.getNode(nodeInfo);
9632         if(!node || !this.isSelected(node)){
9633             Roo.log("not selected");
9634             return; // not selected.
9635         }
9636         // fireevent???
9637         var ns = [];
9638         Roo.each(this.selections, function(s) {
9639             if (s == node ) {
9640                 Roo.fly(node).removeClass(this.selectedClass);
9641
9642                 return;
9643             }
9644             ns.push(s);
9645         },this);
9646         
9647         this.selections= ns;
9648         this.fireEvent("selectionchange", this, this.selections);
9649     },
9650
9651     /**
9652      * Gets a template node.
9653      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9654      * @return {HTMLElement} The node or null if it wasn't found
9655      */
9656     getNode : function(nodeInfo){
9657         if(typeof nodeInfo == "string"){
9658             return document.getElementById(nodeInfo);
9659         }else if(typeof nodeInfo == "number"){
9660             return this.nodes[nodeInfo];
9661         }
9662         return nodeInfo;
9663     },
9664
9665     /**
9666      * Gets a range template nodes.
9667      * @param {Number} startIndex
9668      * @param {Number} endIndex
9669      * @return {Array} An array of nodes
9670      */
9671     getNodes : function(start, end){
9672         var ns = this.nodes;
9673         start = start || 0;
9674         end = typeof end == "undefined" ? ns.length - 1 : end;
9675         var nodes = [];
9676         if(start <= end){
9677             for(var i = start; i <= end; i++){
9678                 nodes.push(ns[i]);
9679             }
9680         } else{
9681             for(var i = start; i >= end; i--){
9682                 nodes.push(ns[i]);
9683             }
9684         }
9685         return nodes;
9686     },
9687
9688     /**
9689      * Finds the index of the passed node
9690      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9691      * @return {Number} The index of the node or -1
9692      */
9693     indexOf : function(node){
9694         node = this.getNode(node);
9695         if(typeof node.nodeIndex == "number"){
9696             return node.nodeIndex;
9697         }
9698         var ns = this.nodes;
9699         for(var i = 0, len = ns.length; i < len; i++){
9700             if(ns[i] == node){
9701                 return i;
9702             }
9703         }
9704         return -1;
9705     }
9706 });
9707 /*
9708  * - LGPL
9709  *
9710  * based on jquery fullcalendar
9711  * 
9712  */
9713
9714 Roo.bootstrap = Roo.bootstrap || {};
9715 /**
9716  * @class Roo.bootstrap.Calendar
9717  * @extends Roo.bootstrap.Component
9718  * Bootstrap Calendar class
9719  * @cfg {Boolean} loadMask (true|false) default false
9720     
9721  * @constructor
9722  * Create a new Container
9723  * @param {Object} config The config object
9724  */
9725
9726
9727
9728 Roo.bootstrap.Calendar = function(config){
9729     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9730      this.addEvents({
9731         /**
9732              * @event select
9733              * Fires when a date is selected
9734              * @param {DatePicker} this
9735              * @param {Date} date The selected date
9736              */
9737         'select': true,
9738         /**
9739              * @event monthchange
9740              * Fires when the displayed month changes 
9741              * @param {DatePicker} this
9742              * @param {Date} date The selected month
9743              */
9744         'monthchange': true,
9745         /**
9746              * @event evententer
9747              * Fires when mouse over an event
9748              * @param {Calendar} this
9749              * @param {event} Event
9750              */
9751         'evententer': true,
9752         /**
9753              * @event eventleave
9754              * Fires when the mouse leaves an
9755              * @param {Calendar} this
9756              * @param {event}
9757              */
9758         'eventleave': true,
9759         /**
9760              * @event eventclick
9761              * Fires when the mouse click an
9762              * @param {Calendar} this
9763              * @param {event}
9764              */
9765         'eventclick': true
9766         
9767     });
9768
9769 };
9770
9771 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9772     
9773      /**
9774      * @cfg {Number} startDay
9775      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9776      */
9777     startDay : 0,
9778     
9779     loadMask : false,
9780       
9781     getAutoCreate : function(){
9782         
9783         
9784         var fc_button = function(name, corner, style, content ) {
9785             return Roo.apply({},{
9786                 tag : 'span',
9787                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9788                          (corner.length ?
9789                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9790                             ''
9791                         ),
9792                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9793                 unselectable: 'on'
9794             });
9795         };
9796         
9797         var header = {
9798             tag : 'table',
9799             cls : 'fc-header',
9800             style : 'width:100%',
9801             cn : [
9802                 {
9803                     tag: 'tr',
9804                     cn : [
9805                         {
9806                             tag : 'td',
9807                             cls : 'fc-header-left',
9808                             cn : [
9809                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9810                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9811                                 { tag: 'span', cls: 'fc-header-space' },
9812                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9813                                 
9814                                 
9815                             ]
9816                         },
9817                         
9818                         {
9819                             tag : 'td',
9820                             cls : 'fc-header-center',
9821                             cn : [
9822                                 {
9823                                     tag: 'span',
9824                                     cls: 'fc-header-title',
9825                                     cn : {
9826                                         tag: 'H2',
9827                                         html : 'month / year'
9828                                     }
9829                                 }
9830                                 
9831                             ]
9832                         },
9833                         {
9834                             tag : 'td',
9835                             cls : 'fc-header-right',
9836                             cn : [
9837                           /*      fc_button('month', 'left', '', 'month' ),
9838                                 fc_button('week', '', '', 'week' ),
9839                                 fc_button('day', 'right', '', 'day' )
9840                             */    
9841                                 
9842                             ]
9843                         }
9844                         
9845                     ]
9846                 }
9847             ]
9848         };
9849         
9850        
9851         var cal_heads = function() {
9852             var ret = [];
9853             // fixme - handle this.
9854             
9855             for (var i =0; i < Date.dayNames.length; i++) {
9856                 var d = Date.dayNames[i];
9857                 ret.push({
9858                     tag: 'th',
9859                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9860                     html : d.substring(0,3)
9861                 });
9862                 
9863             }
9864             ret[0].cls += ' fc-first';
9865             ret[6].cls += ' fc-last';
9866             return ret;
9867         };
9868         var cal_cell = function(n) {
9869             return  {
9870                 tag: 'td',
9871                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9872                 cn : [
9873                     {
9874                         cn : [
9875                             {
9876                                 cls: 'fc-day-number',
9877                                 html: 'D'
9878                             },
9879                             {
9880                                 cls: 'fc-day-content',
9881                              
9882                                 cn : [
9883                                      {
9884                                         style: 'position: relative;' // height: 17px;
9885                                     }
9886                                 ]
9887                             }
9888                             
9889                             
9890                         ]
9891                     }
9892                 ]
9893                 
9894             }
9895         };
9896         var cal_rows = function() {
9897             
9898             var ret = []
9899             for (var r = 0; r < 6; r++) {
9900                 var row= {
9901                     tag : 'tr',
9902                     cls : 'fc-week',
9903                     cn : []
9904                 };
9905                 
9906                 for (var i =0; i < Date.dayNames.length; i++) {
9907                     var d = Date.dayNames[i];
9908                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9909
9910                 }
9911                 row.cn[0].cls+=' fc-first';
9912                 row.cn[0].cn[0].style = 'min-height:90px';
9913                 row.cn[6].cls+=' fc-last';
9914                 ret.push(row);
9915                 
9916             }
9917             ret[0].cls += ' fc-first';
9918             ret[4].cls += ' fc-prev-last';
9919             ret[5].cls += ' fc-last';
9920             return ret;
9921             
9922         };
9923         
9924         var cal_table = {
9925             tag: 'table',
9926             cls: 'fc-border-separate',
9927             style : 'width:100%',
9928             cellspacing  : 0,
9929             cn : [
9930                 { 
9931                     tag: 'thead',
9932                     cn : [
9933                         { 
9934                             tag: 'tr',
9935                             cls : 'fc-first fc-last',
9936                             cn : cal_heads()
9937                         }
9938                     ]
9939                 },
9940                 { 
9941                     tag: 'tbody',
9942                     cn : cal_rows()
9943                 }
9944                   
9945             ]
9946         };
9947          
9948          var cfg = {
9949             cls : 'fc fc-ltr',
9950             cn : [
9951                 header,
9952                 {
9953                     cls : 'fc-content',
9954                     style : "position: relative;",
9955                     cn : [
9956                         {
9957                             cls : 'fc-view fc-view-month fc-grid',
9958                             style : 'position: relative',
9959                             unselectable : 'on',
9960                             cn : [
9961                                 {
9962                                     cls : 'fc-event-container',
9963                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9964                                 },
9965                                 cal_table
9966                             ]
9967                         }
9968                     ]
9969     
9970                 }
9971            ] 
9972             
9973         };
9974         
9975          
9976         
9977         return cfg;
9978     },
9979     
9980     
9981     initEvents : function()
9982     {
9983         if(!this.store){
9984             throw "can not find store for calendar";
9985         }
9986         
9987         var mark = {
9988             tag: "div",
9989             cls:"x-dlg-mask",
9990             style: "text-align:center",
9991             cn: [
9992                 {
9993                     tag: "div",
9994                     style: "background-color:white;width:50%;margin:250 auto",
9995                     cn: [
9996                         {
9997                             tag: "img",
9998                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9999                         },
10000                         {
10001                             tag: "span",
10002                             html: "Loading"
10003                         }
10004                         
10005                     ]
10006                 }
10007             ]
10008         }
10009         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10010         
10011         var size = this.el.select('.fc-content', true).first().getSize();
10012         this.maskEl.setSize(size.width, size.height);
10013         this.maskEl.enableDisplayMode("block");
10014         if(!this.loadMask){
10015             this.maskEl.hide();
10016         }
10017         
10018         this.store = Roo.factory(this.store, Roo.data);
10019         this.store.on('load', this.onLoad, this);
10020         this.store.on('beforeload', this.onBeforeLoad, this);
10021         
10022         this.resize();
10023         
10024         this.cells = this.el.select('.fc-day',true);
10025         //Roo.log(this.cells);
10026         this.textNodes = this.el.query('.fc-day-number');
10027         this.cells.addClassOnOver('fc-state-hover');
10028         
10029         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10030         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10031         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10032         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10033         
10034         this.on('monthchange', this.onMonthChange, this);
10035         
10036         this.update(new Date().clearTime());
10037     },
10038     
10039     resize : function() {
10040         var sz  = this.el.getSize();
10041         
10042         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10043         this.el.select('.fc-day-content div',true).setHeight(34);
10044     },
10045     
10046     
10047     // private
10048     showPrevMonth : function(e){
10049         this.update(this.activeDate.add("mo", -1));
10050     },
10051     showToday : function(e){
10052         this.update(new Date().clearTime());
10053     },
10054     // private
10055     showNextMonth : function(e){
10056         this.update(this.activeDate.add("mo", 1));
10057     },
10058
10059     // private
10060     showPrevYear : function(){
10061         this.update(this.activeDate.add("y", -1));
10062     },
10063
10064     // private
10065     showNextYear : function(){
10066         this.update(this.activeDate.add("y", 1));
10067     },
10068
10069     
10070    // private
10071     update : function(date)
10072     {
10073         var vd = this.activeDate;
10074         this.activeDate = date;
10075 //        if(vd && this.el){
10076 //            var t = date.getTime();
10077 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10078 //                Roo.log('using add remove');
10079 //                
10080 //                this.fireEvent('monthchange', this, date);
10081 //                
10082 //                this.cells.removeClass("fc-state-highlight");
10083 //                this.cells.each(function(c){
10084 //                   if(c.dateValue == t){
10085 //                       c.addClass("fc-state-highlight");
10086 //                       setTimeout(function(){
10087 //                            try{c.dom.firstChild.focus();}catch(e){}
10088 //                       }, 50);
10089 //                       return false;
10090 //                   }
10091 //                   return true;
10092 //                });
10093 //                return;
10094 //            }
10095 //        }
10096         
10097         var days = date.getDaysInMonth();
10098         
10099         var firstOfMonth = date.getFirstDateOfMonth();
10100         var startingPos = firstOfMonth.getDay()-this.startDay;
10101         
10102         if(startingPos < this.startDay){
10103             startingPos += 7;
10104         }
10105         
10106         var pm = date.add(Date.MONTH, -1);
10107         var prevStart = pm.getDaysInMonth()-startingPos;
10108 //        
10109         this.cells = this.el.select('.fc-day',true);
10110         this.textNodes = this.el.query('.fc-day-number');
10111         this.cells.addClassOnOver('fc-state-hover');
10112         
10113         var cells = this.cells.elements;
10114         var textEls = this.textNodes;
10115         
10116         Roo.each(cells, function(cell){
10117             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10118         });
10119         
10120         days += startingPos;
10121
10122         // convert everything to numbers so it's fast
10123         var day = 86400000;
10124         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10125         //Roo.log(d);
10126         //Roo.log(pm);
10127         //Roo.log(prevStart);
10128         
10129         var today = new Date().clearTime().getTime();
10130         var sel = date.clearTime().getTime();
10131         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10132         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10133         var ddMatch = this.disabledDatesRE;
10134         var ddText = this.disabledDatesText;
10135         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10136         var ddaysText = this.disabledDaysText;
10137         var format = this.format;
10138         
10139         var setCellClass = function(cal, cell){
10140             
10141             //Roo.log('set Cell Class');
10142             cell.title = "";
10143             var t = d.getTime();
10144             
10145             //Roo.log(d);
10146             
10147             cell.dateValue = t;
10148             if(t == today){
10149                 cell.className += " fc-today";
10150                 cell.className += " fc-state-highlight";
10151                 cell.title = cal.todayText;
10152             }
10153             if(t == sel){
10154                 // disable highlight in other month..
10155                 //cell.className += " fc-state-highlight";
10156                 
10157             }
10158             // disabling
10159             if(t < min) {
10160                 cell.className = " fc-state-disabled";
10161                 cell.title = cal.minText;
10162                 return;
10163             }
10164             if(t > max) {
10165                 cell.className = " fc-state-disabled";
10166                 cell.title = cal.maxText;
10167                 return;
10168             }
10169             if(ddays){
10170                 if(ddays.indexOf(d.getDay()) != -1){
10171                     cell.title = ddaysText;
10172                     cell.className = " fc-state-disabled";
10173                 }
10174             }
10175             if(ddMatch && format){
10176                 var fvalue = d.dateFormat(format);
10177                 if(ddMatch.test(fvalue)){
10178                     cell.title = ddText.replace("%0", fvalue);
10179                     cell.className = " fc-state-disabled";
10180                 }
10181             }
10182             
10183             if (!cell.initialClassName) {
10184                 cell.initialClassName = cell.dom.className;
10185             }
10186             
10187             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10188         };
10189
10190         var i = 0;
10191         
10192         for(; i < startingPos; i++) {
10193             textEls[i].innerHTML = (++prevStart);
10194             d.setDate(d.getDate()+1);
10195             
10196             cells[i].className = "fc-past fc-other-month";
10197             setCellClass(this, cells[i]);
10198         }
10199         
10200         var intDay = 0;
10201         
10202         for(; i < days; i++){
10203             intDay = i - startingPos + 1;
10204             textEls[i].innerHTML = (intDay);
10205             d.setDate(d.getDate()+1);
10206             
10207             cells[i].className = ''; // "x-date-active";
10208             setCellClass(this, cells[i]);
10209         }
10210         var extraDays = 0;
10211         
10212         for(; i < 42; i++) {
10213             textEls[i].innerHTML = (++extraDays);
10214             d.setDate(d.getDate()+1);
10215             
10216             cells[i].className = "fc-future fc-other-month";
10217             setCellClass(this, cells[i]);
10218         }
10219         
10220         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10221         
10222         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10223         
10224         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10225         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10226         
10227         if(totalRows != 6){
10228             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10229             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10230         }
10231         
10232         this.fireEvent('monthchange', this, date);
10233         
10234         
10235         /*
10236         if(!this.internalRender){
10237             var main = this.el.dom.firstChild;
10238             var w = main.offsetWidth;
10239             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10240             Roo.fly(main).setWidth(w);
10241             this.internalRender = true;
10242             // opera does not respect the auto grow header center column
10243             // then, after it gets a width opera refuses to recalculate
10244             // without a second pass
10245             if(Roo.isOpera && !this.secondPass){
10246                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10247                 this.secondPass = true;
10248                 this.update.defer(10, this, [date]);
10249             }
10250         }
10251         */
10252         
10253     },
10254     
10255     findCell : function(dt) {
10256         dt = dt.clearTime().getTime();
10257         var ret = false;
10258         this.cells.each(function(c){
10259             //Roo.log("check " +c.dateValue + '?=' + dt);
10260             if(c.dateValue == dt){
10261                 ret = c;
10262                 return false;
10263             }
10264             return true;
10265         });
10266         
10267         return ret;
10268     },
10269     
10270     findCells : function(ev) {
10271         var s = ev.start.clone().clearTime().getTime();
10272        // Roo.log(s);
10273         var e= ev.end.clone().clearTime().getTime();
10274        // Roo.log(e);
10275         var ret = [];
10276         this.cells.each(function(c){
10277              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10278             
10279             if(c.dateValue > e){
10280                 return ;
10281             }
10282             if(c.dateValue < s){
10283                 return ;
10284             }
10285             ret.push(c);
10286         });
10287         
10288         return ret;    
10289     },
10290     
10291     findBestRow: function(cells)
10292     {
10293         var ret = 0;
10294         
10295         for (var i =0 ; i < cells.length;i++) {
10296             ret  = Math.max(cells[i].rows || 0,ret);
10297         }
10298         return ret;
10299         
10300     },
10301     
10302     
10303     addItem : function(ev)
10304     {
10305         // look for vertical location slot in
10306         var cells = this.findCells(ev);
10307         
10308         ev.row = this.findBestRow(cells);
10309         
10310         // work out the location.
10311         
10312         var crow = false;
10313         var rows = [];
10314         for(var i =0; i < cells.length; i++) {
10315             if (!crow) {
10316                 crow = {
10317                     start : cells[i],
10318                     end :  cells[i]
10319                 };
10320                 continue;
10321             }
10322             if (crow.start.getY() == cells[i].getY()) {
10323                 // on same row.
10324                 crow.end = cells[i];
10325                 continue;
10326             }
10327             // different row.
10328             rows.push(crow);
10329             crow = {
10330                 start: cells[i],
10331                 end : cells[i]
10332             };
10333             
10334         }
10335         
10336         rows.push(crow);
10337         ev.els = [];
10338         ev.rows = rows;
10339         ev.cells = cells;
10340         for (var i = 0; i < cells.length;i++) {
10341             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10342             
10343         }
10344         
10345         this.calevents.push(ev);
10346     },
10347     
10348     clearEvents: function() {
10349         
10350         if(!this.calevents){
10351             return;
10352         }
10353         
10354         Roo.each(this.cells.elements, function(c){
10355             c.rows = 0;
10356         });
10357         
10358         Roo.each(this.calevents, function(e) {
10359             Roo.each(e.els, function(el) {
10360                 el.un('mouseenter' ,this.onEventEnter, this);
10361                 el.un('mouseleave' ,this.onEventLeave, this);
10362                 el.remove();
10363             },this);
10364         },this);
10365         
10366     },
10367     
10368     renderEvents: function()
10369     {   
10370         // first make sure there is enough space..
10371         
10372         this.cells.each(function(c) {
10373         
10374             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10375         });
10376         
10377         for (var e = 0; e < this.calevents.length; e++) {
10378             var ev = this.calevents[e];
10379             var cells = ev.cells;
10380             var rows = ev.rows;
10381             
10382             for(var i =0; i < rows.length; i++) {
10383                 
10384                  
10385                 // how many rows should it span..
10386                 
10387                 var  cfg = {
10388                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10389                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10390                     
10391                     unselectable : "on",
10392                     cn : [
10393                         {
10394                             cls: 'fc-event-inner',
10395                             cn : [
10396 //                                {
10397 //                                  tag:'span',
10398 //                                  cls: 'fc-event-time',
10399 //                                  html : cells.length > 1 ? '' : ev.time
10400 //                                },
10401                                 {
10402                                   tag:'span',
10403                                   cls: 'fc-event-title',
10404                                   html : String.format('{0}', ev.title)
10405                                 }
10406                                 
10407                                 
10408                             ]
10409                         },
10410                         {
10411                             cls: 'ui-resizable-handle ui-resizable-e',
10412                             html : '&nbsp;&nbsp;&nbsp'
10413                         }
10414                         
10415                     ]
10416                 };
10417                 if (i == 0) {
10418                     cfg.cls += ' fc-event-start';
10419                 }
10420                 if ((i+1) == rows.length) {
10421                     cfg.cls += ' fc-event-end';
10422                 }
10423                 
10424                 var ctr = this.el.select('.fc-event-container',true).first();
10425                 var cg = ctr.createChild(cfg);
10426                 
10427                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10428                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10429                 cg.on('click', this.onEventClick, this, ev);
10430                 
10431                 ev.els.push(cg);
10432                 
10433                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10434                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10435                 //Roo.log(cg);
10436                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10437                 cg.setWidth(ebox.right - sbox.x -2);
10438             }
10439             
10440             
10441         }
10442         
10443     },
10444     
10445     onEventEnter: function (e, el,event,d) {
10446         this.fireEvent('evententer', this, el, event);
10447     },
10448     
10449     onEventLeave: function (e, el,event,d) {
10450         this.fireEvent('eventleave', this, el, event);
10451     },
10452     
10453     onEventClick: function (e, el,event,d) {
10454         this.fireEvent('eventclick', this, el, event);
10455     },
10456     
10457     onMonthChange: function () {
10458         this.store.load();
10459     },
10460     
10461     onLoad: function () 
10462     {   
10463         this.calevents = [];
10464         var cal = this;
10465         
10466         if(this.store.getCount() > 0){
10467             this.store.data.each(function(d){
10468                cal.addItem({
10469                     id : d.data.id,
10470                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10471                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10472                     time : d.data.start_time,
10473                     title : d.data.title,
10474                     description : d.data.description,
10475                     venue : d.data.venue
10476                 });
10477             });
10478         }
10479         
10480         this.renderEvents();
10481         
10482         if(this.loadMask){
10483             this.maskEl.hide();
10484         }
10485     },
10486     
10487     onBeforeLoad: function()
10488     {
10489         this.clearEvents();
10490         
10491         if(this.loadMask){
10492             this.maskEl.show();
10493         }
10494     }
10495 });
10496
10497  
10498  /*
10499  * - LGPL
10500  *
10501  * element
10502  * 
10503  */
10504
10505 /**
10506  * @class Roo.bootstrap.Popover
10507  * @extends Roo.bootstrap.Component
10508  * Bootstrap Popover class
10509  * @cfg {String} html contents of the popover   (or false to use children..)
10510  * @cfg {String} title of popover (or false to hide)
10511  * @cfg {String} placement how it is placed
10512  * @cfg {String} trigger click || hover (or false to trigger manually)
10513  * @cfg {String} over what (parent or false to trigger manually.)
10514  * 
10515  * @constructor
10516  * Create a new Popover
10517  * @param {Object} config The config object
10518  */
10519
10520 Roo.bootstrap.Popover = function(config){
10521     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10522 };
10523
10524 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10525     
10526     title: 'Fill in a title',
10527     html: false,
10528     
10529     placement : 'right',
10530     trigger : 'hover', // hover
10531     
10532     over: 'parent',
10533     
10534     can_build_overlaid : false,
10535     
10536     getChildContainer : function()
10537     {
10538         return this.el.select('.popover-content',true).first();
10539     },
10540     
10541     getAutoCreate : function(){
10542          Roo.log('make popover?');
10543         var cfg = {
10544            cls : 'popover roo-dynamic',
10545            style: 'display:block',
10546            cn : [
10547                 {
10548                     cls : 'arrow'
10549                 },
10550                 {
10551                     cls : 'popover-inner',
10552                     cn : [
10553                         {
10554                             tag: 'h3',
10555                             cls: 'popover-title',
10556                             html : this.title
10557                         },
10558                         {
10559                             cls : 'popover-content',
10560                             html : this.html
10561                         }
10562                     ]
10563                     
10564                 }
10565            ]
10566         };
10567         
10568         return cfg;
10569     },
10570     setTitle: function(str)
10571     {
10572         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10573     },
10574     setContent: function(str)
10575     {
10576         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10577     },
10578     // as it get's added to the bottom of the page.
10579     onRender : function(ct, position)
10580     {
10581         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10582         if(!this.el){
10583             var cfg = Roo.apply({},  this.getAutoCreate());
10584             cfg.id = Roo.id();
10585             
10586             if (this.cls) {
10587                 cfg.cls += ' ' + this.cls;
10588             }
10589             if (this.style) {
10590                 cfg.style = this.style;
10591             }
10592             Roo.log("adding to ")
10593             this.el = Roo.get(document.body).createChild(cfg, position);
10594             Roo.log(this.el);
10595         }
10596         this.initEvents();
10597     },
10598     
10599     initEvents : function()
10600     {
10601         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10602         this.el.enableDisplayMode('block');
10603         this.el.hide();
10604         if (this.over === false) {
10605             return; 
10606         }
10607         if (this.triggers === false) {
10608             return;
10609         }
10610         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10611         var triggers = this.trigger ? this.trigger.split(' ') : [];
10612         Roo.each(triggers, function(trigger) {
10613         
10614             if (trigger == 'click') {
10615                 on_el.on('click', this.toggle, this);
10616             } else if (trigger != 'manual') {
10617                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10618                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10619       
10620                 on_el.on(eventIn  ,this.enter, this);
10621                 on_el.on(eventOut, this.leave, this);
10622             }
10623         }, this);
10624         
10625     },
10626     
10627     
10628     // private
10629     timeout : null,
10630     hoverState : null,
10631     
10632     toggle : function () {
10633         this.hoverState == 'in' ? this.leave() : this.enter();
10634     },
10635     
10636     enter : function () {
10637        
10638     
10639         clearTimeout(this.timeout);
10640     
10641         this.hoverState = 'in'
10642     
10643         if (!this.delay || !this.delay.show) {
10644             this.show();
10645             return 
10646         }
10647         var _t = this;
10648         this.timeout = setTimeout(function () {
10649             if (_t.hoverState == 'in') {
10650                 _t.show();
10651             }
10652         }, this.delay.show)
10653     },
10654     leave : function() {
10655         clearTimeout(this.timeout);
10656     
10657         this.hoverState = 'out'
10658     
10659         if (!this.delay || !this.delay.hide) {
10660             this.hide();
10661             return 
10662         }
10663         var _t = this;
10664         this.timeout = setTimeout(function () {
10665             if (_t.hoverState == 'out') {
10666                 _t.hide();
10667             }
10668         }, this.delay.hide)
10669     },
10670     
10671     show : function (on_el)
10672     {
10673         if (!on_el) {
10674             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10675         }
10676         // set content.
10677         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10678         if (this.html !== false) {
10679             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10680         }
10681         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10682         if (!this.title.length) {
10683             this.el.select('.popover-title',true).hide();
10684         }
10685         
10686         var placement = typeof this.placement == 'function' ?
10687             this.placement.call(this, this.el, on_el) :
10688             this.placement;
10689             
10690         var autoToken = /\s?auto?\s?/i;
10691         var autoPlace = autoToken.test(placement);
10692         if (autoPlace) {
10693             placement = placement.replace(autoToken, '') || 'top';
10694         }
10695         
10696         //this.el.detach()
10697         //this.el.setXY([0,0]);
10698         this.el.show();
10699         this.el.dom.style.display='block';
10700         this.el.addClass(placement);
10701         
10702         //this.el.appendTo(on_el);
10703         
10704         var p = this.getPosition();
10705         var box = this.el.getBox();
10706         
10707         if (autoPlace) {
10708             // fixme..
10709         }
10710         var align = Roo.bootstrap.Popover.alignment[placement]
10711         this.el.alignTo(on_el, align[0],align[1]);
10712         //var arrow = this.el.select('.arrow',true).first();
10713         //arrow.set(align[2], 
10714         
10715         this.el.addClass('in');
10716         this.hoverState = null;
10717         
10718         if (this.el.hasClass('fade')) {
10719             // fade it?
10720         }
10721         
10722     },
10723     hide : function()
10724     {
10725         this.el.setXY([0,0]);
10726         this.el.removeClass('in');
10727         this.el.hide();
10728         
10729     }
10730     
10731 });
10732
10733 Roo.bootstrap.Popover.alignment = {
10734     'left' : ['r-l', [-10,0], 'right'],
10735     'right' : ['l-r', [10,0], 'left'],
10736     'bottom' : ['t-b', [0,10], 'top'],
10737     'top' : [ 'b-t', [0,-10], 'bottom']
10738 };
10739
10740  /*
10741  * - LGPL
10742  *
10743  * Progress
10744  * 
10745  */
10746
10747 /**
10748  * @class Roo.bootstrap.Progress
10749  * @extends Roo.bootstrap.Component
10750  * Bootstrap Progress class
10751  * @cfg {Boolean} striped striped of the progress bar
10752  * @cfg {Boolean} active animated of the progress bar
10753  * 
10754  * 
10755  * @constructor
10756  * Create a new Progress
10757  * @param {Object} config The config object
10758  */
10759
10760 Roo.bootstrap.Progress = function(config){
10761     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10762 };
10763
10764 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10765     
10766     striped : false,
10767     active: false,
10768     
10769     getAutoCreate : function(){
10770         var cfg = {
10771             tag: 'div',
10772             cls: 'progress'
10773         };
10774         
10775         
10776         if(this.striped){
10777             cfg.cls += ' progress-striped';
10778         }
10779       
10780         if(this.active){
10781             cfg.cls += ' active';
10782         }
10783         
10784         
10785         return cfg;
10786     }
10787    
10788 });
10789
10790  
10791
10792  /*
10793  * - LGPL
10794  *
10795  * ProgressBar
10796  * 
10797  */
10798
10799 /**
10800  * @class Roo.bootstrap.ProgressBar
10801  * @extends Roo.bootstrap.Component
10802  * Bootstrap ProgressBar class
10803  * @cfg {Number} aria_valuenow aria-value now
10804  * @cfg {Number} aria_valuemin aria-value min
10805  * @cfg {Number} aria_valuemax aria-value max
10806  * @cfg {String} label label for the progress bar
10807  * @cfg {String} panel (success | info | warning | danger )
10808  * @cfg {String} role role of the progress bar
10809  * @cfg {String} sr_only text
10810  * 
10811  * 
10812  * @constructor
10813  * Create a new ProgressBar
10814  * @param {Object} config The config object
10815  */
10816
10817 Roo.bootstrap.ProgressBar = function(config){
10818     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10819 };
10820
10821 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10822     
10823     aria_valuenow : 0,
10824     aria_valuemin : 0,
10825     aria_valuemax : 100,
10826     label : false,
10827     panel : false,
10828     role : false,
10829     sr_only: false,
10830     
10831     getAutoCreate : function()
10832     {
10833         
10834         var cfg = {
10835             tag: 'div',
10836             cls: 'progress-bar',
10837             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10838         };
10839         
10840         if(this.sr_only){
10841             cfg.cn = {
10842                 tag: 'span',
10843                 cls: 'sr-only',
10844                 html: this.sr_only
10845             }
10846         }
10847         
10848         if(this.role){
10849             cfg.role = this.role;
10850         }
10851         
10852         if(this.aria_valuenow){
10853             cfg['aria-valuenow'] = this.aria_valuenow;
10854         }
10855         
10856         if(this.aria_valuemin){
10857             cfg['aria-valuemin'] = this.aria_valuemin;
10858         }
10859         
10860         if(this.aria_valuemax){
10861             cfg['aria-valuemax'] = this.aria_valuemax;
10862         }
10863         
10864         if(this.label && !this.sr_only){
10865             cfg.html = this.label;
10866         }
10867         
10868         if(this.panel){
10869             cfg.cls += ' progress-bar-' + this.panel;
10870         }
10871         
10872         return cfg;
10873     },
10874     
10875     update : function(aria_valuenow)
10876     {
10877         this.aria_valuenow = aria_valuenow;
10878         
10879         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10880     }
10881    
10882 });
10883
10884  
10885
10886  /*
10887  * - LGPL
10888  *
10889  * TabPanel
10890  * 
10891  */
10892
10893 /**
10894  * @class Roo.bootstrap.TabPanel
10895  * @extends Roo.bootstrap.Component
10896  * Bootstrap TabPanel class
10897  * @cfg {Boolean} active panel active
10898  * @cfg {String} html panel content
10899  * @cfg {String} tabId tab relate id
10900  * 
10901  * 
10902  * @constructor
10903  * Create a new TabPanel
10904  * @param {Object} config The config object
10905  */
10906
10907 Roo.bootstrap.TabPanel = function(config){
10908     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10909 };
10910
10911 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10912     
10913     active: false,
10914     html: false,
10915     tabId: false,
10916     
10917     getAutoCreate : function(){
10918         var cfg = {
10919             tag: 'div',
10920             cls: 'tab-pane',
10921             html: this.html || ''
10922         };
10923         
10924         if(this.active){
10925             cfg.cls += ' active';
10926         }
10927         
10928         if(this.tabId){
10929             cfg.tabId = this.tabId;
10930         }
10931         
10932         return cfg;
10933     }
10934    
10935 });
10936
10937  
10938
10939  /*
10940  * - LGPL
10941  *
10942  * DateField
10943  * 
10944  */
10945
10946 /**
10947  * @class Roo.bootstrap.DateField
10948  * @extends Roo.bootstrap.Input
10949  * Bootstrap DateField class
10950  * @cfg {Number} weekStart default 0
10951  * @cfg {Number} weekStart default 0
10952  * @cfg {Number} viewMode default empty, (months|years)
10953  * @cfg {Number} minViewMode default empty, (months|years)
10954  * @cfg {Number} startDate default -Infinity
10955  * @cfg {Number} endDate default Infinity
10956  * @cfg {Boolean} todayHighlight default false
10957  * @cfg {Boolean} todayBtn default false
10958  * @cfg {Boolean} calendarWeeks default false
10959  * @cfg {Object} daysOfWeekDisabled default empty
10960  * 
10961  * @cfg {Boolean} keyboardNavigation default true
10962  * @cfg {String} language default en
10963  * 
10964  * @constructor
10965  * Create a new DateField
10966  * @param {Object} config The config object
10967  */
10968
10969 Roo.bootstrap.DateField = function(config){
10970     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10971      this.addEvents({
10972             /**
10973              * @event show
10974              * Fires when this field show.
10975              * @param {Roo.bootstrap.DateField} this
10976              * @param {Mixed} date The date value
10977              */
10978             show : true,
10979             /**
10980              * @event show
10981              * Fires when this field hide.
10982              * @param {Roo.bootstrap.DateField} this
10983              * @param {Mixed} date The date value
10984              */
10985             hide : true,
10986             /**
10987              * @event select
10988              * Fires when select a date.
10989              * @param {Roo.bootstrap.DateField} this
10990              * @param {Mixed} date The date value
10991              */
10992             select : true
10993         });
10994 };
10995
10996 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10997     
10998     /**
10999      * @cfg {String} format
11000      * The default date format string which can be overriden for localization support.  The format must be
11001      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11002      */
11003     format : "m/d/y",
11004     /**
11005      * @cfg {String} altFormats
11006      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11007      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11008      */
11009     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11010     
11011     weekStart : 0,
11012     
11013     viewMode : '',
11014     
11015     minViewMode : '',
11016     
11017     todayHighlight : false,
11018     
11019     todayBtn: false,
11020     
11021     language: 'en',
11022     
11023     keyboardNavigation: true,
11024     
11025     calendarWeeks: false,
11026     
11027     startDate: -Infinity,
11028     
11029     endDate: Infinity,
11030     
11031     daysOfWeekDisabled: [],
11032     
11033     _events: [],
11034     
11035     UTCDate: function()
11036     {
11037         return new Date(Date.UTC.apply(Date, arguments));
11038     },
11039     
11040     UTCToday: function()
11041     {
11042         var today = new Date();
11043         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11044     },
11045     
11046     getDate: function() {
11047             var d = this.getUTCDate();
11048             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11049     },
11050     
11051     getUTCDate: function() {
11052             return this.date;
11053     },
11054     
11055     setDate: function(d) {
11056             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11057     },
11058     
11059     setUTCDate: function(d) {
11060             this.date = d;
11061             this.setValue(this.formatDate(this.date));
11062     },
11063         
11064     onRender: function(ct, position)
11065     {
11066         
11067         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11068         
11069         this.language = this.language || 'en';
11070         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11071         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11072         
11073         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11074         this.format = this.format || 'm/d/y';
11075         this.isInline = false;
11076         this.isInput = true;
11077         this.component = this.el.select('.add-on', true).first() || false;
11078         this.component = (this.component && this.component.length === 0) ? false : this.component;
11079         this.hasInput = this.component && this.inputEL().length;
11080         
11081         if (typeof(this.minViewMode === 'string')) {
11082             switch (this.minViewMode) {
11083                 case 'months':
11084                     this.minViewMode = 1;
11085                     break;
11086                 case 'years':
11087                     this.minViewMode = 2;
11088                     break;
11089                 default:
11090                     this.minViewMode = 0;
11091                     break;
11092             }
11093         }
11094         
11095         if (typeof(this.viewMode === 'string')) {
11096             switch (this.viewMode) {
11097                 case 'months':
11098                     this.viewMode = 1;
11099                     break;
11100                 case 'years':
11101                     this.viewMode = 2;
11102                     break;
11103                 default:
11104                     this.viewMode = 0;
11105                     break;
11106             }
11107         }
11108                 
11109         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11110         
11111         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11112         
11113         this.picker().on('mousedown', this.onMousedown, this);
11114         this.picker().on('click', this.onClick, this);
11115         
11116         this.picker().addClass('datepicker-dropdown');
11117         
11118         this.startViewMode = this.viewMode;
11119         
11120         
11121         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11122             if(!this.calendarWeeks){
11123                 v.remove();
11124                 return;
11125             };
11126             
11127             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11128             v.attr('colspan', function(i, val){
11129                 return parseInt(val) + 1;
11130             });
11131         })
11132                         
11133         
11134         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11135         
11136         this.setStartDate(this.startDate);
11137         this.setEndDate(this.endDate);
11138         
11139         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11140         
11141         this.fillDow();
11142         this.fillMonths();
11143         this.update();
11144         this.showMode();
11145         
11146         if(this.isInline) {
11147             this.show();
11148         }
11149     },
11150     
11151     picker : function()
11152     {
11153         return this.el.select('.datepicker', true).first();
11154     },
11155     
11156     fillDow: function()
11157     {
11158         var dowCnt = this.weekStart;
11159         
11160         var dow = {
11161             tag: 'tr',
11162             cn: [
11163                 
11164             ]
11165         };
11166         
11167         if(this.calendarWeeks){
11168             dow.cn.push({
11169                 tag: 'th',
11170                 cls: 'cw',
11171                 html: '&nbsp;'
11172             })
11173         }
11174         
11175         while (dowCnt < this.weekStart + 7) {
11176             dow.cn.push({
11177                 tag: 'th',
11178                 cls: 'dow',
11179                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11180             });
11181         }
11182         
11183         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11184     },
11185     
11186     fillMonths: function()
11187     {    
11188         var i = 0
11189         var months = this.picker().select('>.datepicker-months td', true).first();
11190         
11191         months.dom.innerHTML = '';
11192         
11193         while (i < 12) {
11194             var month = {
11195                 tag: 'span',
11196                 cls: 'month',
11197                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11198             }
11199             
11200             months.createChild(month);
11201         }
11202         
11203     },
11204     
11205     update: function(){
11206         
11207         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11208         
11209         if (this.date < this.startDate) {
11210             this.viewDate = new Date(this.startDate);
11211         } else if (this.date > this.endDate) {
11212             this.viewDate = new Date(this.endDate);
11213         } else {
11214             this.viewDate = new Date(this.date);
11215         }
11216         
11217         this.fill();
11218     },
11219     
11220     fill: function() {
11221         var d = new Date(this.viewDate),
11222                 year = d.getUTCFullYear(),
11223                 month = d.getUTCMonth(),
11224                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11225                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11226                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11227                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11228                 currentDate = this.date && this.date.valueOf(),
11229                 today = this.UTCToday();
11230         
11231         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11232         
11233 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11234         
11235 //        this.picker.select('>tfoot th.today').
11236 //                                              .text(dates[this.language].today)
11237 //                                              .toggle(this.todayBtn !== false);
11238     
11239         this.updateNavArrows();
11240         this.fillMonths();
11241                                                 
11242         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11243         
11244         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11245          
11246         prevMonth.setUTCDate(day);
11247         
11248         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11249         
11250         var nextMonth = new Date(prevMonth);
11251         
11252         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11253         
11254         nextMonth = nextMonth.valueOf();
11255         
11256         var fillMonths = false;
11257         
11258         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11259         
11260         while(prevMonth.valueOf() < nextMonth) {
11261             var clsName = '';
11262             
11263             if (prevMonth.getUTCDay() === this.weekStart) {
11264                 if(fillMonths){
11265                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11266                 }
11267                     
11268                 fillMonths = {
11269                     tag: 'tr',
11270                     cn: []
11271                 };
11272                 
11273                 if(this.calendarWeeks){
11274                     // ISO 8601: First week contains first thursday.
11275                     // ISO also states week starts on Monday, but we can be more abstract here.
11276                     var
11277                     // Start of current week: based on weekstart/current date
11278                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11279                     // Thursday of this week
11280                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11281                     // First Thursday of year, year from thursday
11282                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11283                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11284                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11285                     
11286                     fillMonths.cn.push({
11287                         tag: 'td',
11288                         cls: 'cw',
11289                         html: calWeek
11290                     });
11291                 }
11292             }
11293             
11294             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11295                 clsName += ' old';
11296             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11297                 clsName += ' new';
11298             }
11299             if (this.todayHighlight &&
11300                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11301                 prevMonth.getUTCMonth() == today.getMonth() &&
11302                 prevMonth.getUTCDate() == today.getDate()) {
11303                 clsName += ' today';
11304             }
11305             
11306             if (currentDate && prevMonth.valueOf() === currentDate) {
11307                 clsName += ' active';
11308             }
11309             
11310             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11311                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11312                     clsName += ' disabled';
11313             }
11314             
11315             fillMonths.cn.push({
11316                 tag: 'td',
11317                 cls: 'day ' + clsName,
11318                 html: prevMonth.getDate()
11319             })
11320             
11321             prevMonth.setDate(prevMonth.getDate()+1);
11322         }
11323           
11324         var currentYear = this.date && this.date.getUTCFullYear();
11325         var currentMonth = this.date && this.date.getUTCMonth();
11326         
11327         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11328         
11329         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11330             v.removeClass('active');
11331             
11332             if(currentYear === year && k === currentMonth){
11333                 v.addClass('active');
11334             }
11335             
11336             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11337                 v.addClass('disabled');
11338             }
11339             
11340         });
11341         
11342         
11343         year = parseInt(year/10, 10) * 10;
11344         
11345         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11346         
11347         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11348         
11349         year -= 1;
11350         for (var i = -1; i < 11; i++) {
11351             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11352                 tag: 'span',
11353                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11354                 html: year
11355             })
11356             
11357             year += 1;
11358         }
11359     },
11360     
11361     showMode: function(dir) {
11362         if (dir) {
11363             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11364         }
11365         Roo.each(this.picker().select('>div',true).elements, function(v){
11366             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11367             v.hide();
11368         });
11369         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11370     },
11371     
11372     place: function()
11373     {
11374         if(this.isInline) return;
11375         
11376         this.picker().removeClass(['bottom', 'top']);
11377         
11378         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11379             /*
11380              * place to the top of element!
11381              *
11382              */
11383             
11384             this.picker().addClass('top');
11385             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11386             
11387             return;
11388         }
11389         
11390         this.picker().addClass('bottom');
11391         
11392         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11393     },
11394     
11395     parseDate : function(value){
11396         if(!value || value instanceof Date){
11397             return value;
11398         }
11399         var v = Date.parseDate(value, this.format);
11400         if (!v && this.useIso) {
11401             v = Date.parseDate(value, 'Y-m-d');
11402         }
11403         if(!v && this.altFormats){
11404             if(!this.altFormatsArray){
11405                 this.altFormatsArray = this.altFormats.split("|");
11406             }
11407             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11408                 v = Date.parseDate(value, this.altFormatsArray[i]);
11409             }
11410         }
11411         return v;
11412     },
11413     
11414     formatDate : function(date, fmt){
11415         return (!date || !(date instanceof Date)) ?
11416         date : date.dateFormat(fmt || this.format);
11417     },
11418     
11419     onFocus : function()
11420     {
11421         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11422         this.show();
11423     },
11424     
11425     onBlur : function()
11426     {
11427         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11428         this.hide();
11429     },
11430     
11431     show : function()
11432     {
11433         this.picker().show();
11434         this.update();
11435         this.place();
11436         
11437         this.fireEvent('show', this, this.date);
11438     },
11439     
11440     hide : function()
11441     {
11442         if(this.isInline) return;
11443         this.picker().hide();
11444         this.viewMode = this.startViewMode;
11445         this.showMode();
11446         
11447         this.fireEvent('hide', this, this.date);
11448         
11449     },
11450     
11451     onMousedown: function(e){
11452         e.stopPropagation();
11453         e.preventDefault();
11454     },
11455     
11456     keyup: function(e){
11457         Roo.bootstrap.DateField.superclass.keyup.call(this);
11458         this.update();
11459         
11460     },
11461
11462     setValue: function(v){
11463         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11464         
11465         this.fireEvent('select', this, this.date);
11466         
11467     },
11468     
11469     fireKey: function(e){
11470         if (!this.picker().isVisible()){
11471             if (e.keyCode == 27) // allow escape to hide and re-show picker
11472                 this.show();
11473             return;
11474         }
11475         var dateChanged = false,
11476         dir, day, month,
11477         newDate, newViewDate;
11478         switch(e.keyCode){
11479             case 27: // escape
11480                 this.hide();
11481                 e.preventDefault();
11482                 break;
11483             case 37: // left
11484             case 39: // right
11485                 if (!this.keyboardNavigation) break;
11486                 dir = e.keyCode == 37 ? -1 : 1;
11487                 
11488                 if (e.ctrlKey){
11489                     newDate = this.moveYear(this.date, dir);
11490                     newViewDate = this.moveYear(this.viewDate, dir);
11491                 } else if (e.shiftKey){
11492                     newDate = this.moveMonth(this.date, dir);
11493                     newViewDate = this.moveMonth(this.viewDate, dir);
11494                 } else {
11495                     newDate = new Date(this.date);
11496                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11497                     newViewDate = new Date(this.viewDate);
11498                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11499                 }
11500                 if (this.dateWithinRange(newDate)){
11501                     this.date = newDate;
11502                     this.viewDate = newViewDate;
11503                     this.setValue(this.formatDate(this.date));
11504                     this.update();
11505                     e.preventDefault();
11506                     dateChanged = true;
11507                 }
11508                 break;
11509             case 38: // up
11510             case 40: // down
11511                 if (!this.keyboardNavigation) break;
11512                 dir = e.keyCode == 38 ? -1 : 1;
11513                 if (e.ctrlKey){
11514                     newDate = this.moveYear(this.date, dir);
11515                     newViewDate = this.moveYear(this.viewDate, dir);
11516                 } else if (e.shiftKey){
11517                     newDate = this.moveMonth(this.date, dir);
11518                     newViewDate = this.moveMonth(this.viewDate, dir);
11519                 } else {
11520                     newDate = new Date(this.date);
11521                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11522                     newViewDate = new Date(this.viewDate);
11523                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11524                 }
11525                 if (this.dateWithinRange(newDate)){
11526                     this.date = newDate;
11527                     this.viewDate = newViewDate;
11528                     this.setValue(this.formatDate(this.date));
11529                     this.update();
11530                     e.preventDefault();
11531                     dateChanged = true;
11532                 }
11533                 break;
11534             case 13: // enter
11535                 this.setValue(this.formatDate(this.date));
11536                 this.hide();
11537                 e.preventDefault();
11538                 break;
11539             case 9: // tab
11540                 this.setValue(this.formatDate(this.date));
11541                 this.hide();
11542                 break;
11543         }
11544     },
11545     
11546     
11547     onClick: function(e) {
11548         e.stopPropagation();
11549         e.preventDefault();
11550         
11551         var target = e.getTarget();
11552         
11553         if(target.nodeName.toLowerCase() === 'i'){
11554             target = Roo.get(target).dom.parentNode;
11555         }
11556         
11557         var nodeName = target.nodeName;
11558         var className = target.className;
11559         var html = target.innerHTML;
11560         
11561         switch(nodeName.toLowerCase()) {
11562             case 'th':
11563                 switch(className) {
11564                     case 'switch':
11565                         this.showMode(1);
11566                         break;
11567                     case 'prev':
11568                     case 'next':
11569                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11570                         switch(this.viewMode){
11571                                 case 0:
11572                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11573                                         break;
11574                                 case 1:
11575                                 case 2:
11576                                         this.viewDate = this.moveYear(this.viewDate, dir);
11577                                         break;
11578                         }
11579                         this.fill();
11580                         break;
11581                     case 'today':
11582                         var date = new Date();
11583                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11584                         this.fill()
11585                         this.setValue(this.formatDate(this.date));
11586                         this.hide();
11587                         break;
11588                 }
11589                 break;
11590             case 'span':
11591                 if (className.indexOf('disabled') === -1) {
11592                     this.viewDate.setUTCDate(1);
11593                     if (className.indexOf('month') !== -1) {
11594                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11595                     } else {
11596                         var year = parseInt(html, 10) || 0;
11597                         this.viewDate.setUTCFullYear(year);
11598                         
11599                     }
11600                     this.showMode(-1);
11601                     this.fill();
11602                 }
11603                 break;
11604                 
11605             case 'td':
11606                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11607                     var day = parseInt(html, 10) || 1;
11608                     var year = this.viewDate.getUTCFullYear(),
11609                         month = this.viewDate.getUTCMonth();
11610
11611                     if (className.indexOf('old') !== -1) {
11612                         if(month === 0 ){
11613                             month = 11;
11614                             year -= 1;
11615                         }else{
11616                             month -= 1;
11617                         }
11618                     } else if (className.indexOf('new') !== -1) {
11619                         if (month == 11) {
11620                             month = 0;
11621                             year += 1;
11622                         } else {
11623                             month += 1;
11624                         }
11625                     }
11626                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11627                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11628                     this.fill();
11629                     this.setValue(this.formatDate(this.date));
11630                     this.hide();
11631                 }
11632                 break;
11633         }
11634     },
11635     
11636     setStartDate: function(startDate){
11637         this.startDate = startDate || -Infinity;
11638         if (this.startDate !== -Infinity) {
11639             this.startDate = this.parseDate(this.startDate);
11640         }
11641         this.update();
11642         this.updateNavArrows();
11643     },
11644
11645     setEndDate: function(endDate){
11646         this.endDate = endDate || Infinity;
11647         if (this.endDate !== Infinity) {
11648             this.endDate = this.parseDate(this.endDate);
11649         }
11650         this.update();
11651         this.updateNavArrows();
11652     },
11653     
11654     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11655         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11656         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11657             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11658         }
11659         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11660             return parseInt(d, 10);
11661         });
11662         this.update();
11663         this.updateNavArrows();
11664     },
11665     
11666     updateNavArrows: function() {
11667         var d = new Date(this.viewDate),
11668         year = d.getUTCFullYear(),
11669         month = d.getUTCMonth();
11670         
11671         Roo.each(this.picker().select('.prev', true).elements, function(v){
11672             v.show();
11673             switch (this.viewMode) {
11674                 case 0:
11675
11676                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11677                         v.hide();
11678                     }
11679                     break;
11680                 case 1:
11681                 case 2:
11682                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11683                         v.hide();
11684                     }
11685                     break;
11686             }
11687         });
11688         
11689         Roo.each(this.picker().select('.next', true).elements, function(v){
11690             v.show();
11691             switch (this.viewMode) {
11692                 case 0:
11693
11694                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11695                         v.hide();
11696                     }
11697                     break;
11698                 case 1:
11699                 case 2:
11700                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11701                         v.hide();
11702                     }
11703                     break;
11704             }
11705         })
11706     },
11707     
11708     moveMonth: function(date, dir){
11709         if (!dir) return date;
11710         var new_date = new Date(date.valueOf()),
11711         day = new_date.getUTCDate(),
11712         month = new_date.getUTCMonth(),
11713         mag = Math.abs(dir),
11714         new_month, test;
11715         dir = dir > 0 ? 1 : -1;
11716         if (mag == 1){
11717             test = dir == -1
11718             // If going back one month, make sure month is not current month
11719             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11720             ? function(){
11721                 return new_date.getUTCMonth() == month;
11722             }
11723             // If going forward one month, make sure month is as expected
11724             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11725             : function(){
11726                 return new_date.getUTCMonth() != new_month;
11727             };
11728             new_month = month + dir;
11729             new_date.setUTCMonth(new_month);
11730             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11731             if (new_month < 0 || new_month > 11)
11732                 new_month = (new_month + 12) % 12;
11733         } else {
11734             // For magnitudes >1, move one month at a time...
11735             for (var i=0; i<mag; i++)
11736                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11737                 new_date = this.moveMonth(new_date, dir);
11738             // ...then reset the day, keeping it in the new month
11739             new_month = new_date.getUTCMonth();
11740             new_date.setUTCDate(day);
11741             test = function(){
11742                 return new_month != new_date.getUTCMonth();
11743             };
11744         }
11745         // Common date-resetting loop -- if date is beyond end of month, make it
11746         // end of month
11747         while (test()){
11748             new_date.setUTCDate(--day);
11749             new_date.setUTCMonth(new_month);
11750         }
11751         return new_date;
11752     },
11753
11754     moveYear: function(date, dir){
11755         return this.moveMonth(date, dir*12);
11756     },
11757
11758     dateWithinRange: function(date){
11759         return date >= this.startDate && date <= this.endDate;
11760     },
11761
11762     
11763     remove: function() {
11764         this.picker().remove();
11765     }
11766    
11767 });
11768
11769 Roo.apply(Roo.bootstrap.DateField,  {
11770     
11771     head : {
11772         tag: 'thead',
11773         cn: [
11774         {
11775             tag: 'tr',
11776             cn: [
11777             {
11778                 tag: 'th',
11779                 cls: 'prev',
11780                 html: '<i class="icon-arrow-left"/>'
11781             },
11782             {
11783                 tag: 'th',
11784                 cls: 'switch',
11785                 colspan: '5'
11786             },
11787             {
11788                 tag: 'th',
11789                 cls: 'next',
11790                 html: '<i class="icon-arrow-right"/>'
11791             }
11792
11793             ]
11794         }
11795         ]
11796     },
11797     
11798     content : {
11799         tag: 'tbody',
11800         cn: [
11801         {
11802             tag: 'tr',
11803             cn: [
11804             {
11805                 tag: 'td',
11806                 colspan: '7'
11807             }
11808             ]
11809         }
11810         ]
11811     },
11812     
11813     footer : {
11814         tag: 'tfoot',
11815         cn: [
11816         {
11817             tag: 'tr',
11818             cn: [
11819             {
11820                 tag: 'th',
11821                 colspan: '7',
11822                 cls: 'today'
11823             }
11824                     
11825             ]
11826         }
11827         ]
11828     },
11829     
11830     dates:{
11831         en: {
11832             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11833             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11834             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11835             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11836             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11837             today: "Today"
11838         }
11839     },
11840     
11841     modes: [
11842     {
11843         clsName: 'days',
11844         navFnc: 'Month',
11845         navStep: 1
11846     },
11847     {
11848         clsName: 'months',
11849         navFnc: 'FullYear',
11850         navStep: 1
11851     },
11852     {
11853         clsName: 'years',
11854         navFnc: 'FullYear',
11855         navStep: 10
11856     }]
11857 });
11858
11859 Roo.apply(Roo.bootstrap.DateField,  {
11860   
11861     template : {
11862         tag: 'div',
11863         cls: 'datepicker dropdown-menu',
11864         cn: [
11865         {
11866             tag: 'div',
11867             cls: 'datepicker-days',
11868             cn: [
11869             {
11870                 tag: 'table',
11871                 cls: 'table-condensed',
11872                 cn:[
11873                 Roo.bootstrap.DateField.head,
11874                 {
11875                     tag: 'tbody'
11876                 },
11877                 Roo.bootstrap.DateField.footer
11878                 ]
11879             }
11880             ]
11881         },
11882         {
11883             tag: 'div',
11884             cls: 'datepicker-months',
11885             cn: [
11886             {
11887                 tag: 'table',
11888                 cls: 'table-condensed',
11889                 cn:[
11890                 Roo.bootstrap.DateField.head,
11891                 Roo.bootstrap.DateField.content,
11892                 Roo.bootstrap.DateField.footer
11893                 ]
11894             }
11895             ]
11896         },
11897         {
11898             tag: 'div',
11899             cls: 'datepicker-years',
11900             cn: [
11901             {
11902                 tag: 'table',
11903                 cls: 'table-condensed',
11904                 cn:[
11905                 Roo.bootstrap.DateField.head,
11906                 Roo.bootstrap.DateField.content,
11907                 Roo.bootstrap.DateField.footer
11908                 ]
11909             }
11910             ]
11911         }
11912         ]
11913     }
11914 });
11915
11916  
11917
11918  /*
11919  * - LGPL
11920  *
11921  * TimeField
11922  * 
11923  */
11924
11925 /**
11926  * @class Roo.bootstrap.TimeField
11927  * @extends Roo.bootstrap.Input
11928  * Bootstrap DateField class
11929  * 
11930  * 
11931  * @constructor
11932  * Create a new TimeField
11933  * @param {Object} config The config object
11934  */
11935
11936 Roo.bootstrap.TimeField = function(config){
11937     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11938     this.addEvents({
11939             /**
11940              * @event show
11941              * Fires when this field show.
11942              * @param {Roo.bootstrap.DateField} this
11943              * @param {Mixed} date The date value
11944              */
11945             show : true,
11946             /**
11947              * @event show
11948              * Fires when this field hide.
11949              * @param {Roo.bootstrap.DateField} this
11950              * @param {Mixed} date The date value
11951              */
11952             hide : true,
11953             /**
11954              * @event select
11955              * Fires when select a date.
11956              * @param {Roo.bootstrap.DateField} this
11957              * @param {Mixed} date The date value
11958              */
11959             select : true
11960         });
11961 };
11962
11963 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11964     
11965     /**
11966      * @cfg {String} format
11967      * The default time format string which can be overriden for localization support.  The format must be
11968      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11969      */
11970     format : "H:i",
11971        
11972     onRender: function(ct, position)
11973     {
11974         
11975         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11976                 
11977         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11978         
11979         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11980         
11981         this.pop = this.picker().select('>.datepicker-time',true).first();
11982         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11983         
11984         this.picker().on('mousedown', this.onMousedown, this);
11985         this.picker().on('click', this.onClick, this);
11986         
11987         this.picker().addClass('datepicker-dropdown');
11988     
11989         this.fillTime();
11990         this.update();
11991             
11992         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11993         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11994         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11995         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11996         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11997         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11998
11999     },
12000     
12001     fireKey: function(e){
12002         if (!this.picker().isVisible()){
12003             if (e.keyCode == 27) // allow escape to hide and re-show picker
12004                 this.show();
12005             return;
12006         }
12007
12008         e.preventDefault();
12009         
12010         switch(e.keyCode){
12011             case 27: // escape
12012                 this.hide();
12013                 break;
12014             case 37: // left
12015             case 39: // right
12016                 this.onTogglePeriod();
12017                 break;
12018             case 38: // up
12019                 this.onIncrementMinutes();
12020                 break;
12021             case 40: // down
12022                 this.onDecrementMinutes();
12023                 break;
12024             case 13: // enter
12025             case 9: // tab
12026                 this.setTime();
12027                 break;
12028         }
12029     },
12030     
12031     onClick: function(e) {
12032         e.stopPropagation();
12033         e.preventDefault();
12034     },
12035     
12036     picker : function()
12037     {
12038         return this.el.select('.datepicker', true).first();
12039     },
12040     
12041     fillTime: function()
12042     {    
12043         var time = this.pop.select('tbody', true).first();
12044         
12045         time.dom.innerHTML = '';
12046         
12047         time.createChild({
12048             tag: 'tr',
12049             cn: [
12050                 {
12051                     tag: 'td',
12052                     cn: [
12053                         {
12054                             tag: 'a',
12055                             href: '#',
12056                             cls: 'btn',
12057                             cn: [
12058                                 {
12059                                     tag: 'span',
12060                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12061                                 }
12062                             ]
12063                         } 
12064                     ]
12065                 },
12066                 {
12067                     tag: 'td',
12068                     cls: 'separator'
12069                 },
12070                 {
12071                     tag: 'td',
12072                     cn: [
12073                         {
12074                             tag: 'a',
12075                             href: '#',
12076                             cls: 'btn',
12077                             cn: [
12078                                 {
12079                                     tag: 'span',
12080                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12081                                 }
12082                             ]
12083                         }
12084                     ]
12085                 },
12086                 {
12087                     tag: 'td',
12088                     cls: 'separator'
12089                 }
12090             ]
12091         });
12092         
12093         time.createChild({
12094             tag: 'tr',
12095             cn: [
12096                 {
12097                     tag: 'td',
12098                     cn: [
12099                         {
12100                             tag: 'span',
12101                             cls: 'timepicker-hour',
12102                             html: '00'
12103                         }  
12104                     ]
12105                 },
12106                 {
12107                     tag: 'td',
12108                     cls: 'separator',
12109                     html: ':'
12110                 },
12111                 {
12112                     tag: 'td',
12113                     cn: [
12114                         {
12115                             tag: 'span',
12116                             cls: 'timepicker-minute',
12117                             html: '00'
12118                         }  
12119                     ]
12120                 },
12121                 {
12122                     tag: 'td',
12123                     cls: 'separator'
12124                 },
12125                 {
12126                     tag: 'td',
12127                     cn: [
12128                         {
12129                             tag: 'button',
12130                             type: 'button',
12131                             cls: 'btn btn-primary period',
12132                             html: 'AM'
12133                             
12134                         }
12135                     ]
12136                 }
12137             ]
12138         });
12139         
12140         time.createChild({
12141             tag: 'tr',
12142             cn: [
12143                 {
12144                     tag: 'td',
12145                     cn: [
12146                         {
12147                             tag: 'a',
12148                             href: '#',
12149                             cls: 'btn',
12150                             cn: [
12151                                 {
12152                                     tag: 'span',
12153                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12154                                 }
12155                             ]
12156                         }
12157                     ]
12158                 },
12159                 {
12160                     tag: 'td',
12161                     cls: 'separator'
12162                 },
12163                 {
12164                     tag: 'td',
12165                     cn: [
12166                         {
12167                             tag: 'a',
12168                             href: '#',
12169                             cls: 'btn',
12170                             cn: [
12171                                 {
12172                                     tag: 'span',
12173                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12174                                 }
12175                             ]
12176                         }
12177                     ]
12178                 },
12179                 {
12180                     tag: 'td',
12181                     cls: 'separator'
12182                 }
12183             ]
12184         });
12185         
12186     },
12187     
12188     update: function()
12189     {
12190         
12191         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12192         
12193         this.fill();
12194     },
12195     
12196     fill: function() 
12197     {
12198         var hours = this.time.getHours();
12199         var minutes = this.time.getMinutes();
12200         var period = 'AM';
12201         
12202         if(hours > 11){
12203             period = 'PM';
12204         }
12205         
12206         if(hours == 0){
12207             hours = 12;
12208         }
12209         
12210         
12211         if(hours > 12){
12212             hours = hours - 12;
12213         }
12214         
12215         if(hours < 10){
12216             hours = '0' + hours;
12217         }
12218         
12219         if(minutes < 10){
12220             minutes = '0' + minutes;
12221         }
12222         
12223         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12224         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12225         this.pop.select('button', true).first().dom.innerHTML = period;
12226         
12227     },
12228     
12229     place: function()
12230     {   
12231         this.picker().removeClass(['bottom', 'top']);
12232         
12233         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12234             /*
12235              * place to the top of element!
12236              *
12237              */
12238             
12239             this.picker().addClass('top');
12240             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12241             
12242             return;
12243         }
12244         
12245         this.picker().addClass('bottom');
12246         
12247         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12248     },
12249   
12250     onFocus : function()
12251     {
12252         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12253         this.show();
12254     },
12255     
12256     onBlur : function()
12257     {
12258         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12259         this.hide();
12260     },
12261     
12262     show : function()
12263     {
12264         this.picker().show();
12265         this.pop.show();
12266         this.update();
12267         this.place();
12268         
12269         this.fireEvent('show', this, this.date);
12270     },
12271     
12272     hide : function()
12273     {
12274         this.picker().hide();
12275         this.pop.hide();
12276         
12277         this.fireEvent('hide', this, this.date);
12278     },
12279     
12280     setTime : function()
12281     {
12282         this.hide();
12283         this.setValue(this.time.format(this.format));
12284         
12285         this.fireEvent('select', this, this.date);
12286         
12287         
12288     },
12289     
12290     onMousedown: function(e){
12291         e.stopPropagation();
12292         e.preventDefault();
12293     },
12294     
12295     onIncrementHours: function()
12296     {
12297         Roo.log('onIncrementHours');
12298         this.time = this.time.add(Date.HOUR, 1);
12299         this.update();
12300         
12301     },
12302     
12303     onDecrementHours: function()
12304     {
12305         Roo.log('onDecrementHours');
12306         this.time = this.time.add(Date.HOUR, -1);
12307         this.update();
12308     },
12309     
12310     onIncrementMinutes: function()
12311     {
12312         Roo.log('onIncrementMinutes');
12313         this.time = this.time.add(Date.MINUTE, 1);
12314         this.update();
12315     },
12316     
12317     onDecrementMinutes: function()
12318     {
12319         Roo.log('onDecrementMinutes');
12320         this.time = this.time.add(Date.MINUTE, -1);
12321         this.update();
12322     },
12323     
12324     onTogglePeriod: function()
12325     {
12326         Roo.log('onTogglePeriod');
12327         this.time = this.time.add(Date.HOUR, 12);
12328         this.update();
12329     }
12330     
12331    
12332 });
12333
12334 Roo.apply(Roo.bootstrap.TimeField,  {
12335     
12336     content : {
12337         tag: 'tbody',
12338         cn: [
12339             {
12340                 tag: 'tr',
12341                 cn: [
12342                 {
12343                     tag: 'td',
12344                     colspan: '7'
12345                 }
12346                 ]
12347             }
12348         ]
12349     },
12350     
12351     footer : {
12352         tag: 'tfoot',
12353         cn: [
12354             {
12355                 tag: 'tr',
12356                 cn: [
12357                 {
12358                     tag: 'th',
12359                     colspan: '7',
12360                     cls: '',
12361                     cn: [
12362                         {
12363                             tag: 'button',
12364                             cls: 'btn btn-info ok',
12365                             html: 'OK'
12366                         }
12367                     ]
12368                 }
12369
12370                 ]
12371             }
12372         ]
12373     }
12374 });
12375
12376 Roo.apply(Roo.bootstrap.TimeField,  {
12377   
12378     template : {
12379         tag: 'div',
12380         cls: 'datepicker dropdown-menu',
12381         cn: [
12382             {
12383                 tag: 'div',
12384                 cls: 'datepicker-time',
12385                 cn: [
12386                 {
12387                     tag: 'table',
12388                     cls: 'table-condensed',
12389                     cn:[
12390                     Roo.bootstrap.TimeField.content,
12391                     Roo.bootstrap.TimeField.footer
12392                     ]
12393                 }
12394                 ]
12395             }
12396         ]
12397     }
12398 });
12399
12400  
12401
12402  /*
12403  * - LGPL
12404  *
12405  * CheckBox
12406  * 
12407  */
12408
12409 /**
12410  * @class Roo.bootstrap.CheckBox
12411  * @extends Roo.bootstrap.Input
12412  * Bootstrap CheckBox class
12413  * 
12414  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12415  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12416  * @cfg {String} boxLabel The text that appears beside the checkbox
12417  * @cfg {Boolean} checked initnal the element
12418  * 
12419  * @constructor
12420  * Create a new CheckBox
12421  * @param {Object} config The config object
12422  */
12423
12424 Roo.bootstrap.CheckBox = function(config){
12425     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12426    
12427         this.addEvents({
12428             /**
12429             * @event check
12430             * Fires when the element is checked or unchecked.
12431             * @param {Roo.bootstrap.CheckBox} this This input
12432             * @param {Boolean} checked The new checked value
12433             */
12434            check : true
12435         });
12436 };
12437
12438 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12439     
12440     inputType: 'checkbox',
12441     inputValue: 1,
12442     valueOff: 0,
12443     boxLabel: false,
12444     checked: false,
12445     
12446     getAutoCreate : function()
12447     {
12448         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12449         
12450         var id = Roo.id();
12451         
12452         var cfg = {};
12453         
12454         cfg.cls = 'form-group' //input-group
12455         
12456         var input =  {
12457             tag: 'input',
12458             id : id,
12459             type : this.inputType,
12460             value : (!this.checked) ? this.valueOff : this.inputValue,
12461             cls : 'form-box',
12462             placeholder : this.placeholder || ''
12463             
12464         };
12465         
12466         if (this.disabled) {
12467             input.disabled=true;
12468         }
12469         
12470         if(this.checked){
12471             input.checked = this.checked;
12472         }
12473         
12474         if (this.name) {
12475             input.name = this.name;
12476         }
12477         
12478         if (this.size) {
12479             input.cls += ' input-' + this.size;
12480         }
12481         
12482         var settings=this;
12483         ['xs','sm','md','lg'].map(function(size){
12484             if (settings[size]) {
12485                 cfg.cls += ' col-' + size + '-' + settings[size];
12486             }
12487         });
12488         
12489         var inputblock = input;
12490         
12491         if (this.before || this.after) {
12492             
12493             inputblock = {
12494                 cls : 'input-group',
12495                 cn :  [] 
12496             };
12497             if (this.before) {
12498                 inputblock.cn.push({
12499                     tag :'span',
12500                     cls : 'input-group-addon',
12501                     html : this.before
12502                 });
12503             }
12504             inputblock.cn.push(input);
12505             if (this.after) {
12506                 inputblock.cn.push({
12507                     tag :'span',
12508                     cls : 'input-group-addon',
12509                     html : this.after
12510                 });
12511             }
12512             
12513         };
12514         
12515         if (align ==='left' && this.fieldLabel.length) {
12516                 Roo.log("left and has label");
12517                 cfg.cn = [
12518                     
12519                     {
12520                         tag: 'label',
12521                         'for' :  id,
12522                         cls : 'control-label col-md-' + this.labelWidth,
12523                         html : this.fieldLabel
12524                         
12525                     },
12526                     {
12527                         cls : "col-md-" + (12 - this.labelWidth), 
12528                         cn: [
12529                             inputblock
12530                         ]
12531                     }
12532                     
12533                 ];
12534         } else if ( this.fieldLabel.length) {
12535                 Roo.log(" label");
12536                 cfg.cn = [
12537                    
12538                     {
12539                         tag: this.boxLabel ? 'span' : 'label',
12540                         'for': id,
12541                         cls: 'control-label box-input-label',
12542                         //cls : 'input-group-addon',
12543                         html : this.fieldLabel
12544                         
12545                     },
12546                     
12547                     inputblock
12548                     
12549                 ];
12550
12551         } else {
12552             
12553                    Roo.log(" no label && no align");
12554                 cfg.cn = [
12555                     
12556                         inputblock
12557                     
12558                 ];
12559                 
12560                 
12561         };
12562         
12563         if(this.boxLabel){
12564             cfg.cn.push({
12565                 tag: 'label',
12566                 'for': id,
12567                 cls: 'box-label',
12568                 html: this.boxLabel
12569             })
12570         }
12571         
12572         return cfg;
12573         
12574     },
12575     
12576     /**
12577      * return the real input element.
12578      */
12579     inputEl: function ()
12580     {
12581         return this.el.select('input.form-box',true).first();
12582     },
12583     
12584     initEvents : function()
12585     {
12586 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12587         
12588         this.inputEl().on('click', this.onClick,  this);
12589         
12590     },
12591     
12592     onClick : function()
12593     {   
12594         this.setChecked(!this.checked);
12595     },
12596     
12597     setChecked : function(state,suppressEvent)
12598     {
12599         this.checked = state;
12600         
12601         this.inputEl().dom.checked = state;
12602         
12603         if(suppressEvent !== true){
12604             this.fireEvent('check', this, state);
12605         }
12606         
12607         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12608         
12609     },
12610     
12611     setValue : function(v,suppressEvent)
12612     {
12613         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12614     }
12615     
12616 });
12617
12618  
12619 /*
12620  * - LGPL
12621  *
12622  * Radio
12623  * 
12624  */
12625
12626 /**
12627  * @class Roo.bootstrap.Radio
12628  * @extends Roo.bootstrap.CheckBox
12629  * Bootstrap Radio class
12630
12631  * @constructor
12632  * Create a new Radio
12633  * @param {Object} config The config object
12634  */
12635
12636 Roo.bootstrap.Radio = function(config){
12637     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12638    
12639 };
12640
12641 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12642     
12643     inputType: 'radio',
12644     inputValue: '',
12645     valueOff: '',
12646     
12647     getAutoCreate : function()
12648     {
12649         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12650         
12651         var id = Roo.id();
12652         
12653         var cfg = {};
12654         
12655         cfg.cls = 'form-group' //input-group
12656         
12657         var input =  {
12658             tag: 'input',
12659             id : id,
12660             type : this.inputType,
12661             value : (!this.checked) ? this.valueOff : this.inputValue,
12662             cls : 'form-box',
12663             placeholder : this.placeholder || ''
12664             
12665         };
12666         
12667         if (this.disabled) {
12668             input.disabled=true;
12669         }
12670         
12671         if(this.checked){
12672             input.checked = this.checked;
12673         }
12674         
12675         if (this.name) {
12676             input.name = this.name;
12677         }
12678         
12679         if (this.size) {
12680             input.cls += ' input-' + this.size;
12681         }
12682         
12683         var settings=this;
12684         ['xs','sm','md','lg'].map(function(size){
12685             if (settings[size]) {
12686                 cfg.cls += ' col-' + size + '-' + settings[size];
12687             }
12688         });
12689         
12690         var inputblock = input;
12691         
12692         if (this.before || this.after) {
12693             
12694             inputblock = {
12695                 cls : 'input-group',
12696                 cn :  [] 
12697             };
12698             if (this.before) {
12699                 inputblock.cn.push({
12700                     tag :'span',
12701                     cls : 'input-group-addon',
12702                     html : this.before
12703                 });
12704             }
12705             inputblock.cn.push(input);
12706             if (this.after) {
12707                 inputblock.cn.push({
12708                     tag :'span',
12709                     cls : 'input-group-addon',
12710                     html : this.after
12711                 });
12712             }
12713             
12714         };
12715         
12716         if (align ==='left' && this.fieldLabel.length) {
12717                 Roo.log("left and has label");
12718                 cfg.cn = [
12719                     
12720                     {
12721                         tag: 'label',
12722                         'for' :  id,
12723                         cls : 'control-label col-md-' + this.labelWidth,
12724                         html : this.fieldLabel
12725                         
12726                     },
12727                     {
12728                         cls : "col-md-" + (12 - this.labelWidth), 
12729                         cn: [
12730                             inputblock
12731                         ]
12732                     }
12733                     
12734                 ];
12735         } else if ( this.fieldLabel.length) {
12736                 Roo.log(" label");
12737                  cfg.cn = [
12738                    
12739                     {
12740                         tag: 'label',
12741                         'for': id,
12742                         cls: 'control-label box-input-label',
12743                         //cls : 'input-group-addon',
12744                         html : this.fieldLabel
12745                         
12746                     },
12747                     
12748                     inputblock
12749                     
12750                 ];
12751
12752         } else {
12753             
12754                    Roo.log(" no label && no align");
12755                 cfg.cn = [
12756                     
12757                         inputblock
12758                     
12759                 ];
12760                 
12761                 
12762         };
12763         
12764         if(this.boxLabel){
12765             cfg.cn.push({
12766                 tag: 'label',
12767                 'for': id,
12768                 cls: 'box-label',
12769                 html: this.boxLabel
12770             })
12771         }
12772         
12773         return cfg;
12774         
12775     },
12776    
12777     onClick : function()
12778     {   
12779         this.setChecked(true);
12780     },
12781     
12782     setChecked : function(state,suppressEvent)
12783     {
12784         if(state){
12785             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12786                 v.dom.checked = false;
12787             });
12788         }
12789         
12790         this.checked = state;
12791         this.inputEl().dom.checked = state;
12792         
12793         if(suppressEvent !== true){
12794             this.fireEvent('check', this, state);
12795         }
12796         
12797         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12798         
12799     },
12800     
12801     getGroupValue : function()
12802     {
12803         var value = ''
12804         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12805             if(v.dom.checked == true){
12806                 value = v.dom.value;
12807             }
12808         });
12809         
12810         return value;
12811     },
12812     
12813     /**
12814      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12815      * @return {Mixed} value The field value
12816      */
12817     getValue : function(){
12818         return this.getGroupValue();
12819     }
12820     
12821 });
12822
12823  
12824 //<script type="text/javascript">
12825
12826 /*
12827  * Based  Ext JS Library 1.1.1
12828  * Copyright(c) 2006-2007, Ext JS, LLC.
12829  * LGPL
12830  *
12831  */
12832  
12833 /**
12834  * @class Roo.HtmlEditorCore
12835  * @extends Roo.Component
12836  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12837  *
12838  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12839  */
12840
12841 Roo.HtmlEditorCore = function(config){
12842     
12843     
12844     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12845     this.addEvents({
12846         /**
12847          * @event initialize
12848          * Fires when the editor is fully initialized (including the iframe)
12849          * @param {Roo.HtmlEditorCore} this
12850          */
12851         initialize: true,
12852         /**
12853          * @event activate
12854          * Fires when the editor is first receives the focus. Any insertion must wait
12855          * until after this event.
12856          * @param {Roo.HtmlEditorCore} this
12857          */
12858         activate: true,
12859          /**
12860          * @event beforesync
12861          * Fires before the textarea is updated with content from the editor iframe. Return false
12862          * to cancel the sync.
12863          * @param {Roo.HtmlEditorCore} this
12864          * @param {String} html
12865          */
12866         beforesync: true,
12867          /**
12868          * @event beforepush
12869          * Fires before the iframe editor is updated with content from the textarea. Return false
12870          * to cancel the push.
12871          * @param {Roo.HtmlEditorCore} this
12872          * @param {String} html
12873          */
12874         beforepush: true,
12875          /**
12876          * @event sync
12877          * Fires when the textarea is updated with content from the editor iframe.
12878          * @param {Roo.HtmlEditorCore} this
12879          * @param {String} html
12880          */
12881         sync: true,
12882          /**
12883          * @event push
12884          * Fires when the iframe editor is updated with content from the textarea.
12885          * @param {Roo.HtmlEditorCore} this
12886          * @param {String} html
12887          */
12888         push: true,
12889         
12890         /**
12891          * @event editorevent
12892          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12893          * @param {Roo.HtmlEditorCore} this
12894          */
12895         editorevent: true
12896     });
12897      
12898 };
12899
12900
12901 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12902
12903
12904      /**
12905      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12906      */
12907     
12908     owner : false,
12909     
12910      /**
12911      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12912      *                        Roo.resizable.
12913      */
12914     resizable : false,
12915      /**
12916      * @cfg {Number} height (in pixels)
12917      */   
12918     height: 300,
12919    /**
12920      * @cfg {Number} width (in pixels)
12921      */   
12922     width: 500,
12923     
12924     /**
12925      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12926      * 
12927      */
12928     stylesheets: false,
12929     
12930     // id of frame..
12931     frameId: false,
12932     
12933     // private properties
12934     validationEvent : false,
12935     deferHeight: true,
12936     initialized : false,
12937     activated : false,
12938     sourceEditMode : false,
12939     onFocus : Roo.emptyFn,
12940     iframePad:3,
12941     hideMode:'offsets',
12942     
12943     clearUp: true,
12944     
12945      
12946     
12947
12948     /**
12949      * Protected method that will not generally be called directly. It
12950      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12951      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12952      */
12953     getDocMarkup : function(){
12954         // body styles..
12955         var st = '';
12956         Roo.log(this.stylesheets);
12957         
12958         // inherit styels from page...?? 
12959         if (this.stylesheets === false) {
12960             
12961             Roo.get(document.head).select('style').each(function(node) {
12962                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12963             });
12964             
12965             Roo.get(document.head).select('link').each(function(node) { 
12966                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12967             });
12968             
12969         } else if (!this.stylesheets.length) {
12970                 // simple..
12971                 st = '<style type="text/css">' +
12972                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12973                    '</style>';
12974         } else {
12975             Roo.each(this.stylesheets, function(s) {
12976                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12977             });
12978             
12979         }
12980         
12981         st +=  '<style type="text/css">' +
12982             'IMG { cursor: pointer } ' +
12983         '</style>';
12984
12985         
12986         return '<html><head>' + st  +
12987             //<style type="text/css">' +
12988             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12989             //'</style>' +
12990             ' </head><body class="roo-htmleditor-body"></body></html>';
12991     },
12992
12993     // private
12994     onRender : function(ct, position)
12995     {
12996         var _t = this;
12997         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12998         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12999         
13000         
13001         this.el.dom.style.border = '0 none';
13002         this.el.dom.setAttribute('tabIndex', -1);
13003         this.el.addClass('x-hidden hide');
13004         
13005         
13006         
13007         if(Roo.isIE){ // fix IE 1px bogus margin
13008             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13009         }
13010        
13011         
13012         this.frameId = Roo.id();
13013         
13014          
13015         
13016         var iframe = this.owner.wrap.createChild({
13017             tag: 'iframe',
13018             cls: 'form-control', // bootstrap..
13019             id: this.frameId,
13020             name: this.frameId,
13021             frameBorder : 'no',
13022             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13023         }, this.el
13024         );
13025         
13026         
13027         this.iframe = iframe.dom;
13028
13029          this.assignDocWin();
13030         
13031         this.doc.designMode = 'on';
13032        
13033         this.doc.open();
13034         this.doc.write(this.getDocMarkup());
13035         this.doc.close();
13036
13037         
13038         var task = { // must defer to wait for browser to be ready
13039             run : function(){
13040                 //console.log("run task?" + this.doc.readyState);
13041                 this.assignDocWin();
13042                 if(this.doc.body || this.doc.readyState == 'complete'){
13043                     try {
13044                         this.doc.designMode="on";
13045                     } catch (e) {
13046                         return;
13047                     }
13048                     Roo.TaskMgr.stop(task);
13049                     this.initEditor.defer(10, this);
13050                 }
13051             },
13052             interval : 10,
13053             duration: 10000,
13054             scope: this
13055         };
13056         Roo.TaskMgr.start(task);
13057
13058         
13059          
13060     },
13061
13062     // private
13063     onResize : function(w, h)
13064     {
13065          Roo.log('resize: ' +w + ',' + h );
13066         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13067         if(!this.iframe){
13068             return;
13069         }
13070         if(typeof w == 'number'){
13071             
13072             this.iframe.style.width = w + 'px';
13073         }
13074         if(typeof h == 'number'){
13075             
13076             this.iframe.style.height = h + 'px';
13077             if(this.doc){
13078                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13079             }
13080         }
13081         
13082     },
13083
13084     /**
13085      * Toggles the editor between standard and source edit mode.
13086      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13087      */
13088     toggleSourceEdit : function(sourceEditMode){
13089         
13090         this.sourceEditMode = sourceEditMode === true;
13091         
13092         if(this.sourceEditMode){
13093  
13094             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13095             
13096         }else{
13097             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13098             //this.iframe.className = '';
13099             this.deferFocus();
13100         }
13101         //this.setSize(this.owner.wrap.getSize());
13102         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13103     },
13104
13105     
13106   
13107
13108     /**
13109      * Protected method that will not generally be called directly. If you need/want
13110      * custom HTML cleanup, this is the method you should override.
13111      * @param {String} html The HTML to be cleaned
13112      * return {String} The cleaned HTML
13113      */
13114     cleanHtml : function(html){
13115         html = String(html);
13116         if(html.length > 5){
13117             if(Roo.isSafari){ // strip safari nonsense
13118                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13119             }
13120         }
13121         if(html == '&nbsp;'){
13122             html = '';
13123         }
13124         return html;
13125     },
13126
13127     /**
13128      * HTML Editor -> Textarea
13129      * Protected method that will not generally be called directly. Syncs the contents
13130      * of the editor iframe with the textarea.
13131      */
13132     syncValue : function(){
13133         if(this.initialized){
13134             var bd = (this.doc.body || this.doc.documentElement);
13135             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13136             var html = bd.innerHTML;
13137             if(Roo.isSafari){
13138                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13139                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13140                 if(m && m[1]){
13141                     html = '<div style="'+m[0]+'">' + html + '</div>';
13142                 }
13143             }
13144             html = this.cleanHtml(html);
13145             // fix up the special chars.. normaly like back quotes in word...
13146             // however we do not want to do this with chinese..
13147             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13148                 var cc = b.charCodeAt();
13149                 if (
13150                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13151                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13152                     (cc >= 0xf900 && cc < 0xfb00 )
13153                 ) {
13154                         return b;
13155                 }
13156                 return "&#"+cc+";" 
13157             });
13158             if(this.owner.fireEvent('beforesync', this, html) !== false){
13159                 this.el.dom.value = html;
13160                 this.owner.fireEvent('sync', this, html);
13161             }
13162         }
13163     },
13164
13165     /**
13166      * Protected method that will not generally be called directly. Pushes the value of the textarea
13167      * into the iframe editor.
13168      */
13169     pushValue : function(){
13170         if(this.initialized){
13171             var v = this.el.dom.value.trim();
13172             
13173 //            if(v.length < 1){
13174 //                v = '&#160;';
13175 //            }
13176             
13177             if(this.owner.fireEvent('beforepush', this, v) !== false){
13178                 var d = (this.doc.body || this.doc.documentElement);
13179                 d.innerHTML = v;
13180                 this.cleanUpPaste();
13181                 this.el.dom.value = d.innerHTML;
13182                 this.owner.fireEvent('push', this, v);
13183             }
13184         }
13185     },
13186
13187     // private
13188     deferFocus : function(){
13189         this.focus.defer(10, this);
13190     },
13191
13192     // doc'ed in Field
13193     focus : function(){
13194         if(this.win && !this.sourceEditMode){
13195             this.win.focus();
13196         }else{
13197             this.el.focus();
13198         }
13199     },
13200     
13201     assignDocWin: function()
13202     {
13203         var iframe = this.iframe;
13204         
13205          if(Roo.isIE){
13206             this.doc = iframe.contentWindow.document;
13207             this.win = iframe.contentWindow;
13208         } else {
13209             if (!Roo.get(this.frameId)) {
13210                 return;
13211             }
13212             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13213             this.win = Roo.get(this.frameId).dom.contentWindow;
13214         }
13215     },
13216     
13217     // private
13218     initEditor : function(){
13219         //console.log("INIT EDITOR");
13220         this.assignDocWin();
13221         
13222         
13223         
13224         this.doc.designMode="on";
13225         this.doc.open();
13226         this.doc.write(this.getDocMarkup());
13227         this.doc.close();
13228         
13229         var dbody = (this.doc.body || this.doc.documentElement);
13230         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13231         // this copies styles from the containing element into thsi one..
13232         // not sure why we need all of this..
13233         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13234         ss['background-attachment'] = 'fixed'; // w3c
13235         dbody.bgProperties = 'fixed'; // ie
13236         Roo.DomHelper.applyStyles(dbody, ss);
13237         Roo.EventManager.on(this.doc, {
13238             //'mousedown': this.onEditorEvent,
13239             'mouseup': this.onEditorEvent,
13240             'dblclick': this.onEditorEvent,
13241             'click': this.onEditorEvent,
13242             'keyup': this.onEditorEvent,
13243             buffer:100,
13244             scope: this
13245         });
13246         if(Roo.isGecko){
13247             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13248         }
13249         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13250             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13251         }
13252         this.initialized = true;
13253
13254         this.owner.fireEvent('initialize', this);
13255         this.pushValue();
13256     },
13257
13258     // private
13259     onDestroy : function(){
13260         
13261         
13262         
13263         if(this.rendered){
13264             
13265             //for (var i =0; i < this.toolbars.length;i++) {
13266             //    // fixme - ask toolbars for heights?
13267             //    this.toolbars[i].onDestroy();
13268            // }
13269             
13270             //this.wrap.dom.innerHTML = '';
13271             //this.wrap.remove();
13272         }
13273     },
13274
13275     // private
13276     onFirstFocus : function(){
13277         
13278         this.assignDocWin();
13279         
13280         
13281         this.activated = true;
13282          
13283     
13284         if(Roo.isGecko){ // prevent silly gecko errors
13285             this.win.focus();
13286             var s = this.win.getSelection();
13287             if(!s.focusNode || s.focusNode.nodeType != 3){
13288                 var r = s.getRangeAt(0);
13289                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13290                 r.collapse(true);
13291                 this.deferFocus();
13292             }
13293             try{
13294                 this.execCmd('useCSS', true);
13295                 this.execCmd('styleWithCSS', false);
13296             }catch(e){}
13297         }
13298         this.owner.fireEvent('activate', this);
13299     },
13300
13301     // private
13302     adjustFont: function(btn){
13303         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13304         //if(Roo.isSafari){ // safari
13305         //    adjust *= 2;
13306        // }
13307         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13308         if(Roo.isSafari){ // safari
13309             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13310             v =  (v < 10) ? 10 : v;
13311             v =  (v > 48) ? 48 : v;
13312             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13313             
13314         }
13315         
13316         
13317         v = Math.max(1, v+adjust);
13318         
13319         this.execCmd('FontSize', v  );
13320     },
13321
13322     onEditorEvent : function(e){
13323         this.owner.fireEvent('editorevent', this, e);
13324       //  this.updateToolbar();
13325         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13326     },
13327
13328     insertTag : function(tg)
13329     {
13330         // could be a bit smarter... -> wrap the current selected tRoo..
13331         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13332             
13333             range = this.createRange(this.getSelection());
13334             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13335             wrappingNode.appendChild(range.extractContents());
13336             range.insertNode(wrappingNode);
13337
13338             return;
13339             
13340             
13341             
13342         }
13343         this.execCmd("formatblock",   tg);
13344         
13345     },
13346     
13347     insertText : function(txt)
13348     {
13349         
13350         
13351         var range = this.createRange();
13352         range.deleteContents();
13353                //alert(Sender.getAttribute('label'));
13354                
13355         range.insertNode(this.doc.createTextNode(txt));
13356     } ,
13357     
13358      
13359
13360     /**
13361      * Executes a Midas editor command on the editor document and performs necessary focus and
13362      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13363      * @param {String} cmd The Midas command
13364      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13365      */
13366     relayCmd : function(cmd, value){
13367         this.win.focus();
13368         this.execCmd(cmd, value);
13369         this.owner.fireEvent('editorevent', this);
13370         //this.updateToolbar();
13371         this.owner.deferFocus();
13372     },
13373
13374     /**
13375      * Executes a Midas editor command directly on the editor document.
13376      * For visual commands, you should use {@link #relayCmd} instead.
13377      * <b>This should only be called after the editor is initialized.</b>
13378      * @param {String} cmd The Midas command
13379      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13380      */
13381     execCmd : function(cmd, value){
13382         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13383         this.syncValue();
13384     },
13385  
13386  
13387    
13388     /**
13389      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13390      * to insert tRoo.
13391      * @param {String} text | dom node.. 
13392      */
13393     insertAtCursor : function(text)
13394     {
13395         
13396         
13397         
13398         if(!this.activated){
13399             return;
13400         }
13401         /*
13402         if(Roo.isIE){
13403             this.win.focus();
13404             var r = this.doc.selection.createRange();
13405             if(r){
13406                 r.collapse(true);
13407                 r.pasteHTML(text);
13408                 this.syncValue();
13409                 this.deferFocus();
13410             
13411             }
13412             return;
13413         }
13414         */
13415         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13416             this.win.focus();
13417             
13418             
13419             // from jquery ui (MIT licenced)
13420             var range, node;
13421             var win = this.win;
13422             
13423             if (win.getSelection && win.getSelection().getRangeAt) {
13424                 range = win.getSelection().getRangeAt(0);
13425                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13426                 range.insertNode(node);
13427             } else if (win.document.selection && win.document.selection.createRange) {
13428                 // no firefox support
13429                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13430                 win.document.selection.createRange().pasteHTML(txt);
13431             } else {
13432                 // no firefox support
13433                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13434                 this.execCmd('InsertHTML', txt);
13435             } 
13436             
13437             this.syncValue();
13438             
13439             this.deferFocus();
13440         }
13441     },
13442  // private
13443     mozKeyPress : function(e){
13444         if(e.ctrlKey){
13445             var c = e.getCharCode(), cmd;
13446           
13447             if(c > 0){
13448                 c = String.fromCharCode(c).toLowerCase();
13449                 switch(c){
13450                     case 'b':
13451                         cmd = 'bold';
13452                         break;
13453                     case 'i':
13454                         cmd = 'italic';
13455                         break;
13456                     
13457                     case 'u':
13458                         cmd = 'underline';
13459                         break;
13460                     
13461                     case 'v':
13462                         this.cleanUpPaste.defer(100, this);
13463                         return;
13464                         
13465                 }
13466                 if(cmd){
13467                     this.win.focus();
13468                     this.execCmd(cmd);
13469                     this.deferFocus();
13470                     e.preventDefault();
13471                 }
13472                 
13473             }
13474         }
13475     },
13476
13477     // private
13478     fixKeys : function(){ // load time branching for fastest keydown performance
13479         if(Roo.isIE){
13480             return function(e){
13481                 var k = e.getKey(), r;
13482                 if(k == e.TAB){
13483                     e.stopEvent();
13484                     r = this.doc.selection.createRange();
13485                     if(r){
13486                         r.collapse(true);
13487                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13488                         this.deferFocus();
13489                     }
13490                     return;
13491                 }
13492                 
13493                 if(k == e.ENTER){
13494                     r = this.doc.selection.createRange();
13495                     if(r){
13496                         var target = r.parentElement();
13497                         if(!target || target.tagName.toLowerCase() != 'li'){
13498                             e.stopEvent();
13499                             r.pasteHTML('<br />');
13500                             r.collapse(false);
13501                             r.select();
13502                         }
13503                     }
13504                 }
13505                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13506                     this.cleanUpPaste.defer(100, this);
13507                     return;
13508                 }
13509                 
13510                 
13511             };
13512         }else if(Roo.isOpera){
13513             return function(e){
13514                 var k = e.getKey();
13515                 if(k == e.TAB){
13516                     e.stopEvent();
13517                     this.win.focus();
13518                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13519                     this.deferFocus();
13520                 }
13521                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13522                     this.cleanUpPaste.defer(100, this);
13523                     return;
13524                 }
13525                 
13526             };
13527         }else if(Roo.isSafari){
13528             return function(e){
13529                 var k = e.getKey();
13530                 
13531                 if(k == e.TAB){
13532                     e.stopEvent();
13533                     this.execCmd('InsertText','\t');
13534                     this.deferFocus();
13535                     return;
13536                 }
13537                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13538                     this.cleanUpPaste.defer(100, this);
13539                     return;
13540                 }
13541                 
13542              };
13543         }
13544     }(),
13545     
13546     getAllAncestors: function()
13547     {
13548         var p = this.getSelectedNode();
13549         var a = [];
13550         if (!p) {
13551             a.push(p); // push blank onto stack..
13552             p = this.getParentElement();
13553         }
13554         
13555         
13556         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13557             a.push(p);
13558             p = p.parentNode;
13559         }
13560         a.push(this.doc.body);
13561         return a;
13562     },
13563     lastSel : false,
13564     lastSelNode : false,
13565     
13566     
13567     getSelection : function() 
13568     {
13569         this.assignDocWin();
13570         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13571     },
13572     
13573     getSelectedNode: function() 
13574     {
13575         // this may only work on Gecko!!!
13576         
13577         // should we cache this!!!!
13578         
13579         
13580         
13581          
13582         var range = this.createRange(this.getSelection()).cloneRange();
13583         
13584         if (Roo.isIE) {
13585             var parent = range.parentElement();
13586             while (true) {
13587                 var testRange = range.duplicate();
13588                 testRange.moveToElementText(parent);
13589                 if (testRange.inRange(range)) {
13590                     break;
13591                 }
13592                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13593                     break;
13594                 }
13595                 parent = parent.parentElement;
13596             }
13597             return parent;
13598         }
13599         
13600         // is ancestor a text element.
13601         var ac =  range.commonAncestorContainer;
13602         if (ac.nodeType == 3) {
13603             ac = ac.parentNode;
13604         }
13605         
13606         var ar = ac.childNodes;
13607          
13608         var nodes = [];
13609         var other_nodes = [];
13610         var has_other_nodes = false;
13611         for (var i=0;i<ar.length;i++) {
13612             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13613                 continue;
13614             }
13615             // fullly contained node.
13616             
13617             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13618                 nodes.push(ar[i]);
13619                 continue;
13620             }
13621             
13622             // probably selected..
13623             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13624                 other_nodes.push(ar[i]);
13625                 continue;
13626             }
13627             // outer..
13628             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13629                 continue;
13630             }
13631             
13632             
13633             has_other_nodes = true;
13634         }
13635         if (!nodes.length && other_nodes.length) {
13636             nodes= other_nodes;
13637         }
13638         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13639             return false;
13640         }
13641         
13642         return nodes[0];
13643     },
13644     createRange: function(sel)
13645     {
13646         // this has strange effects when using with 
13647         // top toolbar - not sure if it's a great idea.
13648         //this.editor.contentWindow.focus();
13649         if (typeof sel != "undefined") {
13650             try {
13651                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13652             } catch(e) {
13653                 return this.doc.createRange();
13654             }
13655         } else {
13656             return this.doc.createRange();
13657         }
13658     },
13659     getParentElement: function()
13660     {
13661         
13662         this.assignDocWin();
13663         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13664         
13665         var range = this.createRange(sel);
13666          
13667         try {
13668             var p = range.commonAncestorContainer;
13669             while (p.nodeType == 3) { // text node
13670                 p = p.parentNode;
13671             }
13672             return p;
13673         } catch (e) {
13674             return null;
13675         }
13676     
13677     },
13678     /***
13679      *
13680      * Range intersection.. the hard stuff...
13681      *  '-1' = before
13682      *  '0' = hits..
13683      *  '1' = after.
13684      *         [ -- selected range --- ]
13685      *   [fail]                        [fail]
13686      *
13687      *    basically..
13688      *      if end is before start or  hits it. fail.
13689      *      if start is after end or hits it fail.
13690      *
13691      *   if either hits (but other is outside. - then it's not 
13692      *   
13693      *    
13694      **/
13695     
13696     
13697     // @see http://www.thismuchiknow.co.uk/?p=64.
13698     rangeIntersectsNode : function(range, node)
13699     {
13700         var nodeRange = node.ownerDocument.createRange();
13701         try {
13702             nodeRange.selectNode(node);
13703         } catch (e) {
13704             nodeRange.selectNodeContents(node);
13705         }
13706     
13707         var rangeStartRange = range.cloneRange();
13708         rangeStartRange.collapse(true);
13709     
13710         var rangeEndRange = range.cloneRange();
13711         rangeEndRange.collapse(false);
13712     
13713         var nodeStartRange = nodeRange.cloneRange();
13714         nodeStartRange.collapse(true);
13715     
13716         var nodeEndRange = nodeRange.cloneRange();
13717         nodeEndRange.collapse(false);
13718     
13719         return rangeStartRange.compareBoundaryPoints(
13720                  Range.START_TO_START, nodeEndRange) == -1 &&
13721                rangeEndRange.compareBoundaryPoints(
13722                  Range.START_TO_START, nodeStartRange) == 1;
13723         
13724          
13725     },
13726     rangeCompareNode : function(range, node)
13727     {
13728         var nodeRange = node.ownerDocument.createRange();
13729         try {
13730             nodeRange.selectNode(node);
13731         } catch (e) {
13732             nodeRange.selectNodeContents(node);
13733         }
13734         
13735         
13736         range.collapse(true);
13737     
13738         nodeRange.collapse(true);
13739      
13740         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13741         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13742          
13743         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13744         
13745         var nodeIsBefore   =  ss == 1;
13746         var nodeIsAfter    = ee == -1;
13747         
13748         if (nodeIsBefore && nodeIsAfter)
13749             return 0; // outer
13750         if (!nodeIsBefore && nodeIsAfter)
13751             return 1; //right trailed.
13752         
13753         if (nodeIsBefore && !nodeIsAfter)
13754             return 2;  // left trailed.
13755         // fully contined.
13756         return 3;
13757     },
13758
13759     // private? - in a new class?
13760     cleanUpPaste :  function()
13761     {
13762         // cleans up the whole document..
13763         Roo.log('cleanuppaste');
13764         
13765         this.cleanUpChildren(this.doc.body);
13766         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13767         if (clean != this.doc.body.innerHTML) {
13768             this.doc.body.innerHTML = clean;
13769         }
13770         
13771     },
13772     
13773     cleanWordChars : function(input) {// change the chars to hex code
13774         var he = Roo.HtmlEditorCore;
13775         
13776         var output = input;
13777         Roo.each(he.swapCodes, function(sw) { 
13778             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13779             
13780             output = output.replace(swapper, sw[1]);
13781         });
13782         
13783         return output;
13784     },
13785     
13786     
13787     cleanUpChildren : function (n)
13788     {
13789         if (!n.childNodes.length) {
13790             return;
13791         }
13792         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13793            this.cleanUpChild(n.childNodes[i]);
13794         }
13795     },
13796     
13797     
13798         
13799     
13800     cleanUpChild : function (node)
13801     {
13802         var ed = this;
13803         //console.log(node);
13804         if (node.nodeName == "#text") {
13805             // clean up silly Windows -- stuff?
13806             return; 
13807         }
13808         if (node.nodeName == "#comment") {
13809             node.parentNode.removeChild(node);
13810             // clean up silly Windows -- stuff?
13811             return; 
13812         }
13813         
13814         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13815             // remove node.
13816             node.parentNode.removeChild(node);
13817             return;
13818             
13819         }
13820         
13821         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13822         
13823         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13824         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13825         
13826         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13827         //    remove_keep_children = true;
13828         //}
13829         
13830         if (remove_keep_children) {
13831             this.cleanUpChildren(node);
13832             // inserts everything just before this node...
13833             while (node.childNodes.length) {
13834                 var cn = node.childNodes[0];
13835                 node.removeChild(cn);
13836                 node.parentNode.insertBefore(cn, node);
13837             }
13838             node.parentNode.removeChild(node);
13839             return;
13840         }
13841         
13842         if (!node.attributes || !node.attributes.length) {
13843             this.cleanUpChildren(node);
13844             return;
13845         }
13846         
13847         function cleanAttr(n,v)
13848         {
13849             
13850             if (v.match(/^\./) || v.match(/^\//)) {
13851                 return;
13852             }
13853             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13854                 return;
13855             }
13856             if (v.match(/^#/)) {
13857                 return;
13858             }
13859 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13860             node.removeAttribute(n);
13861             
13862         }
13863         
13864         function cleanStyle(n,v)
13865         {
13866             if (v.match(/expression/)) { //XSS?? should we even bother..
13867                 node.removeAttribute(n);
13868                 return;
13869             }
13870             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13871             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13872             
13873             
13874             var parts = v.split(/;/);
13875             var clean = [];
13876             
13877             Roo.each(parts, function(p) {
13878                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13879                 if (!p.length) {
13880                     return true;
13881                 }
13882                 var l = p.split(':').shift().replace(/\s+/g,'');
13883                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13884                 
13885                 if ( cblack.indexOf(l) > -1) {
13886 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13887                     //node.removeAttribute(n);
13888                     return true;
13889                 }
13890                 //Roo.log()
13891                 // only allow 'c whitelisted system attributes'
13892                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13893 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13894                     //node.removeAttribute(n);
13895                     return true;
13896                 }
13897                 
13898                 
13899                  
13900                 
13901                 clean.push(p);
13902                 return true;
13903             });
13904             if (clean.length) { 
13905                 node.setAttribute(n, clean.join(';'));
13906             } else {
13907                 node.removeAttribute(n);
13908             }
13909             
13910         }
13911         
13912         
13913         for (var i = node.attributes.length-1; i > -1 ; i--) {
13914             var a = node.attributes[i];
13915             //console.log(a);
13916             
13917             if (a.name.toLowerCase().substr(0,2)=='on')  {
13918                 node.removeAttribute(a.name);
13919                 continue;
13920             }
13921             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13922                 node.removeAttribute(a.name);
13923                 continue;
13924             }
13925             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13926                 cleanAttr(a.name,a.value); // fixme..
13927                 continue;
13928             }
13929             if (a.name == 'style') {
13930                 cleanStyle(a.name,a.value);
13931                 continue;
13932             }
13933             /// clean up MS crap..
13934             // tecnically this should be a list of valid class'es..
13935             
13936             
13937             if (a.name == 'class') {
13938                 if (a.value.match(/^Mso/)) {
13939                     node.className = '';
13940                 }
13941                 
13942                 if (a.value.match(/body/)) {
13943                     node.className = '';
13944                 }
13945                 continue;
13946             }
13947             
13948             // style cleanup!?
13949             // class cleanup?
13950             
13951         }
13952         
13953         
13954         this.cleanUpChildren(node);
13955         
13956         
13957     }
13958     
13959     
13960     // hide stuff that is not compatible
13961     /**
13962      * @event blur
13963      * @hide
13964      */
13965     /**
13966      * @event change
13967      * @hide
13968      */
13969     /**
13970      * @event focus
13971      * @hide
13972      */
13973     /**
13974      * @event specialkey
13975      * @hide
13976      */
13977     /**
13978      * @cfg {String} fieldClass @hide
13979      */
13980     /**
13981      * @cfg {String} focusClass @hide
13982      */
13983     /**
13984      * @cfg {String} autoCreate @hide
13985      */
13986     /**
13987      * @cfg {String} inputType @hide
13988      */
13989     /**
13990      * @cfg {String} invalidClass @hide
13991      */
13992     /**
13993      * @cfg {String} invalidText @hide
13994      */
13995     /**
13996      * @cfg {String} msgFx @hide
13997      */
13998     /**
13999      * @cfg {String} validateOnBlur @hide
14000      */
14001 });
14002
14003 Roo.HtmlEditorCore.white = [
14004         'area', 'br', 'img', 'input', 'hr', 'wbr',
14005         
14006        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14007        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14008        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14009        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14010        'table',   'ul',         'xmp', 
14011        
14012        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14013       'thead',   'tr', 
14014      
14015       'dir', 'menu', 'ol', 'ul', 'dl',
14016        
14017       'embed',  'object'
14018 ];
14019
14020
14021 Roo.HtmlEditorCore.black = [
14022     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14023         'applet', // 
14024         'base',   'basefont', 'bgsound', 'blink',  'body', 
14025         'frame',  'frameset', 'head',    'html',   'ilayer', 
14026         'iframe', 'layer',  'link',     'meta',    'object',   
14027         'script', 'style' ,'title',  'xml' // clean later..
14028 ];
14029 Roo.HtmlEditorCore.clean = [
14030     'script', 'style', 'title', 'xml'
14031 ];
14032 Roo.HtmlEditorCore.remove = [
14033     'font'
14034 ];
14035 // attributes..
14036
14037 Roo.HtmlEditorCore.ablack = [
14038     'on'
14039 ];
14040     
14041 Roo.HtmlEditorCore.aclean = [ 
14042     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14043 ];
14044
14045 // protocols..
14046 Roo.HtmlEditorCore.pwhite= [
14047         'http',  'https',  'mailto'
14048 ];
14049
14050 // white listed style attributes.
14051 Roo.HtmlEditorCore.cwhite= [
14052       //  'text-align', /// default is to allow most things..
14053       
14054          
14055 //        'font-size'//??
14056 ];
14057
14058 // black listed style attributes.
14059 Roo.HtmlEditorCore.cblack= [
14060       //  'font-size' -- this can be set by the project 
14061 ];
14062
14063
14064 Roo.HtmlEditorCore.swapCodes   =[ 
14065     [    8211, "--" ], 
14066     [    8212, "--" ], 
14067     [    8216,  "'" ],  
14068     [    8217, "'" ],  
14069     [    8220, '"' ],  
14070     [    8221, '"' ],  
14071     [    8226, "*" ],  
14072     [    8230, "..." ]
14073 ]; 
14074
14075     /*
14076  * - LGPL
14077  *
14078  * HtmlEditor
14079  * 
14080  */
14081
14082 /**
14083  * @class Roo.bootstrap.HtmlEditor
14084  * @extends Roo.bootstrap.TextArea
14085  * Bootstrap HtmlEditor class
14086
14087  * @constructor
14088  * Create a new HtmlEditor
14089  * @param {Object} config The config object
14090  */
14091
14092 Roo.bootstrap.HtmlEditor = function(config){
14093     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14094     if (!this.toolbars) {
14095         this.toolbars = [];
14096     }
14097     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14098     this.addEvents({
14099             /**
14100              * @event initialize
14101              * Fires when the editor is fully initialized (including the iframe)
14102              * @param {HtmlEditor} this
14103              */
14104             initialize: true,
14105             /**
14106              * @event activate
14107              * Fires when the editor is first receives the focus. Any insertion must wait
14108              * until after this event.
14109              * @param {HtmlEditor} this
14110              */
14111             activate: true,
14112              /**
14113              * @event beforesync
14114              * Fires before the textarea is updated with content from the editor iframe. Return false
14115              * to cancel the sync.
14116              * @param {HtmlEditor} this
14117              * @param {String} html
14118              */
14119             beforesync: true,
14120              /**
14121              * @event beforepush
14122              * Fires before the iframe editor is updated with content from the textarea. Return false
14123              * to cancel the push.
14124              * @param {HtmlEditor} this
14125              * @param {String} html
14126              */
14127             beforepush: true,
14128              /**
14129              * @event sync
14130              * Fires when the textarea is updated with content from the editor iframe.
14131              * @param {HtmlEditor} this
14132              * @param {String} html
14133              */
14134             sync: true,
14135              /**
14136              * @event push
14137              * Fires when the iframe editor is updated with content from the textarea.
14138              * @param {HtmlEditor} this
14139              * @param {String} html
14140              */
14141             push: true,
14142              /**
14143              * @event editmodechange
14144              * Fires when the editor switches edit modes
14145              * @param {HtmlEditor} this
14146              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14147              */
14148             editmodechange: true,
14149             /**
14150              * @event editorevent
14151              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14152              * @param {HtmlEditor} this
14153              */
14154             editorevent: true,
14155             /**
14156              * @event firstfocus
14157              * Fires when on first focus - needed by toolbars..
14158              * @param {HtmlEditor} this
14159              */
14160             firstfocus: true,
14161             /**
14162              * @event autosave
14163              * Auto save the htmlEditor value as a file into Events
14164              * @param {HtmlEditor} this
14165              */
14166             autosave: true,
14167             /**
14168              * @event savedpreview
14169              * preview the saved version of htmlEditor
14170              * @param {HtmlEditor} this
14171              */
14172             savedpreview: true
14173         });
14174 };
14175
14176
14177 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14178     
14179     
14180       /**
14181      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14182      */
14183     toolbars : false,
14184    
14185      /**
14186      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14187      *                        Roo.resizable.
14188      */
14189     resizable : false,
14190      /**
14191      * @cfg {Number} height (in pixels)
14192      */   
14193     height: 300,
14194    /**
14195      * @cfg {Number} width (in pixels)
14196      */   
14197     width: false,
14198     
14199     /**
14200      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14201      * 
14202      */
14203     stylesheets: false,
14204     
14205     // id of frame..
14206     frameId: false,
14207     
14208     // private properties
14209     validationEvent : false,
14210     deferHeight: true,
14211     initialized : false,
14212     activated : false,
14213     
14214     onFocus : Roo.emptyFn,
14215     iframePad:3,
14216     hideMode:'offsets',
14217     
14218     
14219     tbContainer : false,
14220     
14221     toolbarContainer :function() {
14222         return this.wrap.select('.x-html-editor-tb',true).first();
14223     },
14224
14225     /**
14226      * Protected method that will not generally be called directly. It
14227      * is called when the editor creates its toolbar. Override this method if you need to
14228      * add custom toolbar buttons.
14229      * @param {HtmlEditor} editor
14230      */
14231     createToolbar : function(){
14232         
14233         Roo.log("create toolbars");
14234         
14235         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14236         this.toolbars[0].render(this.toolbarContainer());
14237         
14238         return;
14239         
14240 //        if (!editor.toolbars || !editor.toolbars.length) {
14241 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14242 //        }
14243 //        
14244 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14245 //            editor.toolbars[i] = Roo.factory(
14246 //                    typeof(editor.toolbars[i]) == 'string' ?
14247 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14248 //                Roo.bootstrap.HtmlEditor);
14249 //            editor.toolbars[i].init(editor);
14250 //        }
14251     },
14252
14253      
14254     // private
14255     onRender : function(ct, position)
14256     {
14257        // Roo.log("Call onRender: " + this.xtype);
14258         var _t = this;
14259         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14260       
14261         this.wrap = this.inputEl().wrap({
14262             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14263         });
14264         
14265         this.editorcore.onRender(ct, position);
14266          
14267         if (this.resizable) {
14268             this.resizeEl = new Roo.Resizable(this.wrap, {
14269                 pinned : true,
14270                 wrap: true,
14271                 dynamic : true,
14272                 minHeight : this.height,
14273                 height: this.height,
14274                 handles : this.resizable,
14275                 width: this.width,
14276                 listeners : {
14277                     resize : function(r, w, h) {
14278                         _t.onResize(w,h); // -something
14279                     }
14280                 }
14281             });
14282             
14283         }
14284         this.createToolbar(this);
14285        
14286         
14287         if(!this.width && this.resizable){
14288             this.setSize(this.wrap.getSize());
14289         }
14290         if (this.resizeEl) {
14291             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14292             // should trigger onReize..
14293         }
14294         
14295     },
14296
14297     // private
14298     onResize : function(w, h)
14299     {
14300         Roo.log('resize: ' +w + ',' + h );
14301         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14302         var ew = false;
14303         var eh = false;
14304         
14305         if(this.inputEl() ){
14306             if(typeof w == 'number'){
14307                 var aw = w - this.wrap.getFrameWidth('lr');
14308                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14309                 ew = aw;
14310             }
14311             if(typeof h == 'number'){
14312                  var tbh = -11;  // fixme it needs to tool bar size!
14313                 for (var i =0; i < this.toolbars.length;i++) {
14314                     // fixme - ask toolbars for heights?
14315                     tbh += this.toolbars[i].el.getHeight();
14316                     //if (this.toolbars[i].footer) {
14317                     //    tbh += this.toolbars[i].footer.el.getHeight();
14318                     //}
14319                 }
14320               
14321                 
14322                 
14323                 
14324                 
14325                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14326                 ah -= 5; // knock a few pixes off for look..
14327                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14328                 var eh = ah;
14329             }
14330         }
14331         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14332         this.editorcore.onResize(ew,eh);
14333         
14334     },
14335
14336     /**
14337      * Toggles the editor between standard and source edit mode.
14338      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14339      */
14340     toggleSourceEdit : function(sourceEditMode)
14341     {
14342         this.editorcore.toggleSourceEdit(sourceEditMode);
14343         
14344         if(this.editorcore.sourceEditMode){
14345             Roo.log('editor - showing textarea');
14346             
14347 //            Roo.log('in');
14348 //            Roo.log(this.syncValue());
14349             this.syncValue();
14350             this.inputEl().removeClass('hide');
14351             this.inputEl().dom.removeAttribute('tabIndex');
14352             this.inputEl().focus();
14353         }else{
14354             Roo.log('editor - hiding textarea');
14355 //            Roo.log('out')
14356 //            Roo.log(this.pushValue()); 
14357             this.pushValue();
14358             
14359             this.inputEl().addClass('hide');
14360             this.inputEl().dom.setAttribute('tabIndex', -1);
14361             //this.deferFocus();
14362         }
14363          
14364         if(this.resizable){
14365             this.setSize(this.wrap.getSize());
14366         }
14367         
14368         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14369     },
14370  
14371     // private (for BoxComponent)
14372     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14373
14374     // private (for BoxComponent)
14375     getResizeEl : function(){
14376         return this.wrap;
14377     },
14378
14379     // private (for BoxComponent)
14380     getPositionEl : function(){
14381         return this.wrap;
14382     },
14383
14384     // private
14385     initEvents : function(){
14386         this.originalValue = this.getValue();
14387     },
14388
14389 //    /**
14390 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14391 //     * @method
14392 //     */
14393 //    markInvalid : Roo.emptyFn,
14394 //    /**
14395 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14396 //     * @method
14397 //     */
14398 //    clearInvalid : Roo.emptyFn,
14399
14400     setValue : function(v){
14401         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14402         this.editorcore.pushValue();
14403     },
14404
14405      
14406     // private
14407     deferFocus : function(){
14408         this.focus.defer(10, this);
14409     },
14410
14411     // doc'ed in Field
14412     focus : function(){
14413         this.editorcore.focus();
14414         
14415     },
14416       
14417
14418     // private
14419     onDestroy : function(){
14420         
14421         
14422         
14423         if(this.rendered){
14424             
14425             for (var i =0; i < this.toolbars.length;i++) {
14426                 // fixme - ask toolbars for heights?
14427                 this.toolbars[i].onDestroy();
14428             }
14429             
14430             this.wrap.dom.innerHTML = '';
14431             this.wrap.remove();
14432         }
14433     },
14434
14435     // private
14436     onFirstFocus : function(){
14437         //Roo.log("onFirstFocus");
14438         this.editorcore.onFirstFocus();
14439          for (var i =0; i < this.toolbars.length;i++) {
14440             this.toolbars[i].onFirstFocus();
14441         }
14442         
14443     },
14444     
14445     // private
14446     syncValue : function()
14447     {   
14448         this.editorcore.syncValue();
14449     },
14450     
14451     pushValue : function()
14452     {   
14453         this.editorcore.pushValue();
14454     }
14455      
14456     
14457     // hide stuff that is not compatible
14458     /**
14459      * @event blur
14460      * @hide
14461      */
14462     /**
14463      * @event change
14464      * @hide
14465      */
14466     /**
14467      * @event focus
14468      * @hide
14469      */
14470     /**
14471      * @event specialkey
14472      * @hide
14473      */
14474     /**
14475      * @cfg {String} fieldClass @hide
14476      */
14477     /**
14478      * @cfg {String} focusClass @hide
14479      */
14480     /**
14481      * @cfg {String} autoCreate @hide
14482      */
14483     /**
14484      * @cfg {String} inputType @hide
14485      */
14486     /**
14487      * @cfg {String} invalidClass @hide
14488      */
14489     /**
14490      * @cfg {String} invalidText @hide
14491      */
14492     /**
14493      * @cfg {String} msgFx @hide
14494      */
14495     /**
14496      * @cfg {String} validateOnBlur @hide
14497      */
14498 });
14499  
14500     
14501    
14502    
14503    
14504       
14505
14506 /**
14507  * @class Roo.bootstrap.HtmlEditorToolbar1
14508  * Basic Toolbar
14509  * 
14510  * Usage:
14511  *
14512  new Roo.bootstrap.HtmlEditor({
14513     ....
14514     toolbars : [
14515         new Roo.bootstrap.HtmlEditorToolbar1({
14516             disable : { fonts: 1 , format: 1, ..., ... , ...],
14517             btns : [ .... ]
14518         })
14519     }
14520      
14521  * 
14522  * @cfg {Object} disable List of elements to disable..
14523  * @cfg {Array} btns List of additional buttons.
14524  * 
14525  * 
14526  * NEEDS Extra CSS? 
14527  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14528  */
14529  
14530 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14531 {
14532     
14533     Roo.apply(this, config);
14534     
14535     // default disabled, based on 'good practice'..
14536     this.disable = this.disable || {};
14537     Roo.applyIf(this.disable, {
14538         fontSize : true,
14539         colors : true,
14540         specialElements : true
14541     });
14542     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14543     
14544     this.editor = config.editor;
14545     this.editorcore = config.editor.editorcore;
14546     
14547     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14548     
14549     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14550     // dont call parent... till later.
14551 }
14552 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14553     
14554     
14555     bar : true,
14556     
14557     editor : false,
14558     editorcore : false,
14559     
14560     
14561     formats : [
14562         "p" ,  
14563         "h1","h2","h3","h4","h5","h6", 
14564         "pre", "code", 
14565         "abbr", "acronym", "address", "cite", "samp", "var",
14566         'div','span'
14567     ],
14568     
14569     onRender : function(ct, position)
14570     {
14571        // Roo.log("Call onRender: " + this.xtype);
14572         
14573        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14574        Roo.log(this.el);
14575        this.el.dom.style.marginBottom = '0';
14576        var _this = this;
14577        var editorcore = this.editorcore;
14578        var editor= this.editor;
14579        
14580        var children = [];
14581        var btn = function(id,cmd , toggle, handler){
14582        
14583             var  event = toggle ? 'toggle' : 'click';
14584        
14585             var a = {
14586                 size : 'sm',
14587                 xtype: 'Button',
14588                 xns: Roo.bootstrap,
14589                 glyphicon : id,
14590                 cmd : id || cmd,
14591                 enableToggle:toggle !== false,
14592                 //html : 'submit'
14593                 pressed : toggle ? false : null,
14594                 listeners : {}
14595             }
14596             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14597                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14598             }
14599             children.push(a);
14600             return a;
14601        }
14602         
14603         var style = {
14604                 xtype: 'Button',
14605                 size : 'sm',
14606                 xns: Roo.bootstrap,
14607                 glyphicon : 'font',
14608                 //html : 'submit'
14609                 menu : {
14610                     xtype: 'Menu',
14611                     xns: Roo.bootstrap,
14612                     items:  []
14613                 }
14614         };
14615         Roo.each(this.formats, function(f) {
14616             style.menu.items.push({
14617                 xtype :'MenuItem',
14618                 xns: Roo.bootstrap,
14619                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14620                 tagname : f,
14621                 listeners : {
14622                     click : function()
14623                     {
14624                         editorcore.insertTag(this.tagname);
14625                         editor.focus();
14626                     }
14627                 }
14628                 
14629             });
14630         });
14631          children.push(style);   
14632             
14633             
14634         btn('bold',false,true);
14635         btn('italic',false,true);
14636         btn('align-left', 'justifyleft',true);
14637         btn('align-center', 'justifycenter',true);
14638         btn('align-right' , 'justifyright',true);
14639         btn('link', false, false, function(btn) {
14640             //Roo.log("create link?");
14641             var url = prompt(this.createLinkText, this.defaultLinkValue);
14642             if(url && url != 'http:/'+'/'){
14643                 this.editorcore.relayCmd('createlink', url);
14644             }
14645         }),
14646         btn('list','insertunorderedlist',true);
14647         btn('pencil', false,true, function(btn){
14648                 Roo.log(this);
14649                 
14650                 this.toggleSourceEdit(btn.pressed);
14651         });
14652         /*
14653         var cog = {
14654                 xtype: 'Button',
14655                 size : 'sm',
14656                 xns: Roo.bootstrap,
14657                 glyphicon : 'cog',
14658                 //html : 'submit'
14659                 menu : {
14660                     xtype: 'Menu',
14661                     xns: Roo.bootstrap,
14662                     items:  []
14663                 }
14664         };
14665         
14666         cog.menu.items.push({
14667             xtype :'MenuItem',
14668             xns: Roo.bootstrap,
14669             html : Clean styles,
14670             tagname : f,
14671             listeners : {
14672                 click : function()
14673                 {
14674                     editorcore.insertTag(this.tagname);
14675                     editor.focus();
14676                 }
14677             }
14678             
14679         });
14680        */
14681         
14682          
14683        this.xtype = 'Navbar';
14684         
14685         for(var i=0;i< children.length;i++) {
14686             
14687             this.buttons.add(this.addxtypeChild(children[i]));
14688             
14689         }
14690         
14691         editor.on('editorevent', this.updateToolbar, this);
14692     },
14693     onBtnClick : function(id)
14694     {
14695        this.editorcore.relayCmd(id);
14696        this.editorcore.focus();
14697     },
14698     
14699     /**
14700      * Protected method that will not generally be called directly. It triggers
14701      * a toolbar update by reading the markup state of the current selection in the editor.
14702      */
14703     updateToolbar: function(){
14704
14705         if(!this.editorcore.activated){
14706             this.editor.onFirstFocus(); // is this neeed?
14707             return;
14708         }
14709
14710         var btns = this.buttons; 
14711         var doc = this.editorcore.doc;
14712         btns.get('bold').setActive(doc.queryCommandState('bold'));
14713         btns.get('italic').setActive(doc.queryCommandState('italic'));
14714         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14715         
14716         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14717         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14718         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14719         
14720         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14721         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14722          /*
14723         
14724         var ans = this.editorcore.getAllAncestors();
14725         if (this.formatCombo) {
14726             
14727             
14728             var store = this.formatCombo.store;
14729             this.formatCombo.setValue("");
14730             for (var i =0; i < ans.length;i++) {
14731                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14732                     // select it..
14733                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14734                     break;
14735                 }
14736             }
14737         }
14738         
14739         
14740         
14741         // hides menus... - so this cant be on a menu...
14742         Roo.bootstrap.MenuMgr.hideAll();
14743         */
14744         Roo.bootstrap.MenuMgr.hideAll();
14745         //this.editorsyncValue();
14746     },
14747     onFirstFocus: function() {
14748         this.buttons.each(function(item){
14749            item.enable();
14750         });
14751     },
14752     toggleSourceEdit : function(sourceEditMode){
14753         
14754           
14755         if(sourceEditMode){
14756             Roo.log("disabling buttons");
14757            this.buttons.each( function(item){
14758                 if(item.cmd != 'pencil'){
14759                     item.disable();
14760                 }
14761             });
14762           
14763         }else{
14764             Roo.log("enabling buttons");
14765             if(this.editorcore.initialized){
14766                 this.buttons.each( function(item){
14767                     item.enable();
14768                 });
14769             }
14770             
14771         }
14772         Roo.log("calling toggole on editor");
14773         // tell the editor that it's been pressed..
14774         this.editor.toggleSourceEdit(sourceEditMode);
14775        
14776     }
14777 });
14778
14779
14780
14781
14782
14783 /**
14784  * @class Roo.bootstrap.Table.AbstractSelectionModel
14785  * @extends Roo.util.Observable
14786  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14787  * implemented by descendant classes.  This class should not be directly instantiated.
14788  * @constructor
14789  */
14790 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14791     this.locked = false;
14792     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14793 };
14794
14795
14796 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14797     /** @ignore Called by the grid automatically. Do not call directly. */
14798     init : function(grid){
14799         this.grid = grid;
14800         this.initEvents();
14801     },
14802
14803     /**
14804      * Locks the selections.
14805      */
14806     lock : function(){
14807         this.locked = true;
14808     },
14809
14810     /**
14811      * Unlocks the selections.
14812      */
14813     unlock : function(){
14814         this.locked = false;
14815     },
14816
14817     /**
14818      * Returns true if the selections are locked.
14819      * @return {Boolean}
14820      */
14821     isLocked : function(){
14822         return this.locked;
14823     }
14824 });
14825 /**
14826  * @class Roo.bootstrap.Table.ColumnModel
14827  * @extends Roo.util.Observable
14828  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14829  * the columns in the table.
14830  
14831  * @constructor
14832  * @param {Object} config An Array of column config objects. See this class's
14833  * config objects for details.
14834 */
14835 Roo.bootstrap.Table.ColumnModel = function(config){
14836         /**
14837      * The config passed into the constructor
14838      */
14839     this.config = config;
14840     this.lookup = {};
14841
14842     // if no id, create one
14843     // if the column does not have a dataIndex mapping,
14844     // map it to the order it is in the config
14845     for(var i = 0, len = config.length; i < len; i++){
14846         var c = config[i];
14847         if(typeof c.dataIndex == "undefined"){
14848             c.dataIndex = i;
14849         }
14850         if(typeof c.renderer == "string"){
14851             c.renderer = Roo.util.Format[c.renderer];
14852         }
14853         if(typeof c.id == "undefined"){
14854             c.id = Roo.id();
14855         }
14856 //        if(c.editor && c.editor.xtype){
14857 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14858 //        }
14859 //        if(c.editor && c.editor.isFormField){
14860 //            c.editor = new Roo.grid.GridEditor(c.editor);
14861 //        }
14862
14863         this.lookup[c.id] = c;
14864     }
14865
14866     /**
14867      * The width of columns which have no width specified (defaults to 100)
14868      * @type Number
14869      */
14870     this.defaultWidth = 100;
14871
14872     /**
14873      * Default sortable of columns which have no sortable specified (defaults to false)
14874      * @type Boolean
14875      */
14876     this.defaultSortable = false;
14877
14878     this.addEvents({
14879         /**
14880              * @event widthchange
14881              * Fires when the width of a column changes.
14882              * @param {ColumnModel} this
14883              * @param {Number} columnIndex The column index
14884              * @param {Number} newWidth The new width
14885              */
14886             "widthchange": true,
14887         /**
14888              * @event headerchange
14889              * Fires when the text of a header changes.
14890              * @param {ColumnModel} this
14891              * @param {Number} columnIndex The column index
14892              * @param {Number} newText The new header text
14893              */
14894             "headerchange": true,
14895         /**
14896              * @event hiddenchange
14897              * Fires when a column is hidden or "unhidden".
14898              * @param {ColumnModel} this
14899              * @param {Number} columnIndex The column index
14900              * @param {Boolean} hidden true if hidden, false otherwise
14901              */
14902             "hiddenchange": true,
14903             /**
14904          * @event columnmoved
14905          * Fires when a column is moved.
14906          * @param {ColumnModel} this
14907          * @param {Number} oldIndex
14908          * @param {Number} newIndex
14909          */
14910         "columnmoved" : true,
14911         /**
14912          * @event columlockchange
14913          * Fires when a column's locked state is changed
14914          * @param {ColumnModel} this
14915          * @param {Number} colIndex
14916          * @param {Boolean} locked true if locked
14917          */
14918         "columnlockchange" : true
14919     });
14920     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14921 };
14922 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14923     /**
14924      * @cfg {String} header The header text to display in the Grid view.
14925      */
14926     /**
14927      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14928      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14929      * specified, the column's index is used as an index into the Record's data Array.
14930      */
14931     /**
14932      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14933      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14934      */
14935     /**
14936      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14937      * Defaults to the value of the {@link #defaultSortable} property.
14938      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14939      */
14940     /**
14941      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14942      */
14943     /**
14944      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14945      */
14946     /**
14947      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14948      */
14949     /**
14950      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14951      */
14952     /**
14953      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14954      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14955      * default renderer uses the raw data value.
14956      */
14957     /**
14958      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14959      */
14960
14961     /**
14962      * Returns the id of the column at the specified index.
14963      * @param {Number} index The column index
14964      * @return {String} the id
14965      */
14966     getColumnId : function(index){
14967         return this.config[index].id;
14968     },
14969
14970     /**
14971      * Returns the column for a specified id.
14972      * @param {String} id The column id
14973      * @return {Object} the column
14974      */
14975     getColumnById : function(id){
14976         return this.lookup[id];
14977     },
14978
14979     
14980     /**
14981      * Returns the column for a specified dataIndex.
14982      * @param {String} dataIndex The column dataIndex
14983      * @return {Object|Boolean} the column or false if not found
14984      */
14985     getColumnByDataIndex: function(dataIndex){
14986         var index = this.findColumnIndex(dataIndex);
14987         return index > -1 ? this.config[index] : false;
14988     },
14989     
14990     /**
14991      * Returns the index for a specified column id.
14992      * @param {String} id The column id
14993      * @return {Number} the index, or -1 if not found
14994      */
14995     getIndexById : function(id){
14996         for(var i = 0, len = this.config.length; i < len; i++){
14997             if(this.config[i].id == id){
14998                 return i;
14999             }
15000         }
15001         return -1;
15002     },
15003     
15004     /**
15005      * Returns the index for a specified column dataIndex.
15006      * @param {String} dataIndex The column dataIndex
15007      * @return {Number} the index, or -1 if not found
15008      */
15009     
15010     findColumnIndex : function(dataIndex){
15011         for(var i = 0, len = this.config.length; i < len; i++){
15012             if(this.config[i].dataIndex == dataIndex){
15013                 return i;
15014             }
15015         }
15016         return -1;
15017     },
15018     
15019     
15020     moveColumn : function(oldIndex, newIndex){
15021         var c = this.config[oldIndex];
15022         this.config.splice(oldIndex, 1);
15023         this.config.splice(newIndex, 0, c);
15024         this.dataMap = null;
15025         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15026     },
15027
15028     isLocked : function(colIndex){
15029         return this.config[colIndex].locked === true;
15030     },
15031
15032     setLocked : function(colIndex, value, suppressEvent){
15033         if(this.isLocked(colIndex) == value){
15034             return;
15035         }
15036         this.config[colIndex].locked = value;
15037         if(!suppressEvent){
15038             this.fireEvent("columnlockchange", this, colIndex, value);
15039         }
15040     },
15041
15042     getTotalLockedWidth : function(){
15043         var totalWidth = 0;
15044         for(var i = 0; i < this.config.length; i++){
15045             if(this.isLocked(i) && !this.isHidden(i)){
15046                 this.totalWidth += this.getColumnWidth(i);
15047             }
15048         }
15049         return totalWidth;
15050     },
15051
15052     getLockedCount : function(){
15053         for(var i = 0, len = this.config.length; i < len; i++){
15054             if(!this.isLocked(i)){
15055                 return i;
15056             }
15057         }
15058     },
15059
15060     /**
15061      * Returns the number of columns.
15062      * @return {Number}
15063      */
15064     getColumnCount : function(visibleOnly){
15065         if(visibleOnly === true){
15066             var c = 0;
15067             for(var i = 0, len = this.config.length; i < len; i++){
15068                 if(!this.isHidden(i)){
15069                     c++;
15070                 }
15071             }
15072             return c;
15073         }
15074         return this.config.length;
15075     },
15076
15077     /**
15078      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15079      * @param {Function} fn
15080      * @param {Object} scope (optional)
15081      * @return {Array} result
15082      */
15083     getColumnsBy : function(fn, scope){
15084         var r = [];
15085         for(var i = 0, len = this.config.length; i < len; i++){
15086             var c = this.config[i];
15087             if(fn.call(scope||this, c, i) === true){
15088                 r[r.length] = c;
15089             }
15090         }
15091         return r;
15092     },
15093
15094     /**
15095      * Returns true if the specified column is sortable.
15096      * @param {Number} col The column index
15097      * @return {Boolean}
15098      */
15099     isSortable : function(col){
15100         if(typeof this.config[col].sortable == "undefined"){
15101             return this.defaultSortable;
15102         }
15103         return this.config[col].sortable;
15104     },
15105
15106     /**
15107      * Returns the rendering (formatting) function defined for the column.
15108      * @param {Number} col The column index.
15109      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15110      */
15111     getRenderer : function(col){
15112         if(!this.config[col].renderer){
15113             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15114         }
15115         return this.config[col].renderer;
15116     },
15117
15118     /**
15119      * Sets the rendering (formatting) function for a column.
15120      * @param {Number} col The column index
15121      * @param {Function} fn The function to use to process the cell's raw data
15122      * to return HTML markup for the grid view. The render function is called with
15123      * the following parameters:<ul>
15124      * <li>Data value.</li>
15125      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15126      * <li>css A CSS style string to apply to the table cell.</li>
15127      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15128      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15129      * <li>Row index</li>
15130      * <li>Column index</li>
15131      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15132      */
15133     setRenderer : function(col, fn){
15134         this.config[col].renderer = fn;
15135     },
15136
15137     /**
15138      * Returns the width for the specified column.
15139      * @param {Number} col The column index
15140      * @return {Number}
15141      */
15142     getColumnWidth : function(col){
15143         return this.config[col].width * 1 || this.defaultWidth;
15144     },
15145
15146     /**
15147      * Sets the width for a column.
15148      * @param {Number} col The column index
15149      * @param {Number} width The new width
15150      */
15151     setColumnWidth : function(col, width, suppressEvent){
15152         this.config[col].width = width;
15153         this.totalWidth = null;
15154         if(!suppressEvent){
15155              this.fireEvent("widthchange", this, col, width);
15156         }
15157     },
15158
15159     /**
15160      * Returns the total width of all columns.
15161      * @param {Boolean} includeHidden True to include hidden column widths
15162      * @return {Number}
15163      */
15164     getTotalWidth : function(includeHidden){
15165         if(!this.totalWidth){
15166             this.totalWidth = 0;
15167             for(var i = 0, len = this.config.length; i < len; i++){
15168                 if(includeHidden || !this.isHidden(i)){
15169                     this.totalWidth += this.getColumnWidth(i);
15170                 }
15171             }
15172         }
15173         return this.totalWidth;
15174     },
15175
15176     /**
15177      * Returns the header for the specified column.
15178      * @param {Number} col The column index
15179      * @return {String}
15180      */
15181     getColumnHeader : function(col){
15182         return this.config[col].header;
15183     },
15184
15185     /**
15186      * Sets the header for a column.
15187      * @param {Number} col The column index
15188      * @param {String} header The new header
15189      */
15190     setColumnHeader : function(col, header){
15191         this.config[col].header = header;
15192         this.fireEvent("headerchange", this, col, header);
15193     },
15194
15195     /**
15196      * Returns the tooltip for the specified column.
15197      * @param {Number} col The column index
15198      * @return {String}
15199      */
15200     getColumnTooltip : function(col){
15201             return this.config[col].tooltip;
15202     },
15203     /**
15204      * Sets the tooltip for a column.
15205      * @param {Number} col The column index
15206      * @param {String} tooltip The new tooltip
15207      */
15208     setColumnTooltip : function(col, tooltip){
15209             this.config[col].tooltip = tooltip;
15210     },
15211
15212     /**
15213      * Returns the dataIndex for the specified column.
15214      * @param {Number} col The column index
15215      * @return {Number}
15216      */
15217     getDataIndex : function(col){
15218         return this.config[col].dataIndex;
15219     },
15220
15221     /**
15222      * Sets the dataIndex for a column.
15223      * @param {Number} col The column index
15224      * @param {Number} dataIndex The new dataIndex
15225      */
15226     setDataIndex : function(col, dataIndex){
15227         this.config[col].dataIndex = dataIndex;
15228     },
15229
15230     
15231     
15232     /**
15233      * Returns true if the cell is editable.
15234      * @param {Number} colIndex The column index
15235      * @param {Number} rowIndex The row index
15236      * @return {Boolean}
15237      */
15238     isCellEditable : function(colIndex, rowIndex){
15239         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15240     },
15241
15242     /**
15243      * Returns the editor defined for the cell/column.
15244      * return false or null to disable editing.
15245      * @param {Number} colIndex The column index
15246      * @param {Number} rowIndex The row index
15247      * @return {Object}
15248      */
15249     getCellEditor : function(colIndex, rowIndex){
15250         return this.config[colIndex].editor;
15251     },
15252
15253     /**
15254      * Sets if a column is editable.
15255      * @param {Number} col The column index
15256      * @param {Boolean} editable True if the column is editable
15257      */
15258     setEditable : function(col, editable){
15259         this.config[col].editable = editable;
15260     },
15261
15262
15263     /**
15264      * Returns true if the column is hidden.
15265      * @param {Number} colIndex The column index
15266      * @return {Boolean}
15267      */
15268     isHidden : function(colIndex){
15269         return this.config[colIndex].hidden;
15270     },
15271
15272
15273     /**
15274      * Returns true if the column width cannot be changed
15275      */
15276     isFixed : function(colIndex){
15277         return this.config[colIndex].fixed;
15278     },
15279
15280     /**
15281      * Returns true if the column can be resized
15282      * @return {Boolean}
15283      */
15284     isResizable : function(colIndex){
15285         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15286     },
15287     /**
15288      * Sets if a column is hidden.
15289      * @param {Number} colIndex The column index
15290      * @param {Boolean} hidden True if the column is hidden
15291      */
15292     setHidden : function(colIndex, hidden){
15293         this.config[colIndex].hidden = hidden;
15294         this.totalWidth = null;
15295         this.fireEvent("hiddenchange", this, colIndex, hidden);
15296     },
15297
15298     /**
15299      * Sets the editor for a column.
15300      * @param {Number} col The column index
15301      * @param {Object} editor The editor object
15302      */
15303     setEditor : function(col, editor){
15304         this.config[col].editor = editor;
15305     }
15306 });
15307
15308 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15309         if(typeof value == "string" && value.length < 1){
15310             return "&#160;";
15311         }
15312         return value;
15313 };
15314
15315 // Alias for backwards compatibility
15316 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15317
15318 /**
15319  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15320  * @class Roo.bootstrap.Table.RowSelectionModel
15321  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15322  * It supports multiple selections and keyboard selection/navigation. 
15323  * @constructor
15324  * @param {Object} config
15325  */
15326
15327 Roo.bootstrap.Table.RowSelectionModel = function(config){
15328     Roo.apply(this, config);
15329     this.selections = new Roo.util.MixedCollection(false, function(o){
15330         return o.id;
15331     });
15332
15333     this.last = false;
15334     this.lastActive = false;
15335
15336     this.addEvents({
15337         /**
15338              * @event selectionchange
15339              * Fires when the selection changes
15340              * @param {SelectionModel} this
15341              */
15342             "selectionchange" : true,
15343         /**
15344              * @event afterselectionchange
15345              * Fires after the selection changes (eg. by key press or clicking)
15346              * @param {SelectionModel} this
15347              */
15348             "afterselectionchange" : true,
15349         /**
15350              * @event beforerowselect
15351              * Fires when a row is selected being selected, return false to cancel.
15352              * @param {SelectionModel} this
15353              * @param {Number} rowIndex The selected index
15354              * @param {Boolean} keepExisting False if other selections will be cleared
15355              */
15356             "beforerowselect" : true,
15357         /**
15358              * @event rowselect
15359              * Fires when a row is selected.
15360              * @param {SelectionModel} this
15361              * @param {Number} rowIndex The selected index
15362              * @param {Roo.data.Record} r The record
15363              */
15364             "rowselect" : true,
15365         /**
15366              * @event rowdeselect
15367              * Fires when a row is deselected.
15368              * @param {SelectionModel} this
15369              * @param {Number} rowIndex The selected index
15370              */
15371         "rowdeselect" : true
15372     });
15373     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15374     this.locked = false;
15375 };
15376
15377 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15378     /**
15379      * @cfg {Boolean} singleSelect
15380      * True to allow selection of only one row at a time (defaults to false)
15381      */
15382     singleSelect : false,
15383
15384     // private
15385     initEvents : function(){
15386
15387         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15388             this.grid.on("mousedown", this.handleMouseDown, this);
15389         }else{ // allow click to work like normal
15390             this.grid.on("rowclick", this.handleDragableRowClick, this);
15391         }
15392
15393         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15394             "up" : function(e){
15395                 if(!e.shiftKey){
15396                     this.selectPrevious(e.shiftKey);
15397                 }else if(this.last !== false && this.lastActive !== false){
15398                     var last = this.last;
15399                     this.selectRange(this.last,  this.lastActive-1);
15400                     this.grid.getView().focusRow(this.lastActive);
15401                     if(last !== false){
15402                         this.last = last;
15403                     }
15404                 }else{
15405                     this.selectFirstRow();
15406                 }
15407                 this.fireEvent("afterselectionchange", this);
15408             },
15409             "down" : function(e){
15410                 if(!e.shiftKey){
15411                     this.selectNext(e.shiftKey);
15412                 }else if(this.last !== false && this.lastActive !== false){
15413                     var last = this.last;
15414                     this.selectRange(this.last,  this.lastActive+1);
15415                     this.grid.getView().focusRow(this.lastActive);
15416                     if(last !== false){
15417                         this.last = last;
15418                     }
15419                 }else{
15420                     this.selectFirstRow();
15421                 }
15422                 this.fireEvent("afterselectionchange", this);
15423             },
15424             scope: this
15425         });
15426
15427         var view = this.grid.view;
15428         view.on("refresh", this.onRefresh, this);
15429         view.on("rowupdated", this.onRowUpdated, this);
15430         view.on("rowremoved", this.onRemove, this);
15431     },
15432
15433     // private
15434     onRefresh : function(){
15435         var ds = this.grid.dataSource, i, v = this.grid.view;
15436         var s = this.selections;
15437         s.each(function(r){
15438             if((i = ds.indexOfId(r.id)) != -1){
15439                 v.onRowSelect(i);
15440             }else{
15441                 s.remove(r);
15442             }
15443         });
15444     },
15445
15446     // private
15447     onRemove : function(v, index, r){
15448         this.selections.remove(r);
15449     },
15450
15451     // private
15452     onRowUpdated : function(v, index, r){
15453         if(this.isSelected(r)){
15454             v.onRowSelect(index);
15455         }
15456     },
15457
15458     /**
15459      * Select records.
15460      * @param {Array} records The records to select
15461      * @param {Boolean} keepExisting (optional) True to keep existing selections
15462      */
15463     selectRecords : function(records, keepExisting){
15464         if(!keepExisting){
15465             this.clearSelections();
15466         }
15467         var ds = this.grid.dataSource;
15468         for(var i = 0, len = records.length; i < len; i++){
15469             this.selectRow(ds.indexOf(records[i]), true);
15470         }
15471     },
15472
15473     /**
15474      * Gets the number of selected rows.
15475      * @return {Number}
15476      */
15477     getCount : function(){
15478         return this.selections.length;
15479     },
15480
15481     /**
15482      * Selects the first row in the grid.
15483      */
15484     selectFirstRow : function(){
15485         this.selectRow(0);
15486     },
15487
15488     /**
15489      * Select the last row.
15490      * @param {Boolean} keepExisting (optional) True to keep existing selections
15491      */
15492     selectLastRow : function(keepExisting){
15493         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15494     },
15495
15496     /**
15497      * Selects the row immediately following the last selected row.
15498      * @param {Boolean} keepExisting (optional) True to keep existing selections
15499      */
15500     selectNext : function(keepExisting){
15501         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15502             this.selectRow(this.last+1, keepExisting);
15503             this.grid.getView().focusRow(this.last);
15504         }
15505     },
15506
15507     /**
15508      * Selects the row that precedes the last selected row.
15509      * @param {Boolean} keepExisting (optional) True to keep existing selections
15510      */
15511     selectPrevious : function(keepExisting){
15512         if(this.last){
15513             this.selectRow(this.last-1, keepExisting);
15514             this.grid.getView().focusRow(this.last);
15515         }
15516     },
15517
15518     /**
15519      * Returns the selected records
15520      * @return {Array} Array of selected records
15521      */
15522     getSelections : function(){
15523         return [].concat(this.selections.items);
15524     },
15525
15526     /**
15527      * Returns the first selected record.
15528      * @return {Record}
15529      */
15530     getSelected : function(){
15531         return this.selections.itemAt(0);
15532     },
15533
15534
15535     /**
15536      * Clears all selections.
15537      */
15538     clearSelections : function(fast){
15539         if(this.locked) return;
15540         if(fast !== true){
15541             var ds = this.grid.dataSource;
15542             var s = this.selections;
15543             s.each(function(r){
15544                 this.deselectRow(ds.indexOfId(r.id));
15545             }, this);
15546             s.clear();
15547         }else{
15548             this.selections.clear();
15549         }
15550         this.last = false;
15551     },
15552
15553
15554     /**
15555      * Selects all rows.
15556      */
15557     selectAll : function(){
15558         if(this.locked) return;
15559         this.selections.clear();
15560         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15561             this.selectRow(i, true);
15562         }
15563     },
15564
15565     /**
15566      * Returns True if there is a selection.
15567      * @return {Boolean}
15568      */
15569     hasSelection : function(){
15570         return this.selections.length > 0;
15571     },
15572
15573     /**
15574      * Returns True if the specified row is selected.
15575      * @param {Number/Record} record The record or index of the record to check
15576      * @return {Boolean}
15577      */
15578     isSelected : function(index){
15579         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15580         return (r && this.selections.key(r.id) ? true : false);
15581     },
15582
15583     /**
15584      * Returns True if the specified record id is selected.
15585      * @param {String} id The id of record to check
15586      * @return {Boolean}
15587      */
15588     isIdSelected : function(id){
15589         return (this.selections.key(id) ? true : false);
15590     },
15591
15592     // private
15593     handleMouseDown : function(e, t){
15594         var view = this.grid.getView(), rowIndex;
15595         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15596             return;
15597         };
15598         if(e.shiftKey && this.last !== false){
15599             var last = this.last;
15600             this.selectRange(last, rowIndex, e.ctrlKey);
15601             this.last = last; // reset the last
15602             view.focusRow(rowIndex);
15603         }else{
15604             var isSelected = this.isSelected(rowIndex);
15605             if(e.button !== 0 && isSelected){
15606                 view.focusRow(rowIndex);
15607             }else if(e.ctrlKey && isSelected){
15608                 this.deselectRow(rowIndex);
15609             }else if(!isSelected){
15610                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15611                 view.focusRow(rowIndex);
15612             }
15613         }
15614         this.fireEvent("afterselectionchange", this);
15615     },
15616     // private
15617     handleDragableRowClick :  function(grid, rowIndex, e) 
15618     {
15619         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15620             this.selectRow(rowIndex, false);
15621             grid.view.focusRow(rowIndex);
15622              this.fireEvent("afterselectionchange", this);
15623         }
15624     },
15625     
15626     /**
15627      * Selects multiple rows.
15628      * @param {Array} rows Array of the indexes of the row to select
15629      * @param {Boolean} keepExisting (optional) True to keep existing selections
15630      */
15631     selectRows : function(rows, keepExisting){
15632         if(!keepExisting){
15633             this.clearSelections();
15634         }
15635         for(var i = 0, len = rows.length; i < len; i++){
15636             this.selectRow(rows[i], true);
15637         }
15638     },
15639
15640     /**
15641      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15642      * @param {Number} startRow The index of the first row in the range
15643      * @param {Number} endRow The index of the last row in the range
15644      * @param {Boolean} keepExisting (optional) True to retain existing selections
15645      */
15646     selectRange : function(startRow, endRow, keepExisting){
15647         if(this.locked) return;
15648         if(!keepExisting){
15649             this.clearSelections();
15650         }
15651         if(startRow <= endRow){
15652             for(var i = startRow; i <= endRow; i++){
15653                 this.selectRow(i, true);
15654             }
15655         }else{
15656             for(var i = startRow; i >= endRow; i--){
15657                 this.selectRow(i, true);
15658             }
15659         }
15660     },
15661
15662     /**
15663      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15664      * @param {Number} startRow The index of the first row in the range
15665      * @param {Number} endRow The index of the last row in the range
15666      */
15667     deselectRange : function(startRow, endRow, preventViewNotify){
15668         if(this.locked) return;
15669         for(var i = startRow; i <= endRow; i++){
15670             this.deselectRow(i, preventViewNotify);
15671         }
15672     },
15673
15674     /**
15675      * Selects a row.
15676      * @param {Number} row The index of the row to select
15677      * @param {Boolean} keepExisting (optional) True to keep existing selections
15678      */
15679     selectRow : function(index, keepExisting, preventViewNotify){
15680         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15681         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15682             if(!keepExisting || this.singleSelect){
15683                 this.clearSelections();
15684             }
15685             var r = this.grid.dataSource.getAt(index);
15686             this.selections.add(r);
15687             this.last = this.lastActive = index;
15688             if(!preventViewNotify){
15689                 this.grid.getView().onRowSelect(index);
15690             }
15691             this.fireEvent("rowselect", this, index, r);
15692             this.fireEvent("selectionchange", this);
15693         }
15694     },
15695
15696     /**
15697      * Deselects a row.
15698      * @param {Number} row The index of the row to deselect
15699      */
15700     deselectRow : function(index, preventViewNotify){
15701         if(this.locked) return;
15702         if(this.last == index){
15703             this.last = false;
15704         }
15705         if(this.lastActive == index){
15706             this.lastActive = false;
15707         }
15708         var r = this.grid.dataSource.getAt(index);
15709         this.selections.remove(r);
15710         if(!preventViewNotify){
15711             this.grid.getView().onRowDeselect(index);
15712         }
15713         this.fireEvent("rowdeselect", this, index);
15714         this.fireEvent("selectionchange", this);
15715     },
15716
15717     // private
15718     restoreLast : function(){
15719         if(this._last){
15720             this.last = this._last;
15721         }
15722     },
15723
15724     // private
15725     acceptsNav : function(row, col, cm){
15726         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15727     },
15728
15729     // private
15730     onEditorKey : function(field, e){
15731         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15732         if(k == e.TAB){
15733             e.stopEvent();
15734             ed.completeEdit();
15735             if(e.shiftKey){
15736                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15737             }else{
15738                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15739             }
15740         }else if(k == e.ENTER && !e.ctrlKey){
15741             e.stopEvent();
15742             ed.completeEdit();
15743             if(e.shiftKey){
15744                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15745             }else{
15746                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15747             }
15748         }else if(k == e.ESC){
15749             ed.cancelEdit();
15750         }
15751         if(newCell){
15752             g.startEditing(newCell[0], newCell[1]);
15753         }
15754     }
15755 });/*
15756  * - LGPL
15757  *
15758  * element
15759  * 
15760  */
15761
15762 /**
15763  * @class Roo.bootstrap.MessageBar
15764  * @extends Roo.bootstrap.Component
15765  * Bootstrap MessageBar class
15766  * @cfg {String} html contents of the MessageBar
15767  * @cfg {String} weight (info | success | warning | danger) default info
15768  * @cfg {String} beforeClass insert the bar before the given class
15769  * @cfg {Boolean} closable (true | false) default false
15770  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15771  * 
15772  * @constructor
15773  * Create a new Element
15774  * @param {Object} config The config object
15775  */
15776
15777 Roo.bootstrap.MessageBar = function(config){
15778     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15779 };
15780
15781 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15782     
15783     html: '',
15784     weight: 'info',
15785     closable: false,
15786     fixed: false,
15787     beforeClass: 'bootstrap-sticky-wrap',
15788     
15789     getAutoCreate : function(){
15790         
15791         var cfg = {
15792             tag: 'div',
15793             cls: 'alert alert-dismissable alert-' + this.weight,
15794             cn: [
15795                 {
15796                     tag: 'span',
15797                     cls: 'message',
15798                     html: this.html || ''
15799                 }
15800             ]
15801         }
15802         
15803         if(this.fixed){
15804             cfg.cls += ' alert-messages-fixed';
15805         }
15806         
15807         if(this.closable){
15808             cfg.cn.push({
15809                 tag: 'button',
15810                 cls: 'close',
15811                 html: 'x'
15812             });
15813         }
15814         
15815         return cfg;
15816     },
15817     
15818     onRender : function(ct, position)
15819     {
15820         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15821         
15822         if(!this.el){
15823             var cfg = Roo.apply({},  this.getAutoCreate());
15824             cfg.id = Roo.id();
15825             
15826             if (this.cls) {
15827                 cfg.cls += ' ' + this.cls;
15828             }
15829             if (this.style) {
15830                 cfg.style = this.style;
15831             }
15832             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15833             
15834             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15835         }
15836         
15837         this.el.select('>button.close').on('click', this.hide, this);
15838         
15839     },
15840     
15841     show : function()
15842     {
15843         if (!this.rendered) {
15844             this.render();
15845         }
15846         
15847         this.el.show();
15848         
15849         this.fireEvent('show', this);
15850         
15851     },
15852     
15853     hide : function()
15854     {
15855         if (!this.rendered) {
15856             this.render();
15857         }
15858         
15859         this.el.hide();
15860         
15861         this.fireEvent('hide', this);
15862     },
15863     
15864     update : function()
15865     {
15866 //        var e = this.el.dom.firstChild;
15867 //        
15868 //        if(this.closable){
15869 //            e = e.nextSibling;
15870 //        }
15871 //        
15872 //        e.data = this.html || '';
15873
15874         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15875     }
15876    
15877 });
15878
15879  
15880
15881