roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 //        this.maskEl.enableDisplayMode("block");
2955 //        this.maskEl.show();
2956         
2957         this.store.on('load', this.onLoad, this);
2958         this.store.on('beforeload', this.onBeforeLoad, this);
2959         
2960         this.store.load();
2961         
2962         
2963         
2964     },
2965     
2966     renderHeader : function()
2967     {
2968         var header = {
2969             tag: 'thead',
2970             cn : []
2971         };
2972         
2973         var cm = this.cm;
2974         
2975         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2976             var index = cm.getDataIndex(i);
2977             Roo.log('header!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
2978             Roo.log(i + " : " + index);
2979             header.cn.push({
2980                 tag: 'th',
2981                 html: cm.getColumnHeader(i)
2982             })
2983         }
2984         
2985         return header;
2986     },
2987     
2988     renderBody : function()
2989     {
2990         var body = {
2991             tag: 'tbody',
2992             cn : []
2993         };
2994         
2995         return body;
2996     },
2997     
2998     renderFooter : function()
2999     {
3000         var footer = {
3001             tag: 'tfoot',
3002             cn : []
3003         };
3004         
3005         return footer;
3006     },
3007     
3008     onLoad : function()
3009     {
3010         Roo.log('ds onload');
3011         
3012         var cm = this.cm;
3013         
3014         var tbody = this.el.select('tbody', true).first();
3015         
3016         var renders = [];
3017         
3018         if(this.store.getCount() > 0){
3019             this.store.data.each(function(d){
3020                 var row = {
3021                     tag : 'tr',
3022                     cn : []
3023                 };
3024                 
3025                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3026                     var renderer = cm.getRenderer(i);
3027                     var config = cm.config[i];
3028                     var value = '';
3029                     var id = Roo.id();
3030                     
3031                     if(typeof(renderer) !== 'undefined'){
3032                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3033                     }
3034                     
3035                     if(typeof(value) === 'object'){
3036                         renders.push({
3037                             id : id,
3038                             cfg : value 
3039                         })
3040                     }
3041                     
3042                     var td = {
3043                         tag: 'td',
3044                         id: id,
3045                         html: (typeof(value) === 'object') ? '' : value
3046                     };
3047                     
3048                     if(typeof(config.width) != 'undefined'){
3049                         td.width = config.width;
3050                     }
3051                     
3052                     row.cn.push(td);
3053                    
3054                 }
3055                 
3056                 tbody.createChild(row);
3057                 
3058             });
3059         }
3060         
3061         
3062         if(renders.length){
3063             var _this = this;
3064             Roo.each(renders, function(r){
3065                 _this.renderColumn(r);
3066             })
3067         }
3068 //        
3069 //        if(this.loadMask){
3070 //            this.maskEl.hide();
3071 //        }
3072     },
3073     
3074     onBeforeLoad : function()
3075     {
3076         Roo.log('ds onBeforeLoad');
3077         
3078         this.clear();
3079         
3080 //        if(this.loadMask){
3081 //            this.maskEl.show();
3082 //        }
3083     },
3084     
3085     clear : function()
3086     {
3087         this.el.select('tbody', true).first().dom.innerHTML = '';
3088     },
3089     
3090     getSelectionModel : function(){
3091         if(!this.selModel){
3092             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3093         }
3094         return this.selModel;
3095     },
3096     
3097     renderColumn : function(r)
3098     {
3099         var _this = this;
3100         r.cfg.render(Roo.get(r.id));
3101         
3102         if(r.cfg.cn){
3103             Roo.each(r.cfg.cn, function(c){
3104                 var child = {
3105                     id: r.id,
3106                     cfg: c
3107                 }
3108                 _this.renderColumn(child);
3109             })
3110         }
3111     }
3112    
3113 });
3114
3115  
3116
3117  /*
3118  * - LGPL
3119  *
3120  * table cell
3121  * 
3122  */
3123
3124 /**
3125  * @class Roo.bootstrap.TableCell
3126  * @extends Roo.bootstrap.Component
3127  * Bootstrap TableCell class
3128  * @cfg {String} html cell contain text
3129  * @cfg {String} cls cell class
3130  * @cfg {String} tag cell tag (td|th) default td
3131  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3132  * @cfg {String} align Aligns the content in a cell
3133  * @cfg {String} axis Categorizes cells
3134  * @cfg {String} bgcolor Specifies the background color of a cell
3135  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3136  * @cfg {Number} colspan Specifies the number of columns a cell should span
3137  * @cfg {String} headers Specifies one or more header cells a cell is related to
3138  * @cfg {Number} height Sets the height of a cell
3139  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3140  * @cfg {Number} rowspan Sets the number of rows a cell should span
3141  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3142  * @cfg {String} valign Vertical aligns the content in a cell
3143  * @cfg {Number} width Specifies the width of a cell
3144  * 
3145  * @constructor
3146  * Create a new TableCell
3147  * @param {Object} config The config object
3148  */
3149
3150 Roo.bootstrap.TableCell = function(config){
3151     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3152 };
3153
3154 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3155     
3156     html: false,
3157     cls: false,
3158     tag: false,
3159     abbr: false,
3160     align: false,
3161     axis: false,
3162     bgcolor: false,
3163     charoff: false,
3164     colspan: false,
3165     headers: false,
3166     height: false,
3167     nowrap: false,
3168     rowspan: false,
3169     scope: false,
3170     valign: false,
3171     width: false,
3172     
3173     
3174     getAutoCreate : function(){
3175         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3176         
3177         cfg = {
3178             tag: 'td'
3179         }
3180         
3181         if(this.tag){
3182             cfg.tag = this.tag;
3183         }
3184         
3185         if (this.html) {
3186             cfg.html=this.html
3187         }
3188         if (this.cls) {
3189             cfg.cls=this.cls
3190         }
3191         if (this.abbr) {
3192             cfg.abbr=this.abbr
3193         }
3194         if (this.align) {
3195             cfg.align=this.align
3196         }
3197         if (this.axis) {
3198             cfg.axis=this.axis
3199         }
3200         if (this.bgcolor) {
3201             cfg.bgcolor=this.bgcolor
3202         }
3203         if (this.charoff) {
3204             cfg.charoff=this.charoff
3205         }
3206         if (this.colspan) {
3207             cfg.colspan=this.colspan
3208         }
3209         if (this.headers) {
3210             cfg.headers=this.headers
3211         }
3212         if (this.height) {
3213             cfg.height=this.height
3214         }
3215         if (this.nowrap) {
3216             cfg.nowrap=this.nowrap
3217         }
3218         if (this.rowspan) {
3219             cfg.rowspan=this.rowspan
3220         }
3221         if (this.scope) {
3222             cfg.scope=this.scope
3223         }
3224         if (this.valign) {
3225             cfg.valign=this.valign
3226         }
3227         if (this.width) {
3228             cfg.width=this.width
3229         }
3230         
3231         
3232         return cfg;
3233     }
3234    
3235 });
3236
3237  
3238
3239  /*
3240  * - LGPL
3241  *
3242  * table row
3243  * 
3244  */
3245
3246 /**
3247  * @class Roo.bootstrap.TableRow
3248  * @extends Roo.bootstrap.Component
3249  * Bootstrap TableRow class
3250  * @cfg {String} cls row class
3251  * @cfg {String} align Aligns the content in a table row
3252  * @cfg {String} bgcolor Specifies a background color for a table row
3253  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3254  * @cfg {String} valign Vertical aligns the content in a table row
3255  * 
3256  * @constructor
3257  * Create a new TableRow
3258  * @param {Object} config The config object
3259  */
3260
3261 Roo.bootstrap.TableRow = function(config){
3262     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3263 };
3264
3265 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3266     
3267     cls: false,
3268     align: false,
3269     bgcolor: false,
3270     charoff: false,
3271     valign: false,
3272     
3273     getAutoCreate : function(){
3274         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3275         
3276         cfg = {
3277             tag: 'tr'
3278         }
3279             
3280         if(this.cls){
3281             cfg.cls = this.cls;
3282         }
3283         if(this.align){
3284             cfg.align = this.align;
3285         }
3286         if(this.bgcolor){
3287             cfg.bgcolor = this.bgcolor;
3288         }
3289         if(this.charoff){
3290             cfg.charoff = this.charoff;
3291         }
3292         if(this.valign){
3293             cfg.valign = this.valign;
3294         }
3295         
3296         return cfg;
3297     }
3298    
3299 });
3300
3301  
3302
3303  /*
3304  * - LGPL
3305  *
3306  * table body
3307  * 
3308  */
3309
3310 /**
3311  * @class Roo.bootstrap.TableBody
3312  * @extends Roo.bootstrap.Component
3313  * Bootstrap TableBody class
3314  * @cfg {String} cls element class
3315  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3316  * @cfg {String} align Aligns the content inside the element
3317  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3318  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3319  * 
3320  * @constructor
3321  * Create a new TableBody
3322  * @param {Object} config The config object
3323  */
3324
3325 Roo.bootstrap.TableBody = function(config){
3326     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3327 };
3328
3329 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3330     
3331     cls: false,
3332     tag: false,
3333     align: false,
3334     charoff: false,
3335     valign: false,
3336     
3337     getAutoCreate : function(){
3338         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3339         
3340         cfg = {
3341             tag: 'tbody'
3342         }
3343             
3344         if (this.cls) {
3345             cfg.cls=this.cls
3346         }
3347         if(this.tag){
3348             cfg.tag = this.tag;
3349         }
3350         
3351         if(this.align){
3352             cfg.align = this.align;
3353         }
3354         if(this.charoff){
3355             cfg.charoff = this.charoff;
3356         }
3357         if(this.valign){
3358             cfg.valign = this.valign;
3359         }
3360         
3361         return cfg;
3362     }
3363     
3364     
3365 //    initEvents : function()
3366 //    {
3367 //        
3368 //        if(!this.store){
3369 //            return;
3370 //        }
3371 //        
3372 //        this.store = Roo.factory(this.store, Roo.data);
3373 //        this.store.on('load', this.onLoad, this);
3374 //        
3375 //        this.store.load();
3376 //        
3377 //    },
3378 //    
3379 //    onLoad: function () 
3380 //    {   
3381 //        this.fireEvent('load', this);
3382 //    }
3383 //    
3384 //   
3385 });
3386
3387  
3388
3389  /*
3390  * Based on:
3391  * Ext JS Library 1.1.1
3392  * Copyright(c) 2006-2007, Ext JS, LLC.
3393  *
3394  * Originally Released Under LGPL - original licence link has changed is not relivant.
3395  *
3396  * Fork - LGPL
3397  * <script type="text/javascript">
3398  */
3399
3400 // as we use this in bootstrap.
3401 Roo.namespace('Roo.form');
3402  /**
3403  * @class Roo.form.Action
3404  * Internal Class used to handle form actions
3405  * @constructor
3406  * @param {Roo.form.BasicForm} el The form element or its id
3407  * @param {Object} config Configuration options
3408  */
3409
3410  
3411  
3412 // define the action interface
3413 Roo.form.Action = function(form, options){
3414     this.form = form;
3415     this.options = options || {};
3416 };
3417 /**
3418  * Client Validation Failed
3419  * @const 
3420  */
3421 Roo.form.Action.CLIENT_INVALID = 'client';
3422 /**
3423  * Server Validation Failed
3424  * @const 
3425  */
3426 Roo.form.Action.SERVER_INVALID = 'server';
3427  /**
3428  * Connect to Server Failed
3429  * @const 
3430  */
3431 Roo.form.Action.CONNECT_FAILURE = 'connect';
3432 /**
3433  * Reading Data from Server Failed
3434  * @const 
3435  */
3436 Roo.form.Action.LOAD_FAILURE = 'load';
3437
3438 Roo.form.Action.prototype = {
3439     type : 'default',
3440     failureType : undefined,
3441     response : undefined,
3442     result : undefined,
3443
3444     // interface method
3445     run : function(options){
3446
3447     },
3448
3449     // interface method
3450     success : function(response){
3451
3452     },
3453
3454     // interface method
3455     handleResponse : function(response){
3456
3457     },
3458
3459     // default connection failure
3460     failure : function(response){
3461         
3462         this.response = response;
3463         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3464         this.form.afterAction(this, false);
3465     },
3466
3467     processResponse : function(response){
3468         this.response = response;
3469         if(!response.responseText){
3470             return true;
3471         }
3472         this.result = this.handleResponse(response);
3473         return this.result;
3474     },
3475
3476     // utility functions used internally
3477     getUrl : function(appendParams){
3478         var url = this.options.url || this.form.url || this.form.el.dom.action;
3479         if(appendParams){
3480             var p = this.getParams();
3481             if(p){
3482                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3483             }
3484         }
3485         return url;
3486     },
3487
3488     getMethod : function(){
3489         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3490     },
3491
3492     getParams : function(){
3493         var bp = this.form.baseParams;
3494         var p = this.options.params;
3495         if(p){
3496             if(typeof p == "object"){
3497                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3498             }else if(typeof p == 'string' && bp){
3499                 p += '&' + Roo.urlEncode(bp);
3500             }
3501         }else if(bp){
3502             p = Roo.urlEncode(bp);
3503         }
3504         return p;
3505     },
3506
3507     createCallback : function(){
3508         return {
3509             success: this.success,
3510             failure: this.failure,
3511             scope: this,
3512             timeout: (this.form.timeout*1000),
3513             upload: this.form.fileUpload ? this.success : undefined
3514         };
3515     }
3516 };
3517
3518 Roo.form.Action.Submit = function(form, options){
3519     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3520 };
3521
3522 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3523     type : 'submit',
3524
3525     haveProgress : false,
3526     uploadComplete : false,
3527     
3528     // uploadProgress indicator.
3529     uploadProgress : function()
3530     {
3531         if (!this.form.progressUrl) {
3532             return;
3533         }
3534         
3535         if (!this.haveProgress) {
3536             Roo.MessageBox.progress("Uploading", "Uploading");
3537         }
3538         if (this.uploadComplete) {
3539            Roo.MessageBox.hide();
3540            return;
3541         }
3542         
3543         this.haveProgress = true;
3544    
3545         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3546         
3547         var c = new Roo.data.Connection();
3548         c.request({
3549             url : this.form.progressUrl,
3550             params: {
3551                 id : uid
3552             },
3553             method: 'GET',
3554             success : function(req){
3555                //console.log(data);
3556                 var rdata = false;
3557                 var edata;
3558                 try  {
3559                    rdata = Roo.decode(req.responseText)
3560                 } catch (e) {
3561                     Roo.log("Invalid data from server..");
3562                     Roo.log(edata);
3563                     return;
3564                 }
3565                 if (!rdata || !rdata.success) {
3566                     Roo.log(rdata);
3567                     Roo.MessageBox.alert(Roo.encode(rdata));
3568                     return;
3569                 }
3570                 var data = rdata.data;
3571                 
3572                 if (this.uploadComplete) {
3573                    Roo.MessageBox.hide();
3574                    return;
3575                 }
3576                    
3577                 if (data){
3578                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3579                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3580                     );
3581                 }
3582                 this.uploadProgress.defer(2000,this);
3583             },
3584        
3585             failure: function(data) {
3586                 Roo.log('progress url failed ');
3587                 Roo.log(data);
3588             },
3589             scope : this
3590         });
3591            
3592     },
3593     
3594     
3595     run : function()
3596     {
3597         // run get Values on the form, so it syncs any secondary forms.
3598         this.form.getValues();
3599         
3600         var o = this.options;
3601         var method = this.getMethod();
3602         var isPost = method == 'POST';
3603         if(o.clientValidation === false || this.form.isValid()){
3604             
3605             if (this.form.progressUrl) {
3606                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3607                     (new Date() * 1) + '' + Math.random());
3608                     
3609             } 
3610             
3611             
3612             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3613                 form:this.form.el.dom,
3614                 url:this.getUrl(!isPost),
3615                 method: method,
3616                 params:isPost ? this.getParams() : null,
3617                 isUpload: this.form.fileUpload
3618             }));
3619             
3620             this.uploadProgress();
3621
3622         }else if (o.clientValidation !== false){ // client validation failed
3623             this.failureType = Roo.form.Action.CLIENT_INVALID;
3624             this.form.afterAction(this, false);
3625         }
3626     },
3627
3628     success : function(response)
3629     {
3630         this.uploadComplete= true;
3631         if (this.haveProgress) {
3632             Roo.MessageBox.hide();
3633         }
3634         
3635         
3636         var result = this.processResponse(response);
3637         if(result === true || result.success){
3638             this.form.afterAction(this, true);
3639             return;
3640         }
3641         if(result.errors){
3642             this.form.markInvalid(result.errors);
3643             this.failureType = Roo.form.Action.SERVER_INVALID;
3644         }
3645         this.form.afterAction(this, false);
3646     },
3647     failure : function(response)
3648     {
3649         this.uploadComplete= true;
3650         if (this.haveProgress) {
3651             Roo.MessageBox.hide();
3652         }
3653         
3654         this.response = response;
3655         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3656         this.form.afterAction(this, false);
3657     },
3658     
3659     handleResponse : function(response){
3660         if(this.form.errorReader){
3661             var rs = this.form.errorReader.read(response);
3662             var errors = [];
3663             if(rs.records){
3664                 for(var i = 0, len = rs.records.length; i < len; i++) {
3665                     var r = rs.records[i];
3666                     errors[i] = r.data;
3667                 }
3668             }
3669             if(errors.length < 1){
3670                 errors = null;
3671             }
3672             return {
3673                 success : rs.success,
3674                 errors : errors
3675             };
3676         }
3677         var ret = false;
3678         try {
3679             ret = Roo.decode(response.responseText);
3680         } catch (e) {
3681             ret = {
3682                 success: false,
3683                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3684                 errors : []
3685             };
3686         }
3687         return ret;
3688         
3689     }
3690 });
3691
3692
3693 Roo.form.Action.Load = function(form, options){
3694     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3695     this.reader = this.form.reader;
3696 };
3697
3698 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3699     type : 'load',
3700
3701     run : function(){
3702         
3703         Roo.Ajax.request(Roo.apply(
3704                 this.createCallback(), {
3705                     method:this.getMethod(),
3706                     url:this.getUrl(false),
3707                     params:this.getParams()
3708         }));
3709     },
3710
3711     success : function(response){
3712         
3713         var result = this.processResponse(response);
3714         if(result === true || !result.success || !result.data){
3715             this.failureType = Roo.form.Action.LOAD_FAILURE;
3716             this.form.afterAction(this, false);
3717             return;
3718         }
3719         this.form.clearInvalid();
3720         this.form.setValues(result.data);
3721         this.form.afterAction(this, true);
3722     },
3723
3724     handleResponse : function(response){
3725         if(this.form.reader){
3726             var rs = this.form.reader.read(response);
3727             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3728             return {
3729                 success : rs.success,
3730                 data : data
3731             };
3732         }
3733         return Roo.decode(response.responseText);
3734     }
3735 });
3736
3737 Roo.form.Action.ACTION_TYPES = {
3738     'load' : Roo.form.Action.Load,
3739     'submit' : Roo.form.Action.Submit
3740 };/*
3741  * - LGPL
3742  *
3743  * form
3744  * 
3745  */
3746
3747 /**
3748  * @class Roo.bootstrap.Form
3749  * @extends Roo.bootstrap.Component
3750  * Bootstrap Form class
3751  * @cfg {String} method  GET | POST (default POST)
3752  * @cfg {String} labelAlign top | left (default top)
3753   * @cfg {String} align left  | right - for navbars
3754
3755  * 
3756  * @constructor
3757  * Create a new Form
3758  * @param {Object} config The config object
3759  */
3760
3761
3762 Roo.bootstrap.Form = function(config){
3763     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3764     this.addEvents({
3765         /**
3766          * @event clientvalidation
3767          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3768          * @param {Form} this
3769          * @param {Boolean} valid true if the form has passed client-side validation
3770          */
3771         clientvalidation: true,
3772         /**
3773          * @event beforeaction
3774          * Fires before any action is performed. Return false to cancel the action.
3775          * @param {Form} this
3776          * @param {Action} action The action to be performed
3777          */
3778         beforeaction: true,
3779         /**
3780          * @event actionfailed
3781          * Fires when an action fails.
3782          * @param {Form} this
3783          * @param {Action} action The action that failed
3784          */
3785         actionfailed : true,
3786         /**
3787          * @event actioncomplete
3788          * Fires when an action is completed.
3789          * @param {Form} this
3790          * @param {Action} action The action that completed
3791          */
3792         actioncomplete : true
3793     });
3794     
3795 };
3796
3797 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3798       
3799      /**
3800      * @cfg {String} method
3801      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3802      */
3803     method : 'POST',
3804     /**
3805      * @cfg {String} url
3806      * The URL to use for form actions if one isn't supplied in the action options.
3807      */
3808     /**
3809      * @cfg {Boolean} fileUpload
3810      * Set to true if this form is a file upload.
3811      */
3812      
3813     /**
3814      * @cfg {Object} baseParams
3815      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3816      */
3817       
3818     /**
3819      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3820      */
3821     timeout: 30,
3822     /**
3823      * @cfg {Sting} align (left|right) for navbar forms
3824      */
3825     align : 'left',
3826
3827     // private
3828     activeAction : null,
3829  
3830     /**
3831      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3832      * element by passing it or its id or mask the form itself by passing in true.
3833      * @type Mixed
3834      */
3835     waitMsgTarget : false,
3836     
3837      
3838     
3839     /**
3840      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3841      * element by passing it or its id or mask the form itself by passing in true.
3842      * @type Mixed
3843      */
3844     
3845     getAutoCreate : function(){
3846         
3847         var cfg = {
3848             tag: 'form',
3849             method : this.method || 'POST',
3850             id : this.id || Roo.id(),
3851             cls : ''
3852         }
3853         if (this.parent().xtype.match(/^Nav/)) {
3854             cfg.cls = 'navbar-form navbar-' + this.align;
3855             
3856         }
3857         
3858         if (this.labelAlign == 'left' ) {
3859             cfg.cls += ' form-horizontal';
3860         }
3861         
3862         
3863         return cfg;
3864     },
3865     initEvents : function()
3866     {
3867         this.el.on('submit', this.onSubmit, this);
3868         
3869         
3870     },
3871     // private
3872     onSubmit : function(e){
3873         e.stopEvent();
3874     },
3875     
3876      /**
3877      * Returns true if client-side validation on the form is successful.
3878      * @return Boolean
3879      */
3880     isValid : function(){
3881         var items = this.getItems();
3882         var valid = true;
3883         items.each(function(f){
3884            if(!f.validate()){
3885                valid = false;
3886                
3887            }
3888         });
3889         return valid;
3890     },
3891     /**
3892      * Returns true if any fields in this form have changed since their original load.
3893      * @return Boolean
3894      */
3895     isDirty : function(){
3896         var dirty = false;
3897         var items = this.getItems();
3898         items.each(function(f){
3899            if(f.isDirty()){
3900                dirty = true;
3901                return false;
3902            }
3903            return true;
3904         });
3905         return dirty;
3906     },
3907      /**
3908      * Performs a predefined action (submit or load) or custom actions you define on this form.
3909      * @param {String} actionName The name of the action type
3910      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3911      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3912      * accept other config options):
3913      * <pre>
3914 Property          Type             Description
3915 ----------------  ---------------  ----------------------------------------------------------------------------------
3916 url               String           The url for the action (defaults to the form's url)
3917 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3918 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3919 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3920                                    validate the form on the client (defaults to false)
3921      * </pre>
3922      * @return {BasicForm} this
3923      */
3924     doAction : function(action, options){
3925         if(typeof action == 'string'){
3926             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3927         }
3928         if(this.fireEvent('beforeaction', this, action) !== false){
3929             this.beforeAction(action);
3930             action.run.defer(100, action);
3931         }
3932         return this;
3933     },
3934     
3935     // private
3936     beforeAction : function(action){
3937         var o = action.options;
3938         
3939         // not really supported yet.. ??
3940         
3941         //if(this.waitMsgTarget === true){
3942             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3943         //}else if(this.waitMsgTarget){
3944         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3945         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3946         //}else {
3947         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3948        // }
3949          
3950     },
3951
3952     // private
3953     afterAction : function(action, success){
3954         this.activeAction = null;
3955         var o = action.options;
3956         
3957         //if(this.waitMsgTarget === true){
3958             this.el.unmask();
3959         //}else if(this.waitMsgTarget){
3960         //    this.waitMsgTarget.unmask();
3961         //}else{
3962         //    Roo.MessageBox.updateProgress(1);
3963         //    Roo.MessageBox.hide();
3964        // }
3965         // 
3966         if(success){
3967             if(o.reset){
3968                 this.reset();
3969             }
3970             Roo.callback(o.success, o.scope, [this, action]);
3971             this.fireEvent('actioncomplete', this, action);
3972             
3973         }else{
3974             
3975             // failure condition..
3976             // we have a scenario where updates need confirming.
3977             // eg. if a locking scenario exists..
3978             // we look for { errors : { needs_confirm : true }} in the response.
3979             if (
3980                 (typeof(action.result) != 'undefined')  &&
3981                 (typeof(action.result.errors) != 'undefined')  &&
3982                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3983            ){
3984                 var _t = this;
3985                 Roo.log("not supported yet");
3986                  /*
3987                 
3988                 Roo.MessageBox.confirm(
3989                     "Change requires confirmation",
3990                     action.result.errorMsg,
3991                     function(r) {
3992                         if (r != 'yes') {
3993                             return;
3994                         }
3995                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3996                     }
3997                     
3998                 );
3999                 */
4000                 
4001                 
4002                 return;
4003             }
4004             
4005             Roo.callback(o.failure, o.scope, [this, action]);
4006             // show an error message if no failed handler is set..
4007             if (!this.hasListener('actionfailed')) {
4008                 Roo.log("need to add dialog support");
4009                 /*
4010                 Roo.MessageBox.alert("Error",
4011                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4012                         action.result.errorMsg :
4013                         "Saving Failed, please check your entries or try again"
4014                 );
4015                 */
4016             }
4017             
4018             this.fireEvent('actionfailed', this, action);
4019         }
4020         
4021     },
4022     /**
4023      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4024      * @param {String} id The value to search for
4025      * @return Field
4026      */
4027     findField : function(id){
4028         var items = this.getItems();
4029         var field = items.get(id);
4030         if(!field){
4031              items.each(function(f){
4032                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4033                     field = f;
4034                     return false;
4035                 }
4036                 return true;
4037             });
4038         }
4039         return field || null;
4040     },
4041      /**
4042      * Mark fields in this form invalid in bulk.
4043      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4044      * @return {BasicForm} this
4045      */
4046     markInvalid : function(errors){
4047         if(errors instanceof Array){
4048             for(var i = 0, len = errors.length; i < len; i++){
4049                 var fieldError = errors[i];
4050                 var f = this.findField(fieldError.id);
4051                 if(f){
4052                     f.markInvalid(fieldError.msg);
4053                 }
4054             }
4055         }else{
4056             var field, id;
4057             for(id in errors){
4058                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4059                     field.markInvalid(errors[id]);
4060                 }
4061             }
4062         }
4063         //Roo.each(this.childForms || [], function (f) {
4064         //    f.markInvalid(errors);
4065         //});
4066         
4067         return this;
4068     },
4069
4070     /**
4071      * Set values for fields in this form in bulk.
4072      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4073      * @return {BasicForm} this
4074      */
4075     setValues : function(values){
4076         if(values instanceof Array){ // array of objects
4077             for(var i = 0, len = values.length; i < len; i++){
4078                 var v = values[i];
4079                 var f = this.findField(v.id);
4080                 if(f){
4081                     f.setValue(v.value);
4082                     if(this.trackResetOnLoad){
4083                         f.originalValue = f.getValue();
4084                     }
4085                 }
4086             }
4087         }else{ // object hash
4088             var field, id;
4089             for(id in values){
4090                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4091                     
4092                     if (field.setFromData && 
4093                         field.valueField && 
4094                         field.displayField &&
4095                         // combos' with local stores can 
4096                         // be queried via setValue()
4097                         // to set their value..
4098                         (field.store && !field.store.isLocal)
4099                         ) {
4100                         // it's a combo
4101                         var sd = { };
4102                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4103                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4104                         field.setFromData(sd);
4105                         
4106                     } else {
4107                         field.setValue(values[id]);
4108                     }
4109                     
4110                     
4111                     if(this.trackResetOnLoad){
4112                         field.originalValue = field.getValue();
4113                     }
4114                 }
4115             }
4116         }
4117          
4118         //Roo.each(this.childForms || [], function (f) {
4119         //    f.setValues(values);
4120         //});
4121                 
4122         return this;
4123     },
4124
4125     /**
4126      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4127      * they are returned as an array.
4128      * @param {Boolean} asString
4129      * @return {Object}
4130      */
4131     getValues : function(asString){
4132         //if (this.childForms) {
4133             // copy values from the child forms
4134         //    Roo.each(this.childForms, function (f) {
4135         //        this.setValues(f.getValues());
4136         //    }, this);
4137         //}
4138         
4139         
4140         
4141         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4142         if(asString === true){
4143             return fs;
4144         }
4145         return Roo.urlDecode(fs);
4146     },
4147     
4148     /**
4149      * Returns the fields in this form as an object with key/value pairs. 
4150      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4151      * @return {Object}
4152      */
4153     getFieldValues : function(with_hidden)
4154     {
4155         var items = this.getItems();
4156         var ret = {};
4157         items.each(function(f){
4158             if (!f.getName()) {
4159                 return;
4160             }
4161             var v = f.getValue();
4162             if (f.inputType =='radio') {
4163                 if (typeof(ret[f.getName()]) == 'undefined') {
4164                     ret[f.getName()] = ''; // empty..
4165                 }
4166                 
4167                 if (!f.el.dom.checked) {
4168                     return;
4169                     
4170                 }
4171                 v = f.el.dom.value;
4172                 
4173             }
4174             
4175             // not sure if this supported any more..
4176             if ((typeof(v) == 'object') && f.getRawValue) {
4177                 v = f.getRawValue() ; // dates..
4178             }
4179             // combo boxes where name != hiddenName...
4180             if (f.name != f.getName()) {
4181                 ret[f.name] = f.getRawValue();
4182             }
4183             ret[f.getName()] = v;
4184         });
4185         
4186         return ret;
4187     },
4188
4189     /**
4190      * Clears all invalid messages in this form.
4191      * @return {BasicForm} this
4192      */
4193     clearInvalid : function(){
4194         var items = this.getItems();
4195         
4196         items.each(function(f){
4197            f.clearInvalid();
4198         });
4199         
4200         
4201         
4202         return this;
4203     },
4204
4205     /**
4206      * Resets this form.
4207      * @return {BasicForm} this
4208      */
4209     reset : function(){
4210         var items = this.getItems();
4211         items.each(function(f){
4212             f.reset();
4213         });
4214         
4215         Roo.each(this.childForms || [], function (f) {
4216             f.reset();
4217         });
4218        
4219         
4220         return this;
4221     },
4222     getItems : function()
4223     {
4224         var r=new Roo.util.MixedCollection(false, function(o){
4225             return o.id || (o.id = Roo.id());
4226         });
4227         var iter = function(el) {
4228             if (el.inputEl) {
4229                 r.add(el);
4230             }
4231             if (!el.items) {
4232                 return;
4233             }
4234             Roo.each(el.items,function(e) {
4235                 iter(e);
4236             });
4237             
4238             
4239         };
4240         iter(this);
4241         return r;
4242         
4243         
4244         
4245         
4246     }
4247     
4248 });
4249
4250  
4251 /*
4252  * Based on:
4253  * Ext JS Library 1.1.1
4254  * Copyright(c) 2006-2007, Ext JS, LLC.
4255  *
4256  * Originally Released Under LGPL - original licence link has changed is not relivant.
4257  *
4258  * Fork - LGPL
4259  * <script type="text/javascript">
4260  */
4261 /**
4262  * @class Roo.form.VTypes
4263  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4264  * @singleton
4265  */
4266 Roo.form.VTypes = function(){
4267     // closure these in so they are only created once.
4268     var alpha = /^[a-zA-Z_]+$/;
4269     var alphanum = /^[a-zA-Z0-9_]+$/;
4270     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4271     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4272
4273     // All these messages and functions are configurable
4274     return {
4275         /**
4276          * The function used to validate email addresses
4277          * @param {String} value The email address
4278          */
4279         'email' : function(v){
4280             return email.test(v);
4281         },
4282         /**
4283          * The error text to display when the email validation function returns false
4284          * @type String
4285          */
4286         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4287         /**
4288          * The keystroke filter mask to be applied on email input
4289          * @type RegExp
4290          */
4291         'emailMask' : /[a-z0-9_\.\-@]/i,
4292
4293         /**
4294          * The function used to validate URLs
4295          * @param {String} value The URL
4296          */
4297         'url' : function(v){
4298             return url.test(v);
4299         },
4300         /**
4301          * The error text to display when the url validation function returns false
4302          * @type String
4303          */
4304         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4305         
4306         /**
4307          * The function used to validate alpha values
4308          * @param {String} value The value
4309          */
4310         'alpha' : function(v){
4311             return alpha.test(v);
4312         },
4313         /**
4314          * The error text to display when the alpha validation function returns false
4315          * @type String
4316          */
4317         'alphaText' : 'This field should only contain letters and _',
4318         /**
4319          * The keystroke filter mask to be applied on alpha input
4320          * @type RegExp
4321          */
4322         'alphaMask' : /[a-z_]/i,
4323
4324         /**
4325          * The function used to validate alphanumeric values
4326          * @param {String} value The value
4327          */
4328         'alphanum' : function(v){
4329             return alphanum.test(v);
4330         },
4331         /**
4332          * The error text to display when the alphanumeric validation function returns false
4333          * @type String
4334          */
4335         'alphanumText' : 'This field should only contain letters, numbers and _',
4336         /**
4337          * The keystroke filter mask to be applied on alphanumeric input
4338          * @type RegExp
4339          */
4340         'alphanumMask' : /[a-z0-9_]/i
4341     };
4342 }();/*
4343  * - LGPL
4344  *
4345  * Input
4346  * 
4347  */
4348
4349 /**
4350  * @class Roo.bootstrap.Input
4351  * @extends Roo.bootstrap.Component
4352  * Bootstrap Input class
4353  * @cfg {Boolean} disabled is it disabled
4354  * @cfg {String} fieldLabel - the label associated
4355  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4356  * @cfg {String} name name of the input
4357  * @cfg {string} fieldLabel - the label associated
4358  * @cfg {string}  inputType - input / file submit ...
4359  * @cfg {string} placeholder - placeholder to put in text.
4360  * @cfg {string}  before - input group add on before
4361  * @cfg {string} after - input group add on after
4362  * @cfg {string} size - (lg|sm) or leave empty..
4363  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4364  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4365  * @cfg {Number} md colspan out of 12 for computer-sized screens
4366  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4367  * @cfg {string} value default value of the input
4368  * @cfg {Number} labelWidth set the width of label (0-12)
4369  * @cfg {String} labelAlign (top|left)
4370  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4371  * 
4372  * 
4373  * @constructor
4374  * Create a new Input
4375  * @param {Object} config The config object
4376  */
4377
4378 Roo.bootstrap.Input = function(config){
4379     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4380    
4381         this.addEvents({
4382             /**
4383              * @event focus
4384              * Fires when this field receives input focus.
4385              * @param {Roo.form.Field} this
4386              */
4387             focus : true,
4388             /**
4389              * @event blur
4390              * Fires when this field loses input focus.
4391              * @param {Roo.form.Field} this
4392              */
4393             blur : true,
4394             /**
4395              * @event specialkey
4396              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4397              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4398              * @param {Roo.form.Field} this
4399              * @param {Roo.EventObject} e The event object
4400              */
4401             specialkey : true,
4402             /**
4403              * @event change
4404              * Fires just before the field blurs if the field value has changed.
4405              * @param {Roo.form.Field} this
4406              * @param {Mixed} newValue The new value
4407              * @param {Mixed} oldValue The original value
4408              */
4409             change : true,
4410             /**
4411              * @event invalid
4412              * Fires after the field has been marked as invalid.
4413              * @param {Roo.form.Field} this
4414              * @param {String} msg The validation message
4415              */
4416             invalid : true,
4417             /**
4418              * @event valid
4419              * Fires after the field has been validated with no errors.
4420              * @param {Roo.form.Field} this
4421              */
4422             valid : true,
4423              /**
4424              * @event keyup
4425              * Fires after the key up
4426              * @param {Roo.form.Field} this
4427              * @param {Roo.EventObject}  e The event Object
4428              */
4429             keyup : true
4430         });
4431 };
4432
4433 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4434      /**
4435      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4436       automatic validation (defaults to "keyup").
4437      */
4438     validationEvent : "keyup",
4439      /**
4440      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4441      */
4442     validateOnBlur : true,
4443     /**
4444      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4445      */
4446     validationDelay : 250,
4447      /**
4448      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4449      */
4450     focusClass : "x-form-focus",  // not needed???
4451     
4452        
4453     /**
4454      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4455      */
4456     invalidClass : "has-error",
4457     
4458     /**
4459      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4460      */
4461     selectOnFocus : false,
4462     
4463      /**
4464      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4465      */
4466     maskRe : null,
4467        /**
4468      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4469      */
4470     vtype : null,
4471     
4472       /**
4473      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4474      */
4475     disableKeyFilter : false,
4476     
4477        /**
4478      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4479      */
4480     disabled : false,
4481      /**
4482      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4483      */
4484     allowBlank : true,
4485     /**
4486      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4487      */
4488     blankText : "This field is required",
4489     
4490      /**
4491      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4492      */
4493     minLength : 0,
4494     /**
4495      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4496      */
4497     maxLength : Number.MAX_VALUE,
4498     /**
4499      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4500      */
4501     minLengthText : "The minimum length for this field is {0}",
4502     /**
4503      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4504      */
4505     maxLengthText : "The maximum length for this field is {0}",
4506   
4507     
4508     /**
4509      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4510      * If available, this function will be called only after the basic validators all return true, and will be passed the
4511      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4512      */
4513     validator : null,
4514     /**
4515      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4516      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4517      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4518      */
4519     regex : null,
4520     /**
4521      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4522      */
4523     regexText : "",
4524     
4525     
4526     
4527     fieldLabel : '',
4528     inputType : 'text',
4529     
4530     name : false,
4531     placeholder: false,
4532     before : false,
4533     after : false,
4534     size : false,
4535     // private
4536     hasFocus : false,
4537     preventMark: false,
4538     isFormField : true,
4539     value : '',
4540     labelWidth : 2,
4541     labelAlign : false,
4542     readOnly : false,
4543     
4544     parentLabelAlign : function()
4545     {
4546         var parent = this;
4547         while (parent.parent()) {
4548             parent = parent.parent();
4549             if (typeof(parent.labelAlign) !='undefined') {
4550                 return parent.labelAlign;
4551             }
4552         }
4553         return 'left';
4554         
4555     },
4556     
4557     getAutoCreate : function(){
4558         
4559         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4560         
4561         var id = Roo.id();
4562         
4563         var cfg = {};
4564         
4565         if(this.inputType != 'hidden'){
4566             cfg.cls = 'form-group' //input-group
4567         }
4568         
4569         var input =  {
4570             tag: 'input',
4571             id : id,
4572             type : this.inputType,
4573             value : this.value,
4574             cls : 'form-control',
4575             placeholder : this.placeholder || ''
4576             
4577         };
4578         
4579         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4580             input.maxLength = this.maxLength;
4581         }
4582         
4583         if (this.disabled) {
4584             input.disabled=true;
4585         }
4586         
4587         if (this.readOnly) {
4588             input.readonly=true;
4589         }
4590         
4591         if (this.name) {
4592             input.name = this.name;
4593         }
4594         if (this.size) {
4595             input.cls += ' input-' + this.size;
4596         }
4597         var settings=this;
4598         ['xs','sm','md','lg'].map(function(size){
4599             if (settings[size]) {
4600                 cfg.cls += ' col-' + size + '-' + settings[size];
4601             }
4602         });
4603         
4604         var inputblock = input;
4605         
4606         if (this.before || this.after) {
4607             
4608             inputblock = {
4609                 cls : 'input-group',
4610                 cn :  [] 
4611             };
4612             if (this.before) {
4613                 inputblock.cn.push({
4614                     tag :'span',
4615                     cls : 'input-group-addon',
4616                     html : this.before
4617                 });
4618             }
4619             inputblock.cn.push(input);
4620             if (this.after) {
4621                 inputblock.cn.push({
4622                     tag :'span',
4623                     cls : 'input-group-addon',
4624                     html : this.after
4625                 });
4626             }
4627             
4628         };
4629         
4630         if (align ==='left' && this.fieldLabel.length) {
4631                 Roo.log("left and has label");
4632                 cfg.cn = [
4633                     
4634                     {
4635                         tag: 'label',
4636                         'for' :  id,
4637                         cls : 'control-label col-sm-' + this.labelWidth,
4638                         html : this.fieldLabel
4639                         
4640                     },
4641                     {
4642                         cls : "col-sm-" + (12 - this.labelWidth), 
4643                         cn: [
4644                             inputblock
4645                         ]
4646                     }
4647                     
4648                 ];
4649         } else if ( this.fieldLabel.length) {
4650                 Roo.log(" label");
4651                  cfg.cn = [
4652                    
4653                     {
4654                         tag: 'label',
4655                         //cls : 'input-group-addon',
4656                         html : this.fieldLabel
4657                         
4658                     },
4659                     
4660                     inputblock
4661                     
4662                 ];
4663
4664         } else {
4665             
4666                 Roo.log(" no label && no align");
4667                 cfg.cn = [
4668                     
4669                         inputblock
4670                     
4671                 ];
4672                 
4673                 
4674         };
4675         Roo.log('input-parentType: ' + this.parentType);
4676         
4677         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4678            cfg.cls += ' navbar-form';
4679            Roo.log(cfg);
4680         }
4681         
4682         return cfg;
4683         
4684     },
4685     /**
4686      * return the real input element.
4687      */
4688     inputEl: function ()
4689     {
4690         return this.el.select('input.form-control',true).first();
4691     },
4692     setDisabled : function(v)
4693     {
4694         var i  = this.inputEl().dom;
4695         if (!v) {
4696             i.removeAttribute('disabled');
4697             return;
4698             
4699         }
4700         i.setAttribute('disabled','true');
4701     },
4702     initEvents : function()
4703     {
4704         
4705         this.inputEl().on("keydown" , this.fireKey,  this);
4706         this.inputEl().on("focus", this.onFocus,  this);
4707         this.inputEl().on("blur", this.onBlur,  this);
4708         
4709         this.inputEl().relayEvent('keyup', this);
4710
4711         // reference to original value for reset
4712         this.originalValue = this.getValue();
4713         //Roo.form.TextField.superclass.initEvents.call(this);
4714         if(this.validationEvent == 'keyup'){
4715             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4716             this.inputEl().on('keyup', this.filterValidation, this);
4717         }
4718         else if(this.validationEvent !== false){
4719             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4720         }
4721         
4722         if(this.selectOnFocus){
4723             this.on("focus", this.preFocus, this);
4724             
4725         }
4726         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4727             this.inputEl().on("keypress", this.filterKeys, this);
4728         }
4729        /* if(this.grow){
4730             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4731             this.el.on("click", this.autoSize,  this);
4732         }
4733         */
4734         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4735             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4736         }
4737         
4738     },
4739     filterValidation : function(e){
4740         if(!e.isNavKeyPress()){
4741             this.validationTask.delay(this.validationDelay);
4742         }
4743     },
4744      /**
4745      * Validates the field value
4746      * @return {Boolean} True if the value is valid, else false
4747      */
4748     validate : function(){
4749         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4750         if(this.disabled || this.validateValue(this.getRawValue())){
4751             this.clearInvalid();
4752             return true;
4753         }
4754         return false;
4755     },
4756     
4757     
4758     /**
4759      * Validates a value according to the field's validation rules and marks the field as invalid
4760      * if the validation fails
4761      * @param {Mixed} value The value to validate
4762      * @return {Boolean} True if the value is valid, else false
4763      */
4764     validateValue : function(value){
4765         if(value.length < 1)  { // if it's blank
4766              if(this.allowBlank){
4767                 this.clearInvalid();
4768                 return true;
4769              }else{
4770                 this.markInvalid(this.blankText);
4771                 return false;
4772              }
4773         }
4774         if(value.length < this.minLength){
4775             this.markInvalid(String.format(this.minLengthText, this.minLength));
4776             return false;
4777         }
4778         if(value.length > this.maxLength){
4779             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4780             return false;
4781         }
4782         if(this.vtype){
4783             var vt = Roo.form.VTypes;
4784             if(!vt[this.vtype](value, this)){
4785                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4786                 return false;
4787             }
4788         }
4789         if(typeof this.validator == "function"){
4790             var msg = this.validator(value);
4791             if(msg !== true){
4792                 this.markInvalid(msg);
4793                 return false;
4794             }
4795         }
4796         if(this.regex && !this.regex.test(value)){
4797             this.markInvalid(this.regexText);
4798             return false;
4799         }
4800         return true;
4801     },
4802
4803     
4804     
4805      // private
4806     fireKey : function(e){
4807         //Roo.log('field ' + e.getKey());
4808         if(e.isNavKeyPress()){
4809             this.fireEvent("specialkey", this, e);
4810         }
4811     },
4812     focus : function (selectText){
4813         if(this.rendered){
4814             this.inputEl().focus();
4815             if(selectText === true){
4816                 this.inputEl().dom.select();
4817             }
4818         }
4819         return this;
4820     } ,
4821     
4822     onFocus : function(){
4823         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4824            // this.el.addClass(this.focusClass);
4825         }
4826         if(!this.hasFocus){
4827             this.hasFocus = true;
4828             this.startValue = this.getValue();
4829             this.fireEvent("focus", this);
4830         }
4831     },
4832     
4833     beforeBlur : Roo.emptyFn,
4834
4835     
4836     // private
4837     onBlur : function(){
4838         this.beforeBlur();
4839         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4840             //this.el.removeClass(this.focusClass);
4841         }
4842         this.hasFocus = false;
4843         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4844             this.validate();
4845         }
4846         var v = this.getValue();
4847         if(String(v) !== String(this.startValue)){
4848             this.fireEvent('change', this, v, this.startValue);
4849         }
4850         this.fireEvent("blur", this);
4851     },
4852     
4853     /**
4854      * Resets the current field value to the originally loaded value and clears any validation messages
4855      */
4856     reset : function(){
4857         this.setValue(this.originalValue);
4858         this.clearInvalid();
4859     },
4860      /**
4861      * Returns the name of the field
4862      * @return {Mixed} name The name field
4863      */
4864     getName: function(){
4865         return this.name;
4866     },
4867      /**
4868      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4869      * @return {Mixed} value The field value
4870      */
4871     getValue : function(){
4872         return this.inputEl().getValue();
4873     },
4874     /**
4875      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4876      * @return {Mixed} value The field value
4877      */
4878     getRawValue : function(){
4879         var v = this.inputEl().getValue();
4880         
4881         return v;
4882     },
4883     
4884     /**
4885      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4886      * @param {Mixed} value The value to set
4887      */
4888     setRawValue : function(v){
4889         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4890     },
4891     
4892     selectText : function(start, end){
4893         var v = this.getRawValue();
4894         if(v.length > 0){
4895             start = start === undefined ? 0 : start;
4896             end = end === undefined ? v.length : end;
4897             var d = this.inputEl().dom;
4898             if(d.setSelectionRange){
4899                 d.setSelectionRange(start, end);
4900             }else if(d.createTextRange){
4901                 var range = d.createTextRange();
4902                 range.moveStart("character", start);
4903                 range.moveEnd("character", v.length-end);
4904                 range.select();
4905             }
4906         }
4907     },
4908     
4909     /**
4910      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4911      * @param {Mixed} value The value to set
4912      */
4913     setValue : function(v){
4914         this.value = v;
4915         if(this.rendered){
4916             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4917             this.validate();
4918         }
4919     },
4920     
4921     /*
4922     processValue : function(value){
4923         if(this.stripCharsRe){
4924             var newValue = value.replace(this.stripCharsRe, '');
4925             if(newValue !== value){
4926                 this.setRawValue(newValue);
4927                 return newValue;
4928             }
4929         }
4930         return value;
4931     },
4932   */
4933     preFocus : function(){
4934         
4935         if(this.selectOnFocus){
4936             this.inputEl().dom.select();
4937         }
4938     },
4939     filterKeys : function(e){
4940         var k = e.getKey();
4941         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4942             return;
4943         }
4944         var c = e.getCharCode(), cc = String.fromCharCode(c);
4945         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4946             return;
4947         }
4948         if(!this.maskRe.test(cc)){
4949             e.stopEvent();
4950         }
4951     },
4952      /**
4953      * Clear any invalid styles/messages for this field
4954      */
4955     clearInvalid : function(){
4956         
4957         if(!this.el || this.preventMark){ // not rendered
4958             return;
4959         }
4960         this.el.removeClass(this.invalidClass);
4961         /*
4962         switch(this.msgTarget){
4963             case 'qtip':
4964                 this.el.dom.qtip = '';
4965                 break;
4966             case 'title':
4967                 this.el.dom.title = '';
4968                 break;
4969             case 'under':
4970                 if(this.errorEl){
4971                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4972                 }
4973                 break;
4974             case 'side':
4975                 if(this.errorIcon){
4976                     this.errorIcon.dom.qtip = '';
4977                     this.errorIcon.hide();
4978                     this.un('resize', this.alignErrorIcon, this);
4979                 }
4980                 break;
4981             default:
4982                 var t = Roo.getDom(this.msgTarget);
4983                 t.innerHTML = '';
4984                 t.style.display = 'none';
4985                 break;
4986         }
4987         */
4988         this.fireEvent('valid', this);
4989     },
4990      /**
4991      * Mark this field as invalid
4992      * @param {String} msg The validation message
4993      */
4994     markInvalid : function(msg){
4995         if(!this.el  || this.preventMark){ // not rendered
4996             return;
4997         }
4998         this.el.addClass(this.invalidClass);
4999         /*
5000         msg = msg || this.invalidText;
5001         switch(this.msgTarget){
5002             case 'qtip':
5003                 this.el.dom.qtip = msg;
5004                 this.el.dom.qclass = 'x-form-invalid-tip';
5005                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5006                     Roo.QuickTips.enable();
5007                 }
5008                 break;
5009             case 'title':
5010                 this.el.dom.title = msg;
5011                 break;
5012             case 'under':
5013                 if(!this.errorEl){
5014                     var elp = this.el.findParent('.x-form-element', 5, true);
5015                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5016                     this.errorEl.setWidth(elp.getWidth(true)-20);
5017                 }
5018                 this.errorEl.update(msg);
5019                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5020                 break;
5021             case 'side':
5022                 if(!this.errorIcon){
5023                     var elp = this.el.findParent('.x-form-element', 5, true);
5024                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5025                 }
5026                 this.alignErrorIcon();
5027                 this.errorIcon.dom.qtip = msg;
5028                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5029                 this.errorIcon.show();
5030                 this.on('resize', this.alignErrorIcon, this);
5031                 break;
5032             default:
5033                 var t = Roo.getDom(this.msgTarget);
5034                 t.innerHTML = msg;
5035                 t.style.display = this.msgDisplay;
5036                 break;
5037         }
5038         */
5039         this.fireEvent('invalid', this, msg);
5040     },
5041     // private
5042     SafariOnKeyDown : function(event)
5043     {
5044         // this is a workaround for a password hang bug on chrome/ webkit.
5045         
5046         var isSelectAll = false;
5047         
5048         if(this.inputEl().dom.selectionEnd > 0){
5049             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5050         }
5051         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5052             event.preventDefault();
5053             this.setValue('');
5054             return;
5055         }
5056         
5057         if(isSelectAll){ // backspace and delete key
5058             
5059             event.preventDefault();
5060             // this is very hacky as keydown always get's upper case.
5061             //
5062             var cc = String.fromCharCode(event.getCharCode());
5063             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5064             
5065         }
5066     },
5067     adjustWidth : function(tag, w){
5068         tag = tag.toLowerCase();
5069         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5070             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5071                 if(tag == 'input'){
5072                     return w + 2;
5073                 }
5074                 if(tag == 'textarea'){
5075                     return w-2;
5076                 }
5077             }else if(Roo.isOpera){
5078                 if(tag == 'input'){
5079                     return w + 2;
5080                 }
5081                 if(tag == 'textarea'){
5082                     return w-2;
5083                 }
5084             }
5085         }
5086         return w;
5087     }
5088     
5089 });
5090
5091  
5092 /*
5093  * - LGPL
5094  *
5095  * Input
5096  * 
5097  */
5098
5099 /**
5100  * @class Roo.bootstrap.TextArea
5101  * @extends Roo.bootstrap.Input
5102  * Bootstrap TextArea class
5103  * @cfg {Number} cols Specifies the visible width of a text area
5104  * @cfg {Number} rows Specifies the visible number of lines in a text area
5105  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5106  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5107  * @cfg {string} html text
5108  * 
5109  * @constructor
5110  * Create a new TextArea
5111  * @param {Object} config The config object
5112  */
5113
5114 Roo.bootstrap.TextArea = function(config){
5115     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5116    
5117 };
5118
5119 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5120      
5121     cols : false,
5122     rows : 5,
5123     readOnly : false,
5124     warp : 'soft',
5125     resize : false,
5126     value: false,
5127     html: false,
5128     
5129     getAutoCreate : function(){
5130         
5131         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5132         
5133         var id = Roo.id();
5134         
5135         var cfg = {};
5136         
5137         var input =  {
5138             tag: 'textarea',
5139             id : id,
5140             warp : this.warp,
5141             rows : this.rows,
5142             value : this.value || '',
5143             html: this.html || '',
5144             cls : 'form-control',
5145             placeholder : this.placeholder || '' 
5146             
5147         };
5148         
5149         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5150             input.maxLength = this.maxLength;
5151         }
5152         
5153         if(this.resize){
5154             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5155         }
5156         
5157         if(this.cols){
5158             input.cols = this.cols;
5159         }
5160         
5161         if (this.readOnly) {
5162             input.readonly = true;
5163         }
5164         
5165         if (this.name) {
5166             input.name = this.name;
5167         }
5168         
5169         if (this.size) {
5170             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5171         }
5172         
5173         var settings=this;
5174         ['xs','sm','md','lg'].map(function(size){
5175             if (settings[size]) {
5176                 cfg.cls += ' col-' + size + '-' + settings[size];
5177             }
5178         });
5179         
5180         var inputblock = input;
5181         
5182         if (this.before || this.after) {
5183             
5184             inputblock = {
5185                 cls : 'input-group',
5186                 cn :  [] 
5187             };
5188             if (this.before) {
5189                 inputblock.cn.push({
5190                     tag :'span',
5191                     cls : 'input-group-addon',
5192                     html : this.before
5193                 });
5194             }
5195             inputblock.cn.push(input);
5196             if (this.after) {
5197                 inputblock.cn.push({
5198                     tag :'span',
5199                     cls : 'input-group-addon',
5200                     html : this.after
5201                 });
5202             }
5203             
5204         }
5205         
5206         if (align ==='left' && this.fieldLabel.length) {
5207                 Roo.log("left and has label");
5208                 cfg.cn = [
5209                     
5210                     {
5211                         tag: 'label',
5212                         'for' :  id,
5213                         cls : 'control-label col-sm-' + this.labelWidth,
5214                         html : this.fieldLabel
5215                         
5216                     },
5217                     {
5218                         cls : "col-sm-" + (12 - this.labelWidth), 
5219                         cn: [
5220                             inputblock
5221                         ]
5222                     }
5223                     
5224                 ];
5225         } else if ( this.fieldLabel.length) {
5226                 Roo.log(" label");
5227                  cfg.cn = [
5228                    
5229                     {
5230                         tag: 'label',
5231                         //cls : 'input-group-addon',
5232                         html : this.fieldLabel
5233                         
5234                     },
5235                     
5236                     inputblock
5237                     
5238                 ];
5239
5240         } else {
5241             
5242                    Roo.log(" no label && no align");
5243                 cfg.cn = [
5244                     
5245                         inputblock
5246                     
5247                 ];
5248                 
5249                 
5250         }
5251         
5252         if (this.disabled) {
5253             input.disabled=true;
5254         }
5255         
5256         return cfg;
5257         
5258     },
5259     /**
5260      * return the real textarea element.
5261      */
5262     inputEl: function ()
5263     {
5264         return this.el.select('textarea.form-control',true).first();
5265     }
5266 });
5267
5268  
5269 /*
5270  * - LGPL
5271  *
5272  * trigger field - base class for combo..
5273  * 
5274  */
5275  
5276 /**
5277  * @class Roo.bootstrap.TriggerField
5278  * @extends Roo.bootstrap.Input
5279  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5280  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5281  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5282  * for which you can provide a custom implementation.  For example:
5283  * <pre><code>
5284 var trigger = new Roo.bootstrap.TriggerField();
5285 trigger.onTriggerClick = myTriggerFn;
5286 trigger.applyTo('my-field');
5287 </code></pre>
5288  *
5289  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5290  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5291  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5292  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5293  * @constructor
5294  * Create a new TriggerField.
5295  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5296  * to the base TextField)
5297  */
5298 Roo.bootstrap.TriggerField = function(config){
5299     this.mimicing = false;
5300     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5301 };
5302
5303 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5304     /**
5305      * @cfg {String} triggerClass A CSS class to apply to the trigger
5306      */
5307      /**
5308      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5309      */
5310     hideTrigger:false,
5311
5312     /** @cfg {Boolean} grow @hide */
5313     /** @cfg {Number} growMin @hide */
5314     /** @cfg {Number} growMax @hide */
5315
5316     /**
5317      * @hide 
5318      * @method
5319      */
5320     autoSize: Roo.emptyFn,
5321     // private
5322     monitorTab : true,
5323     // private
5324     deferHeight : true,
5325
5326     
5327     actionMode : 'wrap',
5328     
5329     
5330     
5331     getAutoCreate : function(){
5332        
5333         var parent = this.parent();
5334         
5335         var align = this.parentLabelAlign();
5336         
5337         var id = Roo.id();
5338         
5339         var cfg = {
5340             cls: 'form-group' //input-group
5341         };
5342         
5343         
5344         var input =  {
5345             tag: 'input',
5346             id : id,
5347             type : this.inputType,
5348             cls : 'form-control',
5349             autocomplete: 'off',
5350             placeholder : this.placeholder || '' 
5351             
5352         };
5353         if (this.name) {
5354             input.name = this.name;
5355         }
5356         if (this.size) {
5357             input.cls += ' input-' + this.size;
5358         }
5359         
5360         if (this.disabled) {
5361             input.disabled=true;
5362         }
5363         
5364         var inputblock = input;
5365         
5366         if (this.before || this.after) {
5367             
5368             inputblock = {
5369                 cls : 'input-group',
5370                 cn :  [] 
5371             };
5372             if (this.before) {
5373                 inputblock.cn.push({
5374                     tag :'span',
5375                     cls : 'input-group-addon',
5376                     html : this.before
5377                 });
5378             }
5379             inputblock.cn.push(input);
5380             if (this.after) {
5381                 inputblock.cn.push({
5382                     tag :'span',
5383                     cls : 'input-group-addon',
5384                     html : this.after
5385                 });
5386             }
5387             
5388         };
5389         
5390         var box = {
5391             tag: 'div',
5392             cn: [
5393                 {
5394                     tag: 'input',
5395                     type : 'hidden',
5396                     cls: 'form-hidden-field'
5397                 },
5398                 inputblock
5399             ]
5400             
5401         };
5402         
5403         if(this.multiple){
5404             Roo.log('multiple');
5405             
5406             box = {
5407                 tag: 'div',
5408                 cn: [
5409                     {
5410                         tag: 'input',
5411                         type : 'hidden',
5412                         cls: 'form-hidden-field'
5413                     },
5414                     {
5415                         tag: 'ul',
5416                         cls: 'select2-choices',
5417                         cn:[
5418                             {
5419                                 tag: 'li',
5420                                 cls: 'select2-search-field',
5421                                 cn: [
5422
5423                                     inputblock
5424                                 ]
5425                             }
5426                         ]
5427                     }
5428                 ]
5429             }
5430         };
5431         
5432         var combobox = {
5433             cls: 'select2-container input-group',
5434             cn: [
5435                 box,
5436                 {
5437                     tag: 'ul',
5438                     cls: 'typeahead typeahead-long dropdown-menu',
5439                     style: 'display:none'
5440                 }
5441             ]
5442         };
5443         
5444         if(!this.multiple){
5445             combobox.cn.push({
5446                 tag :'span',
5447                 cls : 'input-group-addon btn dropdown-toggle',
5448                 cn : [
5449                     {
5450                         tag: 'span',
5451                         cls: 'caret'
5452                     },
5453                     {
5454                         tag: 'span',
5455                         cls: 'combobox-clear',
5456                         cn  : [
5457                             {
5458                                 tag : 'i',
5459                                 cls: 'icon-remove'
5460                             }
5461                         ]
5462                     }
5463                 ]
5464
5465             })
5466         }
5467         
5468         if(this.multiple){
5469             combobox.cls += ' select2-container-multi';
5470         }
5471         
5472         if (align ==='left' && this.fieldLabel.length) {
5473             
5474                 Roo.log("left and has label");
5475                 cfg.cn = [
5476                     
5477                     {
5478                         tag: 'label',
5479                         'for' :  id,
5480                         cls : 'control-label col-sm-' + this.labelWidth,
5481                         html : this.fieldLabel
5482                         
5483                     },
5484                     {
5485                         cls : "col-sm-" + (12 - this.labelWidth), 
5486                         cn: [
5487                             combobox
5488                         ]
5489                     }
5490                     
5491                 ];
5492         } else if ( this.fieldLabel.length) {
5493                 Roo.log(" label");
5494                  cfg.cn = [
5495                    
5496                     {
5497                         tag: 'label',
5498                         //cls : 'input-group-addon',
5499                         html : this.fieldLabel
5500                         
5501                     },
5502                     
5503                     combobox
5504                     
5505                 ];
5506
5507         } else {
5508             
5509                 Roo.log(" no label && no align");
5510                 cfg = combobox
5511                      
5512                 
5513         }
5514          
5515         var settings=this;
5516         ['xs','sm','md','lg'].map(function(size){
5517             if (settings[size]) {
5518                 cfg.cls += ' col-' + size + '-' + settings[size];
5519             }
5520         });
5521         
5522         return cfg;
5523         
5524     },
5525     
5526     
5527     
5528     // private
5529     onResize : function(w, h){
5530 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5531 //        if(typeof w == 'number'){
5532 //            var x = w - this.trigger.getWidth();
5533 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5534 //            this.trigger.setStyle('left', x+'px');
5535 //        }
5536     },
5537
5538     // private
5539     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5540
5541     // private
5542     getResizeEl : function(){
5543         return this.inputEl();
5544     },
5545
5546     // private
5547     getPositionEl : function(){
5548         return this.inputEl();
5549     },
5550
5551     // private
5552     alignErrorIcon : function(){
5553         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5554     },
5555
5556     // private
5557     initEvents : function(){
5558         
5559         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5560         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5561         if(!this.multiple){
5562             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5563             if(this.hideTrigger){
5564                 this.trigger.setDisplayed(false);
5565             }
5566             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5567         }
5568         
5569         if(this.multiple){
5570             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5571         }
5572         
5573         //this.trigger.addClassOnOver('x-form-trigger-over');
5574         //this.trigger.addClassOnClick('x-form-trigger-click');
5575         
5576         //if(!this.width){
5577         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5578         //}
5579     },
5580
5581     // private
5582     initTrigger : function(){
5583        
5584     },
5585
5586     // private
5587     onDestroy : function(){
5588         if(this.trigger){
5589             this.trigger.removeAllListeners();
5590           //  this.trigger.remove();
5591         }
5592         //if(this.wrap){
5593         //    this.wrap.remove();
5594         //}
5595         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5596     },
5597
5598     // private
5599     onFocus : function(){
5600         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5601         /*
5602         if(!this.mimicing){
5603             this.wrap.addClass('x-trigger-wrap-focus');
5604             this.mimicing = true;
5605             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5606             if(this.monitorTab){
5607                 this.el.on("keydown", this.checkTab, this);
5608             }
5609         }
5610         */
5611     },
5612
5613     // private
5614     checkTab : function(e){
5615         if(e.getKey() == e.TAB){
5616             this.triggerBlur();
5617         }
5618     },
5619
5620     // private
5621     onBlur : function(){
5622         // do nothing
5623     },
5624
5625     // private
5626     mimicBlur : function(e, t){
5627         /*
5628         if(!this.wrap.contains(t) && this.validateBlur()){
5629             this.triggerBlur();
5630         }
5631         */
5632     },
5633
5634     // private
5635     triggerBlur : function(){
5636         this.mimicing = false;
5637         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5638         if(this.monitorTab){
5639             this.el.un("keydown", this.checkTab, this);
5640         }
5641         //this.wrap.removeClass('x-trigger-wrap-focus');
5642         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5643     },
5644
5645     // private
5646     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5647     validateBlur : function(e, t){
5648         return true;
5649     },
5650
5651     // private
5652     onDisable : function(){
5653         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5654         //if(this.wrap){
5655         //    this.wrap.addClass('x-item-disabled');
5656         //}
5657     },
5658
5659     // private
5660     onEnable : function(){
5661         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5662         //if(this.wrap){
5663         //    this.el.removeClass('x-item-disabled');
5664         //}
5665     },
5666
5667     // private
5668     onShow : function(){
5669         var ae = this.getActionEl();
5670         
5671         if(ae){
5672             ae.dom.style.display = '';
5673             ae.dom.style.visibility = 'visible';
5674         }
5675     },
5676
5677     // private
5678     
5679     onHide : function(){
5680         var ae = this.getActionEl();
5681         ae.dom.style.display = 'none';
5682     },
5683
5684     /**
5685      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5686      * by an implementing function.
5687      * @method
5688      * @param {EventObject} e
5689      */
5690     onTriggerClick : Roo.emptyFn
5691 });
5692  /*
5693  * Based on:
5694  * Ext JS Library 1.1.1
5695  * Copyright(c) 2006-2007, Ext JS, LLC.
5696  *
5697  * Originally Released Under LGPL - original licence link has changed is not relivant.
5698  *
5699  * Fork - LGPL
5700  * <script type="text/javascript">
5701  */
5702
5703
5704 /**
5705  * @class Roo.data.SortTypes
5706  * @singleton
5707  * Defines the default sorting (casting?) comparison functions used when sorting data.
5708  */
5709 Roo.data.SortTypes = {
5710     /**
5711      * Default sort that does nothing
5712      * @param {Mixed} s The value being converted
5713      * @return {Mixed} The comparison value
5714      */
5715     none : function(s){
5716         return s;
5717     },
5718     
5719     /**
5720      * The regular expression used to strip tags
5721      * @type {RegExp}
5722      * @property
5723      */
5724     stripTagsRE : /<\/?[^>]+>/gi,
5725     
5726     /**
5727      * Strips all HTML tags to sort on text only
5728      * @param {Mixed} s The value being converted
5729      * @return {String} The comparison value
5730      */
5731     asText : function(s){
5732         return String(s).replace(this.stripTagsRE, "");
5733     },
5734     
5735     /**
5736      * Strips all HTML tags to sort on text only - Case insensitive
5737      * @param {Mixed} s The value being converted
5738      * @return {String} The comparison value
5739      */
5740     asUCText : function(s){
5741         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5742     },
5743     
5744     /**
5745      * Case insensitive string
5746      * @param {Mixed} s The value being converted
5747      * @return {String} The comparison value
5748      */
5749     asUCString : function(s) {
5750         return String(s).toUpperCase();
5751     },
5752     
5753     /**
5754      * Date sorting
5755      * @param {Mixed} s The value being converted
5756      * @return {Number} The comparison value
5757      */
5758     asDate : function(s) {
5759         if(!s){
5760             return 0;
5761         }
5762         if(s instanceof Date){
5763             return s.getTime();
5764         }
5765         return Date.parse(String(s));
5766     },
5767     
5768     /**
5769      * Float sorting
5770      * @param {Mixed} s The value being converted
5771      * @return {Float} The comparison value
5772      */
5773     asFloat : function(s) {
5774         var val = parseFloat(String(s).replace(/,/g, ""));
5775         if(isNaN(val)) val = 0;
5776         return val;
5777     },
5778     
5779     /**
5780      * Integer sorting
5781      * @param {Mixed} s The value being converted
5782      * @return {Number} The comparison value
5783      */
5784     asInt : function(s) {
5785         var val = parseInt(String(s).replace(/,/g, ""));
5786         if(isNaN(val)) val = 0;
5787         return val;
5788     }
5789 };/*
5790  * Based on:
5791  * Ext JS Library 1.1.1
5792  * Copyright(c) 2006-2007, Ext JS, LLC.
5793  *
5794  * Originally Released Under LGPL - original licence link has changed is not relivant.
5795  *
5796  * Fork - LGPL
5797  * <script type="text/javascript">
5798  */
5799
5800 /**
5801 * @class Roo.data.Record
5802  * Instances of this class encapsulate both record <em>definition</em> information, and record
5803  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5804  * to access Records cached in an {@link Roo.data.Store} object.<br>
5805  * <p>
5806  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5807  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5808  * objects.<br>
5809  * <p>
5810  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5811  * @constructor
5812  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5813  * {@link #create}. The parameters are the same.
5814  * @param {Array} data An associative Array of data values keyed by the field name.
5815  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5816  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5817  * not specified an integer id is generated.
5818  */
5819 Roo.data.Record = function(data, id){
5820     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5821     this.data = data;
5822 };
5823
5824 /**
5825  * Generate a constructor for a specific record layout.
5826  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5827  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5828  * Each field definition object may contain the following properties: <ul>
5829  * <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,
5830  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5831  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5832  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5833  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5834  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5835  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5836  * this may be omitted.</p></li>
5837  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5838  * <ul><li>auto (Default, implies no conversion)</li>
5839  * <li>string</li>
5840  * <li>int</li>
5841  * <li>float</li>
5842  * <li>boolean</li>
5843  * <li>date</li></ul></p></li>
5844  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5845  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5846  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5847  * by the Reader into an object that will be stored in the Record. It is passed the
5848  * following parameters:<ul>
5849  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5850  * </ul></p></li>
5851  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5852  * </ul>
5853  * <br>usage:<br><pre><code>
5854 var TopicRecord = Roo.data.Record.create(
5855     {name: 'title', mapping: 'topic_title'},
5856     {name: 'author', mapping: 'username'},
5857     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5858     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5859     {name: 'lastPoster', mapping: 'user2'},
5860     {name: 'excerpt', mapping: 'post_text'}
5861 );
5862
5863 var myNewRecord = new TopicRecord({
5864     title: 'Do my job please',
5865     author: 'noobie',
5866     totalPosts: 1,
5867     lastPost: new Date(),
5868     lastPoster: 'Animal',
5869     excerpt: 'No way dude!'
5870 });
5871 myStore.add(myNewRecord);
5872 </code></pre>
5873  * @method create
5874  * @static
5875  */
5876 Roo.data.Record.create = function(o){
5877     var f = function(){
5878         f.superclass.constructor.apply(this, arguments);
5879     };
5880     Roo.extend(f, Roo.data.Record);
5881     var p = f.prototype;
5882     p.fields = new Roo.util.MixedCollection(false, function(field){
5883         return field.name;
5884     });
5885     for(var i = 0, len = o.length; i < len; i++){
5886         p.fields.add(new Roo.data.Field(o[i]));
5887     }
5888     f.getField = function(name){
5889         return p.fields.get(name);  
5890     };
5891     return f;
5892 };
5893
5894 Roo.data.Record.AUTO_ID = 1000;
5895 Roo.data.Record.EDIT = 'edit';
5896 Roo.data.Record.REJECT = 'reject';
5897 Roo.data.Record.COMMIT = 'commit';
5898
5899 Roo.data.Record.prototype = {
5900     /**
5901      * Readonly flag - true if this record has been modified.
5902      * @type Boolean
5903      */
5904     dirty : false,
5905     editing : false,
5906     error: null,
5907     modified: null,
5908
5909     // private
5910     join : function(store){
5911         this.store = store;
5912     },
5913
5914     /**
5915      * Set the named field to the specified value.
5916      * @param {String} name The name of the field to set.
5917      * @param {Object} value The value to set the field to.
5918      */
5919     set : function(name, value){
5920         if(this.data[name] == value){
5921             return;
5922         }
5923         this.dirty = true;
5924         if(!this.modified){
5925             this.modified = {};
5926         }
5927         if(typeof this.modified[name] == 'undefined'){
5928             this.modified[name] = this.data[name];
5929         }
5930         this.data[name] = value;
5931         if(!this.editing && this.store){
5932             this.store.afterEdit(this);
5933         }       
5934     },
5935
5936     /**
5937      * Get the value of the named field.
5938      * @param {String} name The name of the field to get the value of.
5939      * @return {Object} The value of the field.
5940      */
5941     get : function(name){
5942         return this.data[name]; 
5943     },
5944
5945     // private
5946     beginEdit : function(){
5947         this.editing = true;
5948         this.modified = {}; 
5949     },
5950
5951     // private
5952     cancelEdit : function(){
5953         this.editing = false;
5954         delete this.modified;
5955     },
5956
5957     // private
5958     endEdit : function(){
5959         this.editing = false;
5960         if(this.dirty && this.store){
5961             this.store.afterEdit(this);
5962         }
5963     },
5964
5965     /**
5966      * Usually called by the {@link Roo.data.Store} which owns the Record.
5967      * Rejects all changes made to the Record since either creation, or the last commit operation.
5968      * Modified fields are reverted to their original values.
5969      * <p>
5970      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5971      * of reject operations.
5972      */
5973     reject : function(){
5974         var m = this.modified;
5975         for(var n in m){
5976             if(typeof m[n] != "function"){
5977                 this.data[n] = m[n];
5978             }
5979         }
5980         this.dirty = false;
5981         delete this.modified;
5982         this.editing = false;
5983         if(this.store){
5984             this.store.afterReject(this);
5985         }
5986     },
5987
5988     /**
5989      * Usually called by the {@link Roo.data.Store} which owns the Record.
5990      * Commits all changes made to the Record since either creation, or the last commit operation.
5991      * <p>
5992      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5993      * of commit operations.
5994      */
5995     commit : function(){
5996         this.dirty = false;
5997         delete this.modified;
5998         this.editing = false;
5999         if(this.store){
6000             this.store.afterCommit(this);
6001         }
6002     },
6003
6004     // private
6005     hasError : function(){
6006         return this.error != null;
6007     },
6008
6009     // private
6010     clearError : function(){
6011         this.error = null;
6012     },
6013
6014     /**
6015      * Creates a copy of this record.
6016      * @param {String} id (optional) A new record id if you don't want to use this record's id
6017      * @return {Record}
6018      */
6019     copy : function(newId) {
6020         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6021     }
6022 };/*
6023  * Based on:
6024  * Ext JS Library 1.1.1
6025  * Copyright(c) 2006-2007, Ext JS, LLC.
6026  *
6027  * Originally Released Under LGPL - original licence link has changed is not relivant.
6028  *
6029  * Fork - LGPL
6030  * <script type="text/javascript">
6031  */
6032
6033
6034
6035 /**
6036  * @class Roo.data.Store
6037  * @extends Roo.util.Observable
6038  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6039  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6040  * <p>
6041  * 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
6042  * has no knowledge of the format of the data returned by the Proxy.<br>
6043  * <p>
6044  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6045  * instances from the data object. These records are cached and made available through accessor functions.
6046  * @constructor
6047  * Creates a new Store.
6048  * @param {Object} config A config object containing the objects needed for the Store to access data,
6049  * and read the data into Records.
6050  */
6051 Roo.data.Store = function(config){
6052     this.data = new Roo.util.MixedCollection(false);
6053     this.data.getKey = function(o){
6054         return o.id;
6055     };
6056     this.baseParams = {};
6057     // private
6058     this.paramNames = {
6059         "start" : "start",
6060         "limit" : "limit",
6061         "sort" : "sort",
6062         "dir" : "dir",
6063         "multisort" : "_multisort"
6064     };
6065
6066     if(config && config.data){
6067         this.inlineData = config.data;
6068         delete config.data;
6069     }
6070
6071     Roo.apply(this, config);
6072     
6073     if(this.reader){ // reader passed
6074         this.reader = Roo.factory(this.reader, Roo.data);
6075         this.reader.xmodule = this.xmodule || false;
6076         if(!this.recordType){
6077             this.recordType = this.reader.recordType;
6078         }
6079         if(this.reader.onMetaChange){
6080             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6081         }
6082     }
6083
6084     if(this.recordType){
6085         this.fields = this.recordType.prototype.fields;
6086     }
6087     this.modified = [];
6088
6089     this.addEvents({
6090         /**
6091          * @event datachanged
6092          * Fires when the data cache has changed, and a widget which is using this Store
6093          * as a Record cache should refresh its view.
6094          * @param {Store} this
6095          */
6096         datachanged : true,
6097         /**
6098          * @event metachange
6099          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6100          * @param {Store} this
6101          * @param {Object} meta The JSON metadata
6102          */
6103         metachange : true,
6104         /**
6105          * @event add
6106          * Fires when Records have been added to the Store
6107          * @param {Store} this
6108          * @param {Roo.data.Record[]} records The array of Records added
6109          * @param {Number} index The index at which the record(s) were added
6110          */
6111         add : true,
6112         /**
6113          * @event remove
6114          * Fires when a Record has been removed from the Store
6115          * @param {Store} this
6116          * @param {Roo.data.Record} record The Record that was removed
6117          * @param {Number} index The index at which the record was removed
6118          */
6119         remove : true,
6120         /**
6121          * @event update
6122          * Fires when a Record has been updated
6123          * @param {Store} this
6124          * @param {Roo.data.Record} record The Record that was updated
6125          * @param {String} operation The update operation being performed.  Value may be one of:
6126          * <pre><code>
6127  Roo.data.Record.EDIT
6128  Roo.data.Record.REJECT
6129  Roo.data.Record.COMMIT
6130          * </code></pre>
6131          */
6132         update : true,
6133         /**
6134          * @event clear
6135          * Fires when the data cache has been cleared.
6136          * @param {Store} this
6137          */
6138         clear : true,
6139         /**
6140          * @event beforeload
6141          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6142          * the load action will be canceled.
6143          * @param {Store} this
6144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6145          */
6146         beforeload : true,
6147         /**
6148          * @event beforeloadadd
6149          * Fires after a new set of Records has been loaded.
6150          * @param {Store} this
6151          * @param {Roo.data.Record[]} records The Records that were loaded
6152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6153          */
6154         beforeloadadd : true,
6155         /**
6156          * @event load
6157          * Fires after a new set of Records has been loaded, before they are added to the store.
6158          * @param {Store} this
6159          * @param {Roo.data.Record[]} records The Records that were loaded
6160          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6161          * @params {Object} return from reader
6162          */
6163         load : true,
6164         /**
6165          * @event loadexception
6166          * Fires if an exception occurs in the Proxy during loading.
6167          * Called with the signature of the Proxy's "loadexception" event.
6168          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6169          * 
6170          * @param {Proxy} 
6171          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6172          * @param {Object} load options 
6173          * @param {Object} jsonData from your request (normally this contains the Exception)
6174          */
6175         loadexception : true
6176     });
6177     
6178     if(this.proxy){
6179         this.proxy = Roo.factory(this.proxy, Roo.data);
6180         this.proxy.xmodule = this.xmodule || false;
6181         this.relayEvents(this.proxy,  ["loadexception"]);
6182     }
6183     this.sortToggle = {};
6184     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6185
6186     Roo.data.Store.superclass.constructor.call(this);
6187
6188     if(this.inlineData){
6189         this.loadData(this.inlineData);
6190         delete this.inlineData;
6191     }
6192 };
6193
6194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6195      /**
6196     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6197     * without a remote query - used by combo/forms at present.
6198     */
6199     
6200     /**
6201     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6202     */
6203     /**
6204     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6205     */
6206     /**
6207     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6208     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6209     */
6210     /**
6211     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6212     * on any HTTP request
6213     */
6214     /**
6215     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6216     */
6217     /**
6218     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6219     */
6220     multiSort: false,
6221     /**
6222     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6223     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6224     */
6225     remoteSort : false,
6226
6227     /**
6228     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6229      * loaded or when a record is removed. (defaults to false).
6230     */
6231     pruneModifiedRecords : false,
6232
6233     // private
6234     lastOptions : null,
6235
6236     /**
6237      * Add Records to the Store and fires the add event.
6238      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6239      */
6240     add : function(records){
6241         records = [].concat(records);
6242         for(var i = 0, len = records.length; i < len; i++){
6243             records[i].join(this);
6244         }
6245         var index = this.data.length;
6246         this.data.addAll(records);
6247         this.fireEvent("add", this, records, index);
6248     },
6249
6250     /**
6251      * Remove a Record from the Store and fires the remove event.
6252      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6253      */
6254     remove : function(record){
6255         var index = this.data.indexOf(record);
6256         this.data.removeAt(index);
6257         if(this.pruneModifiedRecords){
6258             this.modified.remove(record);
6259         }
6260         this.fireEvent("remove", this, record, index);
6261     },
6262
6263     /**
6264      * Remove all Records from the Store and fires the clear event.
6265      */
6266     removeAll : function(){
6267         this.data.clear();
6268         if(this.pruneModifiedRecords){
6269             this.modified = [];
6270         }
6271         this.fireEvent("clear", this);
6272     },
6273
6274     /**
6275      * Inserts Records to the Store at the given index and fires the add event.
6276      * @param {Number} index The start index at which to insert the passed Records.
6277      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6278      */
6279     insert : function(index, records){
6280         records = [].concat(records);
6281         for(var i = 0, len = records.length; i < len; i++){
6282             this.data.insert(index, records[i]);
6283             records[i].join(this);
6284         }
6285         this.fireEvent("add", this, records, index);
6286     },
6287
6288     /**
6289      * Get the index within the cache of the passed Record.
6290      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6291      * @return {Number} The index of the passed Record. Returns -1 if not found.
6292      */
6293     indexOf : function(record){
6294         return this.data.indexOf(record);
6295     },
6296
6297     /**
6298      * Get the index within the cache of the Record with the passed id.
6299      * @param {String} id The id of the Record to find.
6300      * @return {Number} The index of the Record. Returns -1 if not found.
6301      */
6302     indexOfId : function(id){
6303         return this.data.indexOfKey(id);
6304     },
6305
6306     /**
6307      * Get the Record with the specified id.
6308      * @param {String} id The id of the Record to find.
6309      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6310      */
6311     getById : function(id){
6312         return this.data.key(id);
6313     },
6314
6315     /**
6316      * Get the Record at the specified index.
6317      * @param {Number} index The index of the Record to find.
6318      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6319      */
6320     getAt : function(index){
6321         return this.data.itemAt(index);
6322     },
6323
6324     /**
6325      * Returns a range of Records between specified indices.
6326      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6327      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6328      * @return {Roo.data.Record[]} An array of Records
6329      */
6330     getRange : function(start, end){
6331         return this.data.getRange(start, end);
6332     },
6333
6334     // private
6335     storeOptions : function(o){
6336         o = Roo.apply({}, o);
6337         delete o.callback;
6338         delete o.scope;
6339         this.lastOptions = o;
6340     },
6341
6342     /**
6343      * Loads the Record cache from the configured Proxy using the configured Reader.
6344      * <p>
6345      * If using remote paging, then the first load call must specify the <em>start</em>
6346      * and <em>limit</em> properties in the options.params property to establish the initial
6347      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6348      * <p>
6349      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6350      * and this call will return before the new data has been loaded. Perform any post-processing
6351      * in a callback function, or in a "load" event handler.</strong>
6352      * <p>
6353      * @param {Object} options An object containing properties which control loading options:<ul>
6354      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6355      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6356      * passed the following arguments:<ul>
6357      * <li>r : Roo.data.Record[]</li>
6358      * <li>options: Options object from the load call</li>
6359      * <li>success: Boolean success indicator</li></ul></li>
6360      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6361      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6362      * </ul>
6363      */
6364     load : function(options){
6365         options = options || {};
6366         if(this.fireEvent("beforeload", this, options) !== false){
6367             this.storeOptions(options);
6368             var p = Roo.apply(options.params || {}, this.baseParams);
6369             // if meta was not loaded from remote source.. try requesting it.
6370             if (!this.reader.metaFromRemote) {
6371                 p._requestMeta = 1;
6372             }
6373             if(this.sortInfo && this.remoteSort){
6374                 var pn = this.paramNames;
6375                 p[pn["sort"]] = this.sortInfo.field;
6376                 p[pn["dir"]] = this.sortInfo.direction;
6377             }
6378             if (this.multiSort) {
6379                 var pn = this.paramNames;
6380                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6381             }
6382             
6383             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6384         }
6385     },
6386
6387     /**
6388      * Reloads the Record cache from the configured Proxy using the configured Reader and
6389      * the options from the last load operation performed.
6390      * @param {Object} options (optional) An object containing properties which may override the options
6391      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6392      * the most recently used options are reused).
6393      */
6394     reload : function(options){
6395         this.load(Roo.applyIf(options||{}, this.lastOptions));
6396     },
6397
6398     // private
6399     // Called as a callback by the Reader during a load operation.
6400     loadRecords : function(o, options, success){
6401         if(!o || success === false){
6402             if(success !== false){
6403                 this.fireEvent("load", this, [], options, o);
6404             }
6405             if(options.callback){
6406                 options.callback.call(options.scope || this, [], options, false);
6407             }
6408             return;
6409         }
6410         // if data returned failure - throw an exception.
6411         if (o.success === false) {
6412             // show a message if no listener is registered.
6413             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6414                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6415             }
6416             // loadmask wil be hooked into this..
6417             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6418             return;
6419         }
6420         var r = o.records, t = o.totalRecords || r.length;
6421         
6422         this.fireEvent("beforeloadadd", this, r, options, o);
6423         
6424         if(!options || options.add !== true){
6425             if(this.pruneModifiedRecords){
6426                 this.modified = [];
6427             }
6428             for(var i = 0, len = r.length; i < len; i++){
6429                 r[i].join(this);
6430             }
6431             if(this.snapshot){
6432                 this.data = this.snapshot;
6433                 delete this.snapshot;
6434             }
6435             this.data.clear();
6436             this.data.addAll(r);
6437             this.totalLength = t;
6438             this.applySort();
6439             this.fireEvent("datachanged", this);
6440         }else{
6441             this.totalLength = Math.max(t, this.data.length+r.length);
6442             this.add(r);
6443         }
6444         this.fireEvent("load", this, r, options, o);
6445         if(options.callback){
6446             options.callback.call(options.scope || this, r, options, true);
6447         }
6448     },
6449
6450
6451     /**
6452      * Loads data from a passed data block. A Reader which understands the format of the data
6453      * must have been configured in the constructor.
6454      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6455      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6456      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6457      */
6458     loadData : function(o, append){
6459         var r = this.reader.readRecords(o);
6460         this.loadRecords(r, {add: append}, true);
6461     },
6462
6463     /**
6464      * Gets the number of cached records.
6465      * <p>
6466      * <em>If using paging, this may not be the total size of the dataset. If the data object
6467      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6468      * the data set size</em>
6469      */
6470     getCount : function(){
6471         return this.data.length || 0;
6472     },
6473
6474     /**
6475      * Gets the total number of records in the dataset as returned by the server.
6476      * <p>
6477      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6478      * the dataset size</em>
6479      */
6480     getTotalCount : function(){
6481         return this.totalLength || 0;
6482     },
6483
6484     /**
6485      * Returns the sort state of the Store as an object with two properties:
6486      * <pre><code>
6487  field {String} The name of the field by which the Records are sorted
6488  direction {String} The sort order, "ASC" or "DESC"
6489      * </code></pre>
6490      */
6491     getSortState : function(){
6492         return this.sortInfo;
6493     },
6494
6495     // private
6496     applySort : function(){
6497         if(this.sortInfo && !this.remoteSort){
6498             var s = this.sortInfo, f = s.field;
6499             var st = this.fields.get(f).sortType;
6500             var fn = function(r1, r2){
6501                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6502                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6503             };
6504             this.data.sort(s.direction, fn);
6505             if(this.snapshot && this.snapshot != this.data){
6506                 this.snapshot.sort(s.direction, fn);
6507             }
6508         }
6509     },
6510
6511     /**
6512      * Sets the default sort column and order to be used by the next load operation.
6513      * @param {String} fieldName The name of the field to sort by.
6514      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6515      */
6516     setDefaultSort : function(field, dir){
6517         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6518     },
6519
6520     /**
6521      * Sort the Records.
6522      * If remote sorting is used, the sort is performed on the server, and the cache is
6523      * reloaded. If local sorting is used, the cache is sorted internally.
6524      * @param {String} fieldName The name of the field to sort by.
6525      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6526      */
6527     sort : function(fieldName, dir){
6528         var f = this.fields.get(fieldName);
6529         if(!dir){
6530             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6531             
6532             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6533                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6534             }else{
6535                 dir = f.sortDir;
6536             }
6537         }
6538         this.sortToggle[f.name] = dir;
6539         this.sortInfo = {field: f.name, direction: dir};
6540         if(!this.remoteSort){
6541             this.applySort();
6542             this.fireEvent("datachanged", this);
6543         }else{
6544             this.load(this.lastOptions);
6545         }
6546     },
6547
6548     /**
6549      * Calls the specified function for each of the Records in the cache.
6550      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6551      * Returning <em>false</em> aborts and exits the iteration.
6552      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6553      */
6554     each : function(fn, scope){
6555         this.data.each(fn, scope);
6556     },
6557
6558     /**
6559      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6560      * (e.g., during paging).
6561      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6562      */
6563     getModifiedRecords : function(){
6564         return this.modified;
6565     },
6566
6567     // private
6568     createFilterFn : function(property, value, anyMatch){
6569         if(!value.exec){ // not a regex
6570             value = String(value);
6571             if(value.length == 0){
6572                 return false;
6573             }
6574             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6575         }
6576         return function(r){
6577             return value.test(r.data[property]);
6578         };
6579     },
6580
6581     /**
6582      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6583      * @param {String} property A field on your records
6584      * @param {Number} start The record index to start at (defaults to 0)
6585      * @param {Number} end The last record index to include (defaults to length - 1)
6586      * @return {Number} The sum
6587      */
6588     sum : function(property, start, end){
6589         var rs = this.data.items, v = 0;
6590         start = start || 0;
6591         end = (end || end === 0) ? end : rs.length-1;
6592
6593         for(var i = start; i <= end; i++){
6594             v += (rs[i].data[property] || 0);
6595         }
6596         return v;
6597     },
6598
6599     /**
6600      * Filter the records by a specified property.
6601      * @param {String} field A field on your records
6602      * @param {String/RegExp} value Either a string that the field
6603      * should start with or a RegExp to test against the field
6604      * @param {Boolean} anyMatch True to match any part not just the beginning
6605      */
6606     filter : function(property, value, anyMatch){
6607         var fn = this.createFilterFn(property, value, anyMatch);
6608         return fn ? this.filterBy(fn) : this.clearFilter();
6609     },
6610
6611     /**
6612      * Filter by a function. The specified function will be called with each
6613      * record in this data source. If the function returns true the record is included,
6614      * otherwise it is filtered.
6615      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6616      * @param {Object} scope (optional) The scope of the function (defaults to this)
6617      */
6618     filterBy : function(fn, scope){
6619         this.snapshot = this.snapshot || this.data;
6620         this.data = this.queryBy(fn, scope||this);
6621         this.fireEvent("datachanged", this);
6622     },
6623
6624     /**
6625      * Query the records by a specified property.
6626      * @param {String} field A field on your records
6627      * @param {String/RegExp} value Either a string that the field
6628      * should start with or a RegExp to test against the field
6629      * @param {Boolean} anyMatch True to match any part not just the beginning
6630      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6631      */
6632     query : function(property, value, anyMatch){
6633         var fn = this.createFilterFn(property, value, anyMatch);
6634         return fn ? this.queryBy(fn) : this.data.clone();
6635     },
6636
6637     /**
6638      * Query by a function. The specified function will be called with each
6639      * record in this data source. If the function returns true the record is included
6640      * in the results.
6641      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6642      * @param {Object} scope (optional) The scope of the function (defaults to this)
6643       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6644      **/
6645     queryBy : function(fn, scope){
6646         var data = this.snapshot || this.data;
6647         return data.filterBy(fn, scope||this);
6648     },
6649
6650     /**
6651      * Collects unique values for a particular dataIndex from this store.
6652      * @param {String} dataIndex The property to collect
6653      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6654      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6655      * @return {Array} An array of the unique values
6656      **/
6657     collect : function(dataIndex, allowNull, bypassFilter){
6658         var d = (bypassFilter === true && this.snapshot) ?
6659                 this.snapshot.items : this.data.items;
6660         var v, sv, r = [], l = {};
6661         for(var i = 0, len = d.length; i < len; i++){
6662             v = d[i].data[dataIndex];
6663             sv = String(v);
6664             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6665                 l[sv] = true;
6666                 r[r.length] = v;
6667             }
6668         }
6669         return r;
6670     },
6671
6672     /**
6673      * Revert to a view of the Record cache with no filtering applied.
6674      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6675      */
6676     clearFilter : function(suppressEvent){
6677         if(this.snapshot && this.snapshot != this.data){
6678             this.data = this.snapshot;
6679             delete this.snapshot;
6680             if(suppressEvent !== true){
6681                 this.fireEvent("datachanged", this);
6682             }
6683         }
6684     },
6685
6686     // private
6687     afterEdit : function(record){
6688         if(this.modified.indexOf(record) == -1){
6689             this.modified.push(record);
6690         }
6691         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6692     },
6693     
6694     // private
6695     afterReject : function(record){
6696         this.modified.remove(record);
6697         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6698     },
6699
6700     // private
6701     afterCommit : function(record){
6702         this.modified.remove(record);
6703         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6704     },
6705
6706     /**
6707      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6708      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6709      */
6710     commitChanges : function(){
6711         var m = this.modified.slice(0);
6712         this.modified = [];
6713         for(var i = 0, len = m.length; i < len; i++){
6714             m[i].commit();
6715         }
6716     },
6717
6718     /**
6719      * Cancel outstanding changes on all changed records.
6720      */
6721     rejectChanges : function(){
6722         var m = this.modified.slice(0);
6723         this.modified = [];
6724         for(var i = 0, len = m.length; i < len; i++){
6725             m[i].reject();
6726         }
6727     },
6728
6729     onMetaChange : function(meta, rtype, o){
6730         this.recordType = rtype;
6731         this.fields = rtype.prototype.fields;
6732         delete this.snapshot;
6733         this.sortInfo = meta.sortInfo || this.sortInfo;
6734         this.modified = [];
6735         this.fireEvent('metachange', this, this.reader.meta);
6736     },
6737     
6738     moveIndex : function(data, type)
6739     {
6740         var index = this.indexOf(data);
6741         
6742         var newIndex = index + type;
6743         
6744         this.remove(data);
6745         
6746         this.insert(newIndex, data);
6747         
6748     }
6749 });/*
6750  * Based on:
6751  * Ext JS Library 1.1.1
6752  * Copyright(c) 2006-2007, Ext JS, LLC.
6753  *
6754  * Originally Released Under LGPL - original licence link has changed is not relivant.
6755  *
6756  * Fork - LGPL
6757  * <script type="text/javascript">
6758  */
6759
6760 /**
6761  * @class Roo.data.SimpleStore
6762  * @extends Roo.data.Store
6763  * Small helper class to make creating Stores from Array data easier.
6764  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6765  * @cfg {Array} fields An array of field definition objects, or field name strings.
6766  * @cfg {Array} data The multi-dimensional array of data
6767  * @constructor
6768  * @param {Object} config
6769  */
6770 Roo.data.SimpleStore = function(config){
6771     Roo.data.SimpleStore.superclass.constructor.call(this, {
6772         isLocal : true,
6773         reader: new Roo.data.ArrayReader({
6774                 id: config.id
6775             },
6776             Roo.data.Record.create(config.fields)
6777         ),
6778         proxy : new Roo.data.MemoryProxy(config.data)
6779     });
6780     this.load();
6781 };
6782 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6783  * Based on:
6784  * Ext JS Library 1.1.1
6785  * Copyright(c) 2006-2007, Ext JS, LLC.
6786  *
6787  * Originally Released Under LGPL - original licence link has changed is not relivant.
6788  *
6789  * Fork - LGPL
6790  * <script type="text/javascript">
6791  */
6792
6793 /**
6794 /**
6795  * @extends Roo.data.Store
6796  * @class Roo.data.JsonStore
6797  * Small helper class to make creating Stores for JSON data easier. <br/>
6798 <pre><code>
6799 var store = new Roo.data.JsonStore({
6800     url: 'get-images.php',
6801     root: 'images',
6802     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6803 });
6804 </code></pre>
6805  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6806  * JsonReader and HttpProxy (unless inline data is provided).</b>
6807  * @cfg {Array} fields An array of field definition objects, or field name strings.
6808  * @constructor
6809  * @param {Object} config
6810  */
6811 Roo.data.JsonStore = function(c){
6812     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6813         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6814         reader: new Roo.data.JsonReader(c, c.fields)
6815     }));
6816 };
6817 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6818  * Based on:
6819  * Ext JS Library 1.1.1
6820  * Copyright(c) 2006-2007, Ext JS, LLC.
6821  *
6822  * Originally Released Under LGPL - original licence link has changed is not relivant.
6823  *
6824  * Fork - LGPL
6825  * <script type="text/javascript">
6826  */
6827
6828  
6829 Roo.data.Field = function(config){
6830     if(typeof config == "string"){
6831         config = {name: config};
6832     }
6833     Roo.apply(this, config);
6834     
6835     if(!this.type){
6836         this.type = "auto";
6837     }
6838     
6839     var st = Roo.data.SortTypes;
6840     // named sortTypes are supported, here we look them up
6841     if(typeof this.sortType == "string"){
6842         this.sortType = st[this.sortType];
6843     }
6844     
6845     // set default sortType for strings and dates
6846     if(!this.sortType){
6847         switch(this.type){
6848             case "string":
6849                 this.sortType = st.asUCString;
6850                 break;
6851             case "date":
6852                 this.sortType = st.asDate;
6853                 break;
6854             default:
6855                 this.sortType = st.none;
6856         }
6857     }
6858
6859     // define once
6860     var stripRe = /[\$,%]/g;
6861
6862     // prebuilt conversion function for this field, instead of
6863     // switching every time we're reading a value
6864     if(!this.convert){
6865         var cv, dateFormat = this.dateFormat;
6866         switch(this.type){
6867             case "":
6868             case "auto":
6869             case undefined:
6870                 cv = function(v){ return v; };
6871                 break;
6872             case "string":
6873                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6874                 break;
6875             case "int":
6876                 cv = function(v){
6877                     return v !== undefined && v !== null && v !== '' ?
6878                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6879                     };
6880                 break;
6881             case "float":
6882                 cv = function(v){
6883                     return v !== undefined && v !== null && v !== '' ?
6884                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6885                     };
6886                 break;
6887             case "bool":
6888             case "boolean":
6889                 cv = function(v){ return v === true || v === "true" || v == 1; };
6890                 break;
6891             case "date":
6892                 cv = function(v){
6893                     if(!v){
6894                         return '';
6895                     }
6896                     if(v instanceof Date){
6897                         return v;
6898                     }
6899                     if(dateFormat){
6900                         if(dateFormat == "timestamp"){
6901                             return new Date(v*1000);
6902                         }
6903                         return Date.parseDate(v, dateFormat);
6904                     }
6905                     var parsed = Date.parse(v);
6906                     return parsed ? new Date(parsed) : null;
6907                 };
6908              break;
6909             
6910         }
6911         this.convert = cv;
6912     }
6913 };
6914
6915 Roo.data.Field.prototype = {
6916     dateFormat: null,
6917     defaultValue: "",
6918     mapping: null,
6919     sortType : null,
6920     sortDir : "ASC"
6921 };/*
6922  * Based on:
6923  * Ext JS Library 1.1.1
6924  * Copyright(c) 2006-2007, Ext JS, LLC.
6925  *
6926  * Originally Released Under LGPL - original licence link has changed is not relivant.
6927  *
6928  * Fork - LGPL
6929  * <script type="text/javascript">
6930  */
6931  
6932 // Base class for reading structured data from a data source.  This class is intended to be
6933 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6934
6935 /**
6936  * @class Roo.data.DataReader
6937  * Base class for reading structured data from a data source.  This class is intended to be
6938  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6939  */
6940
6941 Roo.data.DataReader = function(meta, recordType){
6942     
6943     this.meta = meta;
6944     
6945     this.recordType = recordType instanceof Array ? 
6946         Roo.data.Record.create(recordType) : recordType;
6947 };
6948
6949 Roo.data.DataReader.prototype = {
6950      /**
6951      * Create an empty record
6952      * @param {Object} data (optional) - overlay some values
6953      * @return {Roo.data.Record} record created.
6954      */
6955     newRow :  function(d) {
6956         var da =  {};
6957         this.recordType.prototype.fields.each(function(c) {
6958             switch( c.type) {
6959                 case 'int' : da[c.name] = 0; break;
6960                 case 'date' : da[c.name] = new Date(); break;
6961                 case 'float' : da[c.name] = 0.0; break;
6962                 case 'boolean' : da[c.name] = false; break;
6963                 default : da[c.name] = ""; break;
6964             }
6965             
6966         });
6967         return new this.recordType(Roo.apply(da, d));
6968     }
6969     
6970 };/*
6971  * Based on:
6972  * Ext JS Library 1.1.1
6973  * Copyright(c) 2006-2007, Ext JS, LLC.
6974  *
6975  * Originally Released Under LGPL - original licence link has changed is not relivant.
6976  *
6977  * Fork - LGPL
6978  * <script type="text/javascript">
6979  */
6980
6981 /**
6982  * @class Roo.data.DataProxy
6983  * @extends Roo.data.Observable
6984  * This class is an abstract base class for implementations which provide retrieval of
6985  * unformatted data objects.<br>
6986  * <p>
6987  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6988  * (of the appropriate type which knows how to parse the data object) to provide a block of
6989  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6990  * <p>
6991  * Custom implementations must implement the load method as described in
6992  * {@link Roo.data.HttpProxy#load}.
6993  */
6994 Roo.data.DataProxy = function(){
6995     this.addEvents({
6996         /**
6997          * @event beforeload
6998          * Fires before a network request is made to retrieve a data object.
6999          * @param {Object} This DataProxy object.
7000          * @param {Object} params The params parameter to the load function.
7001          */
7002         beforeload : true,
7003         /**
7004          * @event load
7005          * Fires before the load method's callback is called.
7006          * @param {Object} This DataProxy object.
7007          * @param {Object} o The data object.
7008          * @param {Object} arg The callback argument object passed to the load function.
7009          */
7010         load : true,
7011         /**
7012          * @event loadexception
7013          * Fires if an Exception occurs during data retrieval.
7014          * @param {Object} This DataProxy object.
7015          * @param {Object} o The data object.
7016          * @param {Object} arg The callback argument object passed to the load function.
7017          * @param {Object} e The Exception.
7018          */
7019         loadexception : true
7020     });
7021     Roo.data.DataProxy.superclass.constructor.call(this);
7022 };
7023
7024 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7025
7026     /**
7027      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7028      */
7029 /*
7030  * Based on:
7031  * Ext JS Library 1.1.1
7032  * Copyright(c) 2006-2007, Ext JS, LLC.
7033  *
7034  * Originally Released Under LGPL - original licence link has changed is not relivant.
7035  *
7036  * Fork - LGPL
7037  * <script type="text/javascript">
7038  */
7039 /**
7040  * @class Roo.data.MemoryProxy
7041  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7042  * to the Reader when its load method is called.
7043  * @constructor
7044  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7045  */
7046 Roo.data.MemoryProxy = function(data){
7047     if (data.data) {
7048         data = data.data;
7049     }
7050     Roo.data.MemoryProxy.superclass.constructor.call(this);
7051     this.data = data;
7052 };
7053
7054 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7055     /**
7056      * Load data from the requested source (in this case an in-memory
7057      * data object passed to the constructor), read the data object into
7058      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7059      * process that block using the passed callback.
7060      * @param {Object} params This parameter is not used by the MemoryProxy class.
7061      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7062      * object into a block of Roo.data.Records.
7063      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7064      * The function must be passed <ul>
7065      * <li>The Record block object</li>
7066      * <li>The "arg" argument from the load function</li>
7067      * <li>A boolean success indicator</li>
7068      * </ul>
7069      * @param {Object} scope The scope in which to call the callback
7070      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7071      */
7072     load : function(params, reader, callback, scope, arg){
7073         params = params || {};
7074         var result;
7075         try {
7076             result = reader.readRecords(this.data);
7077         }catch(e){
7078             this.fireEvent("loadexception", this, arg, null, e);
7079             callback.call(scope, null, arg, false);
7080             return;
7081         }
7082         callback.call(scope, result, arg, true);
7083     },
7084     
7085     // private
7086     update : function(params, records){
7087         
7088     }
7089 });/*
7090  * Based on:
7091  * Ext JS Library 1.1.1
7092  * Copyright(c) 2006-2007, Ext JS, LLC.
7093  *
7094  * Originally Released Under LGPL - original licence link has changed is not relivant.
7095  *
7096  * Fork - LGPL
7097  * <script type="text/javascript">
7098  */
7099 /**
7100  * @class Roo.data.HttpProxy
7101  * @extends Roo.data.DataProxy
7102  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7103  * configured to reference a certain URL.<br><br>
7104  * <p>
7105  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7106  * from which the running page was served.<br><br>
7107  * <p>
7108  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7109  * <p>
7110  * Be aware that to enable the browser to parse an XML document, the server must set
7111  * the Content-Type header in the HTTP response to "text/xml".
7112  * @constructor
7113  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7114  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7115  * will be used to make the request.
7116  */
7117 Roo.data.HttpProxy = function(conn){
7118     Roo.data.HttpProxy.superclass.constructor.call(this);
7119     // is conn a conn config or a real conn?
7120     this.conn = conn;
7121     this.useAjax = !conn || !conn.events;
7122   
7123 };
7124
7125 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7126     // thse are take from connection...
7127     
7128     /**
7129      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7130      */
7131     /**
7132      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7133      * extra parameters to each request made by this object. (defaults to undefined)
7134      */
7135     /**
7136      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7137      *  to each request made by this object. (defaults to undefined)
7138      */
7139     /**
7140      * @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)
7141      */
7142     /**
7143      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7144      */
7145      /**
7146      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7147      * @type Boolean
7148      */
7149   
7150
7151     /**
7152      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7153      * @type Boolean
7154      */
7155     /**
7156      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7157      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7158      * a finer-grained basis than the DataProxy events.
7159      */
7160     getConnection : function(){
7161         return this.useAjax ? Roo.Ajax : this.conn;
7162     },
7163
7164     /**
7165      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7166      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7167      * process that block using the passed callback.
7168      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7169      * for the request to the remote server.
7170      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7171      * object into a block of Roo.data.Records.
7172      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7173      * The function must be passed <ul>
7174      * <li>The Record block object</li>
7175      * <li>The "arg" argument from the load function</li>
7176      * <li>A boolean success indicator</li>
7177      * </ul>
7178      * @param {Object} scope The scope in which to call the callback
7179      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7180      */
7181     load : function(params, reader, callback, scope, arg){
7182         if(this.fireEvent("beforeload", this, params) !== false){
7183             var  o = {
7184                 params : params || {},
7185                 request: {
7186                     callback : callback,
7187                     scope : scope,
7188                     arg : arg
7189                 },
7190                 reader: reader,
7191                 callback : this.loadResponse,
7192                 scope: this
7193             };
7194             if(this.useAjax){
7195                 Roo.applyIf(o, this.conn);
7196                 if(this.activeRequest){
7197                     Roo.Ajax.abort(this.activeRequest);
7198                 }
7199                 this.activeRequest = Roo.Ajax.request(o);
7200             }else{
7201                 this.conn.request(o);
7202             }
7203         }else{
7204             callback.call(scope||this, null, arg, false);
7205         }
7206     },
7207
7208     // private
7209     loadResponse : function(o, success, response){
7210         delete this.activeRequest;
7211         if(!success){
7212             this.fireEvent("loadexception", this, o, response);
7213             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7214             return;
7215         }
7216         var result;
7217         try {
7218             result = o.reader.read(response);
7219         }catch(e){
7220             this.fireEvent("loadexception", this, o, response, e);
7221             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7222             return;
7223         }
7224         
7225         this.fireEvent("load", this, o, o.request.arg);
7226         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7227     },
7228
7229     // private
7230     update : function(dataSet){
7231
7232     },
7233
7234     // private
7235     updateResponse : function(dataSet){
7236
7237     }
7238 });/*
7239  * Based on:
7240  * Ext JS Library 1.1.1
7241  * Copyright(c) 2006-2007, Ext JS, LLC.
7242  *
7243  * Originally Released Under LGPL - original licence link has changed is not relivant.
7244  *
7245  * Fork - LGPL
7246  * <script type="text/javascript">
7247  */
7248
7249 /**
7250  * @class Roo.data.ScriptTagProxy
7251  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7252  * other than the originating domain of the running page.<br><br>
7253  * <p>
7254  * <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
7255  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7256  * <p>
7257  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7258  * source code that is used as the source inside a &lt;script> tag.<br><br>
7259  * <p>
7260  * In order for the browser to process the returned data, the server must wrap the data object
7261  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7262  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7263  * depending on whether the callback name was passed:
7264  * <p>
7265  * <pre><code>
7266 boolean scriptTag = false;
7267 String cb = request.getParameter("callback");
7268 if (cb != null) {
7269     scriptTag = true;
7270     response.setContentType("text/javascript");
7271 } else {
7272     response.setContentType("application/x-json");
7273 }
7274 Writer out = response.getWriter();
7275 if (scriptTag) {
7276     out.write(cb + "(");
7277 }
7278 out.print(dataBlock.toJsonString());
7279 if (scriptTag) {
7280     out.write(");");
7281 }
7282 </pre></code>
7283  *
7284  * @constructor
7285  * @param {Object} config A configuration object.
7286  */
7287 Roo.data.ScriptTagProxy = function(config){
7288     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7289     Roo.apply(this, config);
7290     this.head = document.getElementsByTagName("head")[0];
7291 };
7292
7293 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7294
7295 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7296     /**
7297      * @cfg {String} url The URL from which to request the data object.
7298      */
7299     /**
7300      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7301      */
7302     timeout : 30000,
7303     /**
7304      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7305      * the server the name of the callback function set up by the load call to process the returned data object.
7306      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7307      * javascript output which calls this named function passing the data object as its only parameter.
7308      */
7309     callbackParam : "callback",
7310     /**
7311      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7312      * name to the request.
7313      */
7314     nocache : true,
7315
7316     /**
7317      * Load data from the configured URL, read the data object into
7318      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7319      * process that block using the passed callback.
7320      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7321      * for the request to the remote server.
7322      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7323      * object into a block of Roo.data.Records.
7324      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7325      * The function must be passed <ul>
7326      * <li>The Record block object</li>
7327      * <li>The "arg" argument from the load function</li>
7328      * <li>A boolean success indicator</li>
7329      * </ul>
7330      * @param {Object} scope The scope in which to call the callback
7331      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7332      */
7333     load : function(params, reader, callback, scope, arg){
7334         if(this.fireEvent("beforeload", this, params) !== false){
7335
7336             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7337
7338             var url = this.url;
7339             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7340             if(this.nocache){
7341                 url += "&_dc=" + (new Date().getTime());
7342             }
7343             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7344             var trans = {
7345                 id : transId,
7346                 cb : "stcCallback"+transId,
7347                 scriptId : "stcScript"+transId,
7348                 params : params,
7349                 arg : arg,
7350                 url : url,
7351                 callback : callback,
7352                 scope : scope,
7353                 reader : reader
7354             };
7355             var conn = this;
7356
7357             window[trans.cb] = function(o){
7358                 conn.handleResponse(o, trans);
7359             };
7360
7361             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7362
7363             if(this.autoAbort !== false){
7364                 this.abort();
7365             }
7366
7367             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7368
7369             var script = document.createElement("script");
7370             script.setAttribute("src", url);
7371             script.setAttribute("type", "text/javascript");
7372             script.setAttribute("id", trans.scriptId);
7373             this.head.appendChild(script);
7374
7375             this.trans = trans;
7376         }else{
7377             callback.call(scope||this, null, arg, false);
7378         }
7379     },
7380
7381     // private
7382     isLoading : function(){
7383         return this.trans ? true : false;
7384     },
7385
7386     /**
7387      * Abort the current server request.
7388      */
7389     abort : function(){
7390         if(this.isLoading()){
7391             this.destroyTrans(this.trans);
7392         }
7393     },
7394
7395     // private
7396     destroyTrans : function(trans, isLoaded){
7397         this.head.removeChild(document.getElementById(trans.scriptId));
7398         clearTimeout(trans.timeoutId);
7399         if(isLoaded){
7400             window[trans.cb] = undefined;
7401             try{
7402                 delete window[trans.cb];
7403             }catch(e){}
7404         }else{
7405             // if hasn't been loaded, wait for load to remove it to prevent script error
7406             window[trans.cb] = function(){
7407                 window[trans.cb] = undefined;
7408                 try{
7409                     delete window[trans.cb];
7410                 }catch(e){}
7411             };
7412         }
7413     },
7414
7415     // private
7416     handleResponse : function(o, trans){
7417         this.trans = false;
7418         this.destroyTrans(trans, true);
7419         var result;
7420         try {
7421             result = trans.reader.readRecords(o);
7422         }catch(e){
7423             this.fireEvent("loadexception", this, o, trans.arg, e);
7424             trans.callback.call(trans.scope||window, null, trans.arg, false);
7425             return;
7426         }
7427         this.fireEvent("load", this, o, trans.arg);
7428         trans.callback.call(trans.scope||window, result, trans.arg, true);
7429     },
7430
7431     // private
7432     handleFailure : function(trans){
7433         this.trans = false;
7434         this.destroyTrans(trans, false);
7435         this.fireEvent("loadexception", this, null, trans.arg);
7436         trans.callback.call(trans.scope||window, null, trans.arg, false);
7437     }
7438 });/*
7439  * Based on:
7440  * Ext JS Library 1.1.1
7441  * Copyright(c) 2006-2007, Ext JS, LLC.
7442  *
7443  * Originally Released Under LGPL - original licence link has changed is not relivant.
7444  *
7445  * Fork - LGPL
7446  * <script type="text/javascript">
7447  */
7448
7449 /**
7450  * @class Roo.data.JsonReader
7451  * @extends Roo.data.DataReader
7452  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7453  * based on mappings in a provided Roo.data.Record constructor.
7454  * 
7455  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7456  * in the reply previously. 
7457  * 
7458  * <p>
7459  * Example code:
7460  * <pre><code>
7461 var RecordDef = Roo.data.Record.create([
7462     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7463     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7464 ]);
7465 var myReader = new Roo.data.JsonReader({
7466     totalProperty: "results",    // The property which contains the total dataset size (optional)
7467     root: "rows",                // The property which contains an Array of row objects
7468     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7469 }, RecordDef);
7470 </code></pre>
7471  * <p>
7472  * This would consume a JSON file like this:
7473  * <pre><code>
7474 { 'results': 2, 'rows': [
7475     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7476     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7477 }
7478 </code></pre>
7479  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7480  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7481  * paged from the remote server.
7482  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7483  * @cfg {String} root name of the property which contains the Array of row objects.
7484  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7485  * @constructor
7486  * Create a new JsonReader
7487  * @param {Object} meta Metadata configuration options
7488  * @param {Object} recordType Either an Array of field definition objects,
7489  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7490  */
7491 Roo.data.JsonReader = function(meta, recordType){
7492     
7493     meta = meta || {};
7494     // set some defaults:
7495     Roo.applyIf(meta, {
7496         totalProperty: 'total',
7497         successProperty : 'success',
7498         root : 'data',
7499         id : 'id'
7500     });
7501     
7502     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7503 };
7504 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7505     
7506     /**
7507      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7508      * Used by Store query builder to append _requestMeta to params.
7509      * 
7510      */
7511     metaFromRemote : false,
7512     /**
7513      * This method is only used by a DataProxy which has retrieved data from a remote server.
7514      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7515      * @return {Object} data A data block which is used by an Roo.data.Store object as
7516      * a cache of Roo.data.Records.
7517      */
7518     read : function(response){
7519         var json = response.responseText;
7520        
7521         var o = /* eval:var:o */ eval("("+json+")");
7522         if(!o) {
7523             throw {message: "JsonReader.read: Json object not found"};
7524         }
7525         
7526         if(o.metaData){
7527             
7528             delete this.ef;
7529             this.metaFromRemote = true;
7530             this.meta = o.metaData;
7531             this.recordType = Roo.data.Record.create(o.metaData.fields);
7532             this.onMetaChange(this.meta, this.recordType, o);
7533         }
7534         return this.readRecords(o);
7535     },
7536
7537     // private function a store will implement
7538     onMetaChange : function(meta, recordType, o){
7539
7540     },
7541
7542     /**
7543          * @ignore
7544          */
7545     simpleAccess: function(obj, subsc) {
7546         return obj[subsc];
7547     },
7548
7549         /**
7550          * @ignore
7551          */
7552     getJsonAccessor: function(){
7553         var re = /[\[\.]/;
7554         return function(expr) {
7555             try {
7556                 return(re.test(expr))
7557                     ? new Function("obj", "return obj." + expr)
7558                     : function(obj){
7559                         return obj[expr];
7560                     };
7561             } catch(e){}
7562             return Roo.emptyFn;
7563         };
7564     }(),
7565
7566     /**
7567      * Create a data block containing Roo.data.Records from an XML document.
7568      * @param {Object} o An object which contains an Array of row objects in the property specified
7569      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7570      * which contains the total size of the dataset.
7571      * @return {Object} data A data block which is used by an Roo.data.Store object as
7572      * a cache of Roo.data.Records.
7573      */
7574     readRecords : function(o){
7575         /**
7576          * After any data loads, the raw JSON data is available for further custom processing.
7577          * @type Object
7578          */
7579         this.o = o;
7580         var s = this.meta, Record = this.recordType,
7581             f = Record.prototype.fields, fi = f.items, fl = f.length;
7582
7583 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7584         if (!this.ef) {
7585             if(s.totalProperty) {
7586                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7587                 }
7588                 if(s.successProperty) {
7589                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7590                 }
7591                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7592                 if (s.id) {
7593                         var g = this.getJsonAccessor(s.id);
7594                         this.getId = function(rec) {
7595                                 var r = g(rec);
7596                                 return (r === undefined || r === "") ? null : r;
7597                         };
7598                 } else {
7599                         this.getId = function(){return null;};
7600                 }
7601             this.ef = [];
7602             for(var jj = 0; jj < fl; jj++){
7603                 f = fi[jj];
7604                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7605                 this.ef[jj] = this.getJsonAccessor(map);
7606             }
7607         }
7608
7609         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7610         if(s.totalProperty){
7611             var vt = parseInt(this.getTotal(o), 10);
7612             if(!isNaN(vt)){
7613                 totalRecords = vt;
7614             }
7615         }
7616         if(s.successProperty){
7617             var vs = this.getSuccess(o);
7618             if(vs === false || vs === 'false'){
7619                 success = false;
7620             }
7621         }
7622         var records = [];
7623             for(var i = 0; i < c; i++){
7624                     var n = root[i];
7625                 var values = {};
7626                 var id = this.getId(n);
7627                 for(var j = 0; j < fl; j++){
7628                     f = fi[j];
7629                 var v = this.ef[j](n);
7630                 if (!f.convert) {
7631                     Roo.log('missing convert for ' + f.name);
7632                     Roo.log(f);
7633                     continue;
7634                 }
7635                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7636                 }
7637                 var record = new Record(values, id);
7638                 record.json = n;
7639                 records[i] = record;
7640             }
7641             return {
7642             raw : o,
7643                 success : success,
7644                 records : records,
7645                 totalRecords : totalRecords
7646             };
7647     }
7648 });/*
7649  * Based on:
7650  * Ext JS Library 1.1.1
7651  * Copyright(c) 2006-2007, Ext JS, LLC.
7652  *
7653  * Originally Released Under LGPL - original licence link has changed is not relivant.
7654  *
7655  * Fork - LGPL
7656  * <script type="text/javascript">
7657  */
7658
7659 /**
7660  * @class Roo.data.ArrayReader
7661  * @extends Roo.data.DataReader
7662  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7663  * Each element of that Array represents a row of data fields. The
7664  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7665  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7666  * <p>
7667  * Example code:.
7668  * <pre><code>
7669 var RecordDef = Roo.data.Record.create([
7670     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7671     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7672 ]);
7673 var myReader = new Roo.data.ArrayReader({
7674     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7675 }, RecordDef);
7676 </code></pre>
7677  * <p>
7678  * This would consume an Array like this:
7679  * <pre><code>
7680 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7681   </code></pre>
7682  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7683  * @constructor
7684  * Create a new JsonReader
7685  * @param {Object} meta Metadata configuration options.
7686  * @param {Object} recordType Either an Array of field definition objects
7687  * as specified to {@link Roo.data.Record#create},
7688  * or an {@link Roo.data.Record} object
7689  * created using {@link Roo.data.Record#create}.
7690  */
7691 Roo.data.ArrayReader = function(meta, recordType){
7692     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7693 };
7694
7695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7696     /**
7697      * Create a data block containing Roo.data.Records from an XML document.
7698      * @param {Object} o An Array of row objects which represents the dataset.
7699      * @return {Object} data A data block which is used by an Roo.data.Store object as
7700      * a cache of Roo.data.Records.
7701      */
7702     readRecords : function(o){
7703         var sid = this.meta ? this.meta.id : null;
7704         var recordType = this.recordType, fields = recordType.prototype.fields;
7705         var records = [];
7706         var root = o;
7707             for(var i = 0; i < root.length; i++){
7708                     var n = root[i];
7709                 var values = {};
7710                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7711                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7712                 var f = fields.items[j];
7713                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7714                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7715                 v = f.convert(v);
7716                 values[f.name] = v;
7717             }
7718                 var record = new recordType(values, id);
7719                 record.json = n;
7720                 records[records.length] = record;
7721             }
7722             return {
7723                 records : records,
7724                 totalRecords : records.length
7725             };
7726     }
7727 });/*
7728  * - LGPL
7729  * * 
7730  */
7731
7732 /**
7733  * @class Roo.bootstrap.ComboBox
7734  * @extends Roo.bootstrap.TriggerField
7735  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7736  * @cfg {Boolean} append (true|false) default false
7737  * @constructor
7738  * Create a new ComboBox.
7739  * @param {Object} config Configuration options
7740  */
7741 Roo.bootstrap.ComboBox = function(config){
7742     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7743     this.addEvents({
7744         /**
7745          * @event expand
7746          * Fires when the dropdown list is expanded
7747              * @param {Roo.bootstrap.ComboBox} combo This combo box
7748              */
7749         'expand' : true,
7750         /**
7751          * @event collapse
7752          * Fires when the dropdown list is collapsed
7753              * @param {Roo.bootstrap.ComboBox} combo This combo box
7754              */
7755         'collapse' : true,
7756         /**
7757          * @event beforeselect
7758          * Fires before a list item is selected. Return false to cancel the selection.
7759              * @param {Roo.bootstrap.ComboBox} combo This combo box
7760              * @param {Roo.data.Record} record The data record returned from the underlying store
7761              * @param {Number} index The index of the selected item in the dropdown list
7762              */
7763         'beforeselect' : true,
7764         /**
7765          * @event select
7766          * Fires when a list item is selected
7767              * @param {Roo.bootstrap.ComboBox} combo This combo box
7768              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7769              * @param {Number} index The index of the selected item in the dropdown list
7770              */
7771         'select' : true,
7772         /**
7773          * @event beforequery
7774          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7775          * The event object passed has these properties:
7776              * @param {Roo.bootstrap.ComboBox} combo This combo box
7777              * @param {String} query The query
7778              * @param {Boolean} forceAll true to force "all" query
7779              * @param {Boolean} cancel true to cancel the query
7780              * @param {Object} e The query event object
7781              */
7782         'beforequery': true,
7783          /**
7784          * @event add
7785          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7786              * @param {Roo.bootstrap.ComboBox} combo This combo box
7787              */
7788         'add' : true,
7789         /**
7790          * @event edit
7791          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7792              * @param {Roo.bootstrap.ComboBox} combo This combo box
7793              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7794              */
7795         'edit' : true,
7796         /**
7797          * @event remove
7798          * Fires when the remove value from the combobox array
7799              * @param {Roo.bootstrap.ComboBox} combo This combo box
7800              */
7801         'remove' : true
7802         
7803     });
7804     
7805     
7806     this.selectedIndex = -1;
7807     if(this.mode == 'local'){
7808         if(config.queryDelay === undefined){
7809             this.queryDelay = 10;
7810         }
7811         if(config.minChars === undefined){
7812             this.minChars = 0;
7813         }
7814     }
7815 };
7816
7817 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7818      
7819     /**
7820      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7821      * rendering into an Roo.Editor, defaults to false)
7822      */
7823     /**
7824      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7825      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7826      */
7827     /**
7828      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7829      */
7830     /**
7831      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7832      * the dropdown list (defaults to undefined, with no header element)
7833      */
7834
7835      /**
7836      * @cfg {String/Roo.Template} tpl The template to use to render the output
7837      */
7838      
7839      /**
7840      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7841      */
7842     listWidth: undefined,
7843     /**
7844      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7845      * mode = 'remote' or 'text' if mode = 'local')
7846      */
7847     displayField: undefined,
7848     /**
7849      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7850      * mode = 'remote' or 'value' if mode = 'local'). 
7851      * Note: use of a valueField requires the user make a selection
7852      * in order for a value to be mapped.
7853      */
7854     valueField: undefined,
7855     
7856     
7857     /**
7858      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7859      * field's data value (defaults to the underlying DOM element's name)
7860      */
7861     hiddenName: undefined,
7862     /**
7863      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7864      */
7865     listClass: '',
7866     /**
7867      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7868      */
7869     selectedClass: 'active',
7870     
7871     /**
7872      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7873      */
7874     shadow:'sides',
7875     /**
7876      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7877      * anchor positions (defaults to 'tl-bl')
7878      */
7879     listAlign: 'tl-bl?',
7880     /**
7881      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7882      */
7883     maxHeight: 300,
7884     /**
7885      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7886      * query specified by the allQuery config option (defaults to 'query')
7887      */
7888     triggerAction: 'query',
7889     /**
7890      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7891      * (defaults to 4, does not apply if editable = false)
7892      */
7893     minChars : 4,
7894     /**
7895      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7896      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7897      */
7898     typeAhead: false,
7899     /**
7900      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7901      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7902      */
7903     queryDelay: 500,
7904     /**
7905      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7906      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7907      */
7908     pageSize: 0,
7909     /**
7910      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7911      * when editable = true (defaults to false)
7912      */
7913     selectOnFocus:false,
7914     /**
7915      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7916      */
7917     queryParam: 'query',
7918     /**
7919      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7920      * when mode = 'remote' (defaults to 'Loading...')
7921      */
7922     loadingText: 'Loading...',
7923     /**
7924      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7925      */
7926     resizable: false,
7927     /**
7928      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7929      */
7930     handleHeight : 8,
7931     /**
7932      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7933      * traditional select (defaults to true)
7934      */
7935     editable: true,
7936     /**
7937      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7938      */
7939     allQuery: '',
7940     /**
7941      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7942      */
7943     mode: 'remote',
7944     /**
7945      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7946      * listWidth has a higher value)
7947      */
7948     minListWidth : 70,
7949     /**
7950      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7951      * allow the user to set arbitrary text into the field (defaults to false)
7952      */
7953     forceSelection:false,
7954     /**
7955      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7956      * if typeAhead = true (defaults to 250)
7957      */
7958     typeAheadDelay : 250,
7959     /**
7960      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7961      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7962      */
7963     valueNotFoundText : undefined,
7964     /**
7965      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7966      */
7967     blockFocus : false,
7968     
7969     /**
7970      * @cfg {Boolean} disableClear Disable showing of clear button.
7971      */
7972     disableClear : false,
7973     /**
7974      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7975      */
7976     alwaysQuery : false,
7977     
7978     /**
7979      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7980      */
7981     multiple : false,
7982     
7983     //private
7984     addicon : false,
7985     editicon: false,
7986     
7987     page: 0,
7988     hasQuery: false,
7989     append: false,
7990     loadNext: false,
7991     item: [],
7992     
7993     // element that contains real text value.. (when hidden is used..)
7994      
7995     // private
7996     initEvents: function(){
7997         
7998         if (!this.store) {
7999             throw "can not find store for combo";
8000         }
8001         this.store = Roo.factory(this.store, Roo.data);
8002         
8003         
8004         
8005         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8006         
8007         
8008         if(this.hiddenName){
8009             
8010             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8011             
8012             this.hiddenField.dom.value =
8013                 this.hiddenValue !== undefined ? this.hiddenValue :
8014                 this.value !== undefined ? this.value : '';
8015
8016             // prevent input submission
8017             this.el.dom.removeAttribute('name');
8018             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8019              
8020              
8021         }
8022         //if(Roo.isGecko){
8023         //    this.el.dom.setAttribute('autocomplete', 'off');
8024         //}
8025
8026         var cls = 'x-combo-list';
8027         this.list = this.el.select('ul.dropdown-menu',true).first();
8028
8029         //this.list = new Roo.Layer({
8030         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8031         //});
8032         
8033         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8034         this.list.setWidth(lw);
8035         
8036         this.list.on('mouseover', this.onViewOver, this);
8037         this.list.on('mousemove', this.onViewMove, this);
8038         
8039         this.list.on('scroll', this.onViewScroll, this);
8040         
8041         /*
8042         this.list.swallowEvent('mousewheel');
8043         this.assetHeight = 0;
8044
8045         if(this.title){
8046             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8047             this.assetHeight += this.header.getHeight();
8048         }
8049
8050         this.innerList = this.list.createChild({cls:cls+'-inner'});
8051         this.innerList.on('mouseover', this.onViewOver, this);
8052         this.innerList.on('mousemove', this.onViewMove, this);
8053         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8054         
8055         if(this.allowBlank && !this.pageSize && !this.disableClear){
8056             this.footer = this.list.createChild({cls:cls+'-ft'});
8057             this.pageTb = new Roo.Toolbar(this.footer);
8058            
8059         }
8060         if(this.pageSize){
8061             this.footer = this.list.createChild({cls:cls+'-ft'});
8062             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8063                     {pageSize: this.pageSize});
8064             
8065         }
8066         
8067         if (this.pageTb && this.allowBlank && !this.disableClear) {
8068             var _this = this;
8069             this.pageTb.add(new Roo.Toolbar.Fill(), {
8070                 cls: 'x-btn-icon x-btn-clear',
8071                 text: '&#160;',
8072                 handler: function()
8073                 {
8074                     _this.collapse();
8075                     _this.clearValue();
8076                     _this.onSelect(false, -1);
8077                 }
8078             });
8079         }
8080         if (this.footer) {
8081             this.assetHeight += this.footer.getHeight();
8082         }
8083         */
8084             
8085         if(!this.tpl){
8086             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8087         }
8088
8089         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8090             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8091         });
8092         //this.view.wrapEl.setDisplayed(false);
8093         this.view.on('click', this.onViewClick, this);
8094         
8095         
8096         
8097         this.store.on('beforeload', this.onBeforeLoad, this);
8098         this.store.on('load', this.onLoad, this);
8099         this.store.on('loadexception', this.onLoadException, this);
8100         /*
8101         if(this.resizable){
8102             this.resizer = new Roo.Resizable(this.list,  {
8103                pinned:true, handles:'se'
8104             });
8105             this.resizer.on('resize', function(r, w, h){
8106                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8107                 this.listWidth = w;
8108                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8109                 this.restrictHeight();
8110             }, this);
8111             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8112         }
8113         */
8114         if(!this.editable){
8115             this.editable = true;
8116             this.setEditable(false);
8117         }
8118         
8119         /*
8120         
8121         if (typeof(this.events.add.listeners) != 'undefined') {
8122             
8123             this.addicon = this.wrap.createChild(
8124                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8125        
8126             this.addicon.on('click', function(e) {
8127                 this.fireEvent('add', this);
8128             }, this);
8129         }
8130         if (typeof(this.events.edit.listeners) != 'undefined') {
8131             
8132             this.editicon = this.wrap.createChild(
8133                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8134             if (this.addicon) {
8135                 this.editicon.setStyle('margin-left', '40px');
8136             }
8137             this.editicon.on('click', function(e) {
8138                 
8139                 // we fire even  if inothing is selected..
8140                 this.fireEvent('edit', this, this.lastData );
8141                 
8142             }, this);
8143         }
8144         */
8145         
8146         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8147             "up" : function(e){
8148                 this.inKeyMode = true;
8149                 this.selectPrev();
8150             },
8151
8152             "down" : function(e){
8153                 if(!this.isExpanded()){
8154                     this.onTriggerClick();
8155                 }else{
8156                     this.inKeyMode = true;
8157                     this.selectNext();
8158                 }
8159             },
8160
8161             "enter" : function(e){
8162                 this.onViewClick();
8163                 //return true;
8164             },
8165
8166             "esc" : function(e){
8167                 this.collapse();
8168             },
8169
8170             "tab" : function(e){
8171                 this.collapse();
8172                 
8173                 if(this.fireEvent("specialkey", this, e)){
8174                     this.onViewClick(false);
8175                 }
8176                 
8177                 return true;
8178             },
8179
8180             scope : this,
8181
8182             doRelay : function(foo, bar, hname){
8183                 if(hname == 'down' || this.scope.isExpanded()){
8184                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8185                 }
8186                 return true;
8187             },
8188
8189             forceKeyDown: true
8190         });
8191         
8192         
8193         this.queryDelay = Math.max(this.queryDelay || 10,
8194                 this.mode == 'local' ? 10 : 250);
8195         
8196         
8197         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8198         
8199         if(this.typeAhead){
8200             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8201         }
8202         if(this.editable !== false){
8203             this.inputEl().on("keyup", this.onKeyUp, this);
8204         }
8205         if(this.forceSelection){
8206             this.on('blur', this.doForce, this);
8207         }
8208         
8209         if(this.multiple){
8210             this.choices = this.el.select('ul.select2-choices', true).first();
8211             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8212         }
8213     },
8214
8215     onDestroy : function(){
8216         if(this.view){
8217             this.view.setStore(null);
8218             this.view.el.removeAllListeners();
8219             this.view.el.remove();
8220             this.view.purgeListeners();
8221         }
8222         if(this.list){
8223             this.list.dom.innerHTML  = '';
8224         }
8225         if(this.store){
8226             this.store.un('beforeload', this.onBeforeLoad, this);
8227             this.store.un('load', this.onLoad, this);
8228             this.store.un('loadexception', this.onLoadException, this);
8229         }
8230         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8231     },
8232
8233     // private
8234     fireKey : function(e){
8235         if(e.isNavKeyPress() && !this.list.isVisible()){
8236             this.fireEvent("specialkey", this, e);
8237         }
8238     },
8239
8240     // private
8241     onResize: function(w, h){
8242 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8243 //        
8244 //        if(typeof w != 'number'){
8245 //            // we do not handle it!?!?
8246 //            return;
8247 //        }
8248 //        var tw = this.trigger.getWidth();
8249 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8250 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8251 //        var x = w - tw;
8252 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8253 //            
8254 //        //this.trigger.setStyle('left', x+'px');
8255 //        
8256 //        if(this.list && this.listWidth === undefined){
8257 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8258 //            this.list.setWidth(lw);
8259 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8260 //        }
8261         
8262     
8263         
8264     },
8265
8266     /**
8267      * Allow or prevent the user from directly editing the field text.  If false is passed,
8268      * the user will only be able to select from the items defined in the dropdown list.  This method
8269      * is the runtime equivalent of setting the 'editable' config option at config time.
8270      * @param {Boolean} value True to allow the user to directly edit the field text
8271      */
8272     setEditable : function(value){
8273         if(value == this.editable){
8274             return;
8275         }
8276         this.editable = value;
8277         if(!value){
8278             this.inputEl().dom.setAttribute('readOnly', true);
8279             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8280             this.inputEl().addClass('x-combo-noedit');
8281         }else{
8282             this.inputEl().dom.setAttribute('readOnly', false);
8283             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8284             this.inputEl().removeClass('x-combo-noedit');
8285         }
8286     },
8287
8288     // private
8289     
8290     onBeforeLoad : function(combo,opts){
8291         if(!this.hasFocus){
8292             return;
8293         }
8294          if (!opts.add) {
8295             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8296          }
8297         this.restrictHeight();
8298         this.selectedIndex = -1;
8299     },
8300
8301     // private
8302     onLoad : function(){
8303         
8304         this.hasQuery = false;
8305         
8306         if(!this.hasFocus){
8307             return;
8308         }
8309         
8310         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8311             this.loading.hide();
8312         }
8313         
8314         if(this.store.getCount() > 0){
8315             this.expand();
8316             this.restrictHeight();
8317             if(this.lastQuery == this.allQuery){
8318                 if(this.editable){
8319                     this.inputEl().dom.select();
8320                 }
8321                 if(!this.selectByValue(this.value, true)){
8322                     this.select(0, true);
8323                 }
8324             }else{
8325                 this.selectNext();
8326                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8327                     this.taTask.delay(this.typeAheadDelay);
8328                 }
8329             }
8330         }else{
8331             this.onEmptyResults();
8332         }
8333         
8334         //this.el.focus();
8335     },
8336     // private
8337     onLoadException : function()
8338     {
8339         this.hasQuery = false;
8340         
8341         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8342             this.loading.hide();
8343         }
8344         
8345         this.collapse();
8346         Roo.log(this.store.reader.jsonData);
8347         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8348             // fixme
8349             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8350         }
8351         
8352         
8353     },
8354     // private
8355     onTypeAhead : function(){
8356         if(this.store.getCount() > 0){
8357             var r = this.store.getAt(0);
8358             var newValue = r.data[this.displayField];
8359             var len = newValue.length;
8360             var selStart = this.getRawValue().length;
8361             
8362             if(selStart != len){
8363                 this.setRawValue(newValue);
8364                 this.selectText(selStart, newValue.length);
8365             }
8366         }
8367     },
8368
8369     // private
8370     onSelect : function(record, index){
8371         
8372         if(this.fireEvent('beforeselect', this, record, index) !== false){
8373         
8374             this.setFromData(index > -1 ? record.data : false);
8375             
8376             this.collapse();
8377             this.fireEvent('select', this, record, index);
8378         }
8379     },
8380
8381     /**
8382      * Returns the currently selected field value or empty string if no value is set.
8383      * @return {String} value The selected value
8384      */
8385     getValue : function(){
8386         
8387         if(this.multiple){
8388             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8389         }
8390         
8391         if(this.valueField){
8392             return typeof this.value != 'undefined' ? this.value : '';
8393         }else{
8394             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8395         }
8396     },
8397
8398     /**
8399      * Clears any text/value currently set in the field
8400      */
8401     clearValue : function(){
8402         if(this.hiddenField){
8403             this.hiddenField.dom.value = '';
8404         }
8405         this.value = '';
8406         this.setRawValue('');
8407         this.lastSelectionText = '';
8408         
8409     },
8410
8411     /**
8412      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8413      * will be displayed in the field.  If the value does not match the data value of an existing item,
8414      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8415      * Otherwise the field will be blank (although the value will still be set).
8416      * @param {String} value The value to match
8417      */
8418     setValue : function(v){
8419         if(this.multiple){
8420             this.syncValue();
8421             return;
8422         }
8423         
8424         var text = v;
8425         if(this.valueField){
8426             var r = this.findRecord(this.valueField, v);
8427             if(r){
8428                 text = r.data[this.displayField];
8429             }else if(this.valueNotFoundText !== undefined){
8430                 text = this.valueNotFoundText;
8431             }
8432         }
8433         this.lastSelectionText = text;
8434         if(this.hiddenField){
8435             this.hiddenField.dom.value = v;
8436         }
8437         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8438         this.value = v;
8439     },
8440     /**
8441      * @property {Object} the last set data for the element
8442      */
8443     
8444     lastData : false,
8445     /**
8446      * Sets the value of the field based on a object which is related to the record format for the store.
8447      * @param {Object} value the value to set as. or false on reset?
8448      */
8449     setFromData : function(o){
8450         
8451         if(this.multiple){
8452             this.addItem(o);
8453             return;
8454         }
8455             
8456         var dv = ''; // display value
8457         var vv = ''; // value value..
8458         this.lastData = o;
8459         if (this.displayField) {
8460             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8461         } else {
8462             // this is an error condition!!!
8463             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8464         }
8465         
8466         if(this.valueField){
8467             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8468         }
8469         
8470         if(this.hiddenField){
8471             this.hiddenField.dom.value = vv;
8472             
8473             this.lastSelectionText = dv;
8474             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8475             this.value = vv;
8476             return;
8477         }
8478         // no hidden field.. - we store the value in 'value', but still display
8479         // display field!!!!
8480         this.lastSelectionText = dv;
8481         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8482         this.value = vv;
8483         
8484         
8485     },
8486     // private
8487     reset : function(){
8488         // overridden so that last data is reset..
8489         this.setValue(this.originalValue);
8490         this.clearInvalid();
8491         this.lastData = false;
8492         if (this.view) {
8493             this.view.clearSelections();
8494         }
8495     },
8496     // private
8497     findRecord : function(prop, value){
8498         var record;
8499         if(this.store.getCount() > 0){
8500             this.store.each(function(r){
8501                 if(r.data[prop] == value){
8502                     record = r;
8503                     return false;
8504                 }
8505                 return true;
8506             });
8507         }
8508         return record;
8509     },
8510     
8511     getName: function()
8512     {
8513         // returns hidden if it's set..
8514         if (!this.rendered) {return ''};
8515         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8516         
8517     },
8518     // private
8519     onViewMove : function(e, t){
8520         this.inKeyMode = false;
8521     },
8522
8523     // private
8524     onViewOver : function(e, t){
8525         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8526             return;
8527         }
8528         var item = this.view.findItemFromChild(t);
8529         if(item){
8530             var index = this.view.indexOf(item);
8531             this.select(index, false);
8532         }
8533     },
8534
8535     // private
8536     onViewClick : function(doFocus)
8537     {
8538         var index = this.view.getSelectedIndexes()[0];
8539         var r = this.store.getAt(index);
8540         if(r){
8541             this.onSelect(r, index);
8542         }
8543         if(doFocus !== false && !this.blockFocus){
8544             this.inputEl().focus();
8545         }
8546     },
8547
8548     // private
8549     restrictHeight : function(){
8550         //this.innerList.dom.style.height = '';
8551         //var inner = this.innerList.dom;
8552         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8553         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8554         //this.list.beginUpdate();
8555         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8556         this.list.alignTo(this.inputEl(), this.listAlign);
8557         //this.list.endUpdate();
8558     },
8559
8560     // private
8561     onEmptyResults : function(){
8562         this.collapse();
8563     },
8564
8565     /**
8566      * Returns true if the dropdown list is expanded, else false.
8567      */
8568     isExpanded : function(){
8569         return this.list.isVisible();
8570     },
8571
8572     /**
8573      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8574      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8575      * @param {String} value The data value of the item to select
8576      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8577      * selected item if it is not currently in view (defaults to true)
8578      * @return {Boolean} True if the value matched an item in the list, else false
8579      */
8580     selectByValue : function(v, scrollIntoView){
8581         if(v !== undefined && v !== null){
8582             var r = this.findRecord(this.valueField || this.displayField, v);
8583             if(r){
8584                 this.select(this.store.indexOf(r), scrollIntoView);
8585                 return true;
8586             }
8587         }
8588         return false;
8589     },
8590
8591     /**
8592      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8593      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8594      * @param {Number} index The zero-based index of the list item to select
8595      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8596      * selected item if it is not currently in view (defaults to true)
8597      */
8598     select : function(index, scrollIntoView){
8599         this.selectedIndex = index;
8600         this.view.select(index);
8601         if(scrollIntoView !== false){
8602             var el = this.view.getNode(index);
8603             if(el){
8604                 //this.innerList.scrollChildIntoView(el, false);
8605                 
8606             }
8607         }
8608     },
8609
8610     // private
8611     selectNext : function(){
8612         var ct = this.store.getCount();
8613         if(ct > 0){
8614             if(this.selectedIndex == -1){
8615                 this.select(0);
8616             }else if(this.selectedIndex < ct-1){
8617                 this.select(this.selectedIndex+1);
8618             }
8619         }
8620     },
8621
8622     // private
8623     selectPrev : function(){
8624         var ct = this.store.getCount();
8625         if(ct > 0){
8626             if(this.selectedIndex == -1){
8627                 this.select(0);
8628             }else if(this.selectedIndex != 0){
8629                 this.select(this.selectedIndex-1);
8630             }
8631         }
8632     },
8633
8634     // private
8635     onKeyUp : function(e){
8636         if(this.editable !== false && !e.isSpecialKey()){
8637             this.lastKey = e.getKey();
8638             this.dqTask.delay(this.queryDelay);
8639         }
8640     },
8641
8642     // private
8643     validateBlur : function(){
8644         return !this.list || !this.list.isVisible();   
8645     },
8646
8647     // private
8648     initQuery : function(){
8649         this.doQuery(this.getRawValue());
8650     },
8651
8652     // private
8653     doForce : function(){
8654         if(this.el.dom.value.length > 0){
8655             this.el.dom.value =
8656                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8657              
8658         }
8659     },
8660
8661     /**
8662      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8663      * query allowing the query action to be canceled if needed.
8664      * @param {String} query The SQL query to execute
8665      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8666      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8667      * saved in the current store (defaults to false)
8668      */
8669     doQuery : function(q, forceAll){
8670         
8671         if(q === undefined || q === null){
8672             q = '';
8673         }
8674         var qe = {
8675             query: q,
8676             forceAll: forceAll,
8677             combo: this,
8678             cancel:false
8679         };
8680         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8681             return false;
8682         }
8683         q = qe.query;
8684         
8685         forceAll = qe.forceAll;
8686         if(forceAll === true || (q.length >= this.minChars)){
8687             
8688             this.hasQuery = true;
8689             
8690             if(this.lastQuery != q || this.alwaysQuery){
8691                 this.lastQuery = q;
8692                 if(this.mode == 'local'){
8693                     this.selectedIndex = -1;
8694                     if(forceAll){
8695                         this.store.clearFilter();
8696                     }else{
8697                         this.store.filter(this.displayField, q);
8698                     }
8699                     this.onLoad();
8700                 }else{
8701                     this.store.baseParams[this.queryParam] = q;
8702                     
8703                     var options = {params : this.getParams(q)};
8704                     
8705                     if(this.loadNext){
8706                         options.add = true;
8707                         options.params.start = this.page * this.pageSize;
8708                     }
8709                     
8710                     this.store.load(options);
8711                     this.expand();
8712                 }
8713             }else{
8714                 this.selectedIndex = -1;
8715                 this.onLoad();   
8716             }
8717         }
8718         
8719         this.loadNext = false;
8720     },
8721
8722     // private
8723     getParams : function(q){
8724         var p = {};
8725         //p[this.queryParam] = q;
8726         
8727         if(this.pageSize){
8728             p.start = 0;
8729             p.limit = this.pageSize;
8730         }
8731         return p;
8732     },
8733
8734     /**
8735      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8736      */
8737     collapse : function(){
8738         if(!this.isExpanded()){
8739             return;
8740         }
8741         
8742         this.list.hide();
8743         Roo.get(document).un('mousedown', this.collapseIf, this);
8744         Roo.get(document).un('mousewheel', this.collapseIf, this);
8745         if (!this.editable) {
8746             Roo.get(document).un('keydown', this.listKeyPress, this);
8747         }
8748         this.fireEvent('collapse', this);
8749     },
8750
8751     // private
8752     collapseIf : function(e){
8753         var in_combo  = e.within(this.el);
8754         var in_list =  e.within(this.list);
8755         
8756         if (in_combo || in_list) {
8757             //e.stopPropagation();
8758             return;
8759         }
8760
8761         this.collapse();
8762         
8763     },
8764
8765     /**
8766      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8767      */
8768     expand : function(){
8769        
8770         if(this.isExpanded() || !this.hasFocus){
8771             return;
8772         }
8773          Roo.log('expand');
8774         this.list.alignTo(this.inputEl(), this.listAlign);
8775         this.list.show();
8776         Roo.get(document).on('mousedown', this.collapseIf, this);
8777         Roo.get(document).on('mousewheel', this.collapseIf, this);
8778         if (!this.editable) {
8779             Roo.get(document).on('keydown', this.listKeyPress, this);
8780         }
8781         
8782         this.fireEvent('expand', this);
8783     },
8784
8785     // private
8786     // Implements the default empty TriggerField.onTriggerClick function
8787     onTriggerClick : function()
8788     {
8789         Roo.log('trigger click');
8790         
8791         if(this.disabled){
8792             return;
8793         }
8794         
8795         this.page = 0;
8796         this.loadNext = false;
8797         
8798         if(this.isExpanded()){
8799             this.collapse();
8800             if (!this.blockFocus) {
8801                 this.inputEl().focus();
8802             }
8803             
8804         }else {
8805             this.hasFocus = true;
8806             if(this.triggerAction == 'all') {
8807                 this.doQuery(this.allQuery, true);
8808             } else {
8809                 this.doQuery(this.getRawValue());
8810             }
8811             if (!this.blockFocus) {
8812                 this.inputEl().focus();
8813             }
8814         }
8815     },
8816     listKeyPress : function(e)
8817     {
8818         //Roo.log('listkeypress');
8819         // scroll to first matching element based on key pres..
8820         if (e.isSpecialKey()) {
8821             return false;
8822         }
8823         var k = String.fromCharCode(e.getKey()).toUpperCase();
8824         //Roo.log(k);
8825         var match  = false;
8826         var csel = this.view.getSelectedNodes();
8827         var cselitem = false;
8828         if (csel.length) {
8829             var ix = this.view.indexOf(csel[0]);
8830             cselitem  = this.store.getAt(ix);
8831             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8832                 cselitem = false;
8833             }
8834             
8835         }
8836         
8837         this.store.each(function(v) { 
8838             if (cselitem) {
8839                 // start at existing selection.
8840                 if (cselitem.id == v.id) {
8841                     cselitem = false;
8842                 }
8843                 return true;
8844             }
8845                 
8846             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8847                 match = this.store.indexOf(v);
8848                 return false;
8849             }
8850             return true;
8851         }, this);
8852         
8853         if (match === false) {
8854             return true; // no more action?
8855         }
8856         // scroll to?
8857         this.view.select(match);
8858         var sn = Roo.get(this.view.getSelectedNodes()[0])
8859         //sn.scrollIntoView(sn.dom.parentNode, false);
8860     },
8861     
8862     onViewScroll : function(e, t){
8863         
8864         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8865             return;
8866         }
8867         
8868         this.hasQuery = true;
8869         
8870         this.loading = this.list.select('.loading', true).first();
8871         
8872         if(this.loading === null){
8873             this.list.createChild({
8874                 tag: 'div',
8875                 cls: 'loading select2-more-results select2-active',
8876                 html: 'Loading more results...'
8877             })
8878             
8879             this.loading = this.list.select('.loading', true).first();
8880             
8881             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8882             
8883             this.loading.hide();
8884         }
8885         
8886         this.loading.show();
8887         
8888         var _combo = this;
8889         
8890         this.page++;
8891         this.loadNext = true;
8892         
8893         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8894         
8895         return;
8896     },
8897     
8898     addItem : function(o)
8899     {   
8900         var dv = ''; // display value
8901         
8902         if (this.displayField) {
8903             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8904         } else {
8905             // this is an error condition!!!
8906             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8907         }
8908         
8909         if(!dv.length){
8910             return;
8911         }
8912         
8913         var choice = this.choices.createChild({
8914             tag: 'li',
8915             cls: 'select2-search-choice',
8916             cn: [
8917                 {
8918                     tag: 'div',
8919                     html: dv
8920                 },
8921                 {
8922                     tag: 'a',
8923                     href: '#',
8924                     cls: 'select2-search-choice-close',
8925                     tabindex: '-1'
8926                 }
8927             ]
8928             
8929         }, this.searchField);
8930         
8931         var close = choice.select('a.select2-search-choice-close', true).first()
8932         
8933         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8934         
8935         this.item.push(o);
8936         this.lastData = o;
8937         
8938         this.syncValue();
8939         
8940         this.inputEl().dom.value = '';
8941         
8942     },
8943     
8944     onRemoveItem : function(e, _self, o)
8945     {
8946         Roo.log('remove item');
8947         var index = this.item.indexOf(o.data) * 1;
8948         
8949         if( index < 0){
8950             Roo.log('not this item?!');
8951             return;
8952         }
8953         
8954         this.item.splice(index, 1);
8955         o.item.remove();
8956         
8957         this.syncValue();
8958         
8959         this.fireEvent('remove', this);
8960         
8961     },
8962     
8963     syncValue : function()
8964     {
8965         if(!this.item.length){
8966             this.clearValue();
8967             return;
8968         }
8969             
8970         var value = [];
8971         var _this = this;
8972         Roo.each(this.item, function(i){
8973             if(_this.valueField){
8974                 value.push(i[_this.valueField]);
8975                 return;
8976             }
8977
8978             value.push(i);
8979         });
8980
8981         this.value = value.join(',');
8982
8983         if(this.hiddenField){
8984             this.hiddenField.dom.value = this.value;
8985         }
8986     },
8987     
8988     clearItem : function()
8989     {
8990         if(!this.multiple){
8991             return;
8992         }
8993         
8994         this.item = [];
8995         
8996         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8997            c.remove();
8998         });
8999         
9000         this.syncValue();
9001     }
9002     
9003     
9004
9005     /** 
9006     * @cfg {Boolean} grow 
9007     * @hide 
9008     */
9009     /** 
9010     * @cfg {Number} growMin 
9011     * @hide 
9012     */
9013     /** 
9014     * @cfg {Number} growMax 
9015     * @hide 
9016     */
9017     /**
9018      * @hide
9019      * @method autoSize
9020      */
9021 });
9022 /*
9023  * Based on:
9024  * Ext JS Library 1.1.1
9025  * Copyright(c) 2006-2007, Ext JS, LLC.
9026  *
9027  * Originally Released Under LGPL - original licence link has changed is not relivant.
9028  *
9029  * Fork - LGPL
9030  * <script type="text/javascript">
9031  */
9032
9033 /**
9034  * @class Roo.View
9035  * @extends Roo.util.Observable
9036  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9037  * This class also supports single and multi selection modes. <br>
9038  * Create a data model bound view:
9039  <pre><code>
9040  var store = new Roo.data.Store(...);
9041
9042  var view = new Roo.View({
9043     el : "my-element",
9044     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9045  
9046     singleSelect: true,
9047     selectedClass: "ydataview-selected",
9048     store: store
9049  });
9050
9051  // listen for node click?
9052  view.on("click", function(vw, index, node, e){
9053  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9054  });
9055
9056  // load XML data
9057  dataModel.load("foobar.xml");
9058  </code></pre>
9059  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9060  * <br><br>
9061  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9062  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9063  * 
9064  * Note: old style constructor is still suported (container, template, config)
9065  * 
9066  * @constructor
9067  * Create a new View
9068  * @param {Object} config The config object
9069  * 
9070  */
9071 Roo.View = function(config, depreciated_tpl, depreciated_config){
9072     
9073     if (typeof(depreciated_tpl) == 'undefined') {
9074         // new way.. - universal constructor.
9075         Roo.apply(this, config);
9076         this.el  = Roo.get(this.el);
9077     } else {
9078         // old format..
9079         this.el  = Roo.get(config);
9080         this.tpl = depreciated_tpl;
9081         Roo.apply(this, depreciated_config);
9082     }
9083     this.wrapEl  = this.el.wrap().wrap();
9084     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9085     
9086     
9087     if(typeof(this.tpl) == "string"){
9088         this.tpl = new Roo.Template(this.tpl);
9089     } else {
9090         // support xtype ctors..
9091         this.tpl = new Roo.factory(this.tpl, Roo);
9092     }
9093     
9094     
9095     this.tpl.compile();
9096    
9097   
9098     
9099      
9100     /** @private */
9101     this.addEvents({
9102         /**
9103          * @event beforeclick
9104          * Fires before a click is processed. Returns false to cancel the default action.
9105          * @param {Roo.View} this
9106          * @param {Number} index The index of the target node
9107          * @param {HTMLElement} node The target node
9108          * @param {Roo.EventObject} e The raw event object
9109          */
9110             "beforeclick" : true,
9111         /**
9112          * @event click
9113          * Fires when a template node is clicked.
9114          * @param {Roo.View} this
9115          * @param {Number} index The index of the target node
9116          * @param {HTMLElement} node The target node
9117          * @param {Roo.EventObject} e The raw event object
9118          */
9119             "click" : true,
9120         /**
9121          * @event dblclick
9122          * Fires when a template node is double clicked.
9123          * @param {Roo.View} this
9124          * @param {Number} index The index of the target node
9125          * @param {HTMLElement} node The target node
9126          * @param {Roo.EventObject} e The raw event object
9127          */
9128             "dblclick" : true,
9129         /**
9130          * @event contextmenu
9131          * Fires when a template node is right clicked.
9132          * @param {Roo.View} this
9133          * @param {Number} index The index of the target node
9134          * @param {HTMLElement} node The target node
9135          * @param {Roo.EventObject} e The raw event object
9136          */
9137             "contextmenu" : true,
9138         /**
9139          * @event selectionchange
9140          * Fires when the selected nodes change.
9141          * @param {Roo.View} this
9142          * @param {Array} selections Array of the selected nodes
9143          */
9144             "selectionchange" : true,
9145     
9146         /**
9147          * @event beforeselect
9148          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9149          * @param {Roo.View} this
9150          * @param {HTMLElement} node The node to be selected
9151          * @param {Array} selections Array of currently selected nodes
9152          */
9153             "beforeselect" : true,
9154         /**
9155          * @event preparedata
9156          * Fires on every row to render, to allow you to change the data.
9157          * @param {Roo.View} this
9158          * @param {Object} data to be rendered (change this)
9159          */
9160           "preparedata" : true
9161           
9162           
9163         });
9164
9165
9166
9167     this.el.on({
9168         "click": this.onClick,
9169         "dblclick": this.onDblClick,
9170         "contextmenu": this.onContextMenu,
9171         scope:this
9172     });
9173
9174     this.selections = [];
9175     this.nodes = [];
9176     this.cmp = new Roo.CompositeElementLite([]);
9177     if(this.store){
9178         this.store = Roo.factory(this.store, Roo.data);
9179         this.setStore(this.store, true);
9180     }
9181     
9182     if ( this.footer && this.footer.xtype) {
9183            
9184          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9185         
9186         this.footer.dataSource = this.store
9187         this.footer.container = fctr;
9188         this.footer = Roo.factory(this.footer, Roo);
9189         fctr.insertFirst(this.el);
9190         
9191         // this is a bit insane - as the paging toolbar seems to detach the el..
9192 //        dom.parentNode.parentNode.parentNode
9193          // they get detached?
9194     }
9195     
9196     
9197     Roo.View.superclass.constructor.call(this);
9198     
9199     
9200 };
9201
9202 Roo.extend(Roo.View, Roo.util.Observable, {
9203     
9204      /**
9205      * @cfg {Roo.data.Store} store Data store to load data from.
9206      */
9207     store : false,
9208     
9209     /**
9210      * @cfg {String|Roo.Element} el The container element.
9211      */
9212     el : '',
9213     
9214     /**
9215      * @cfg {String|Roo.Template} tpl The template used by this View 
9216      */
9217     tpl : false,
9218     /**
9219      * @cfg {String} dataName the named area of the template to use as the data area
9220      *                          Works with domtemplates roo-name="name"
9221      */
9222     dataName: false,
9223     /**
9224      * @cfg {String} selectedClass The css class to add to selected nodes
9225      */
9226     selectedClass : "x-view-selected",
9227      /**
9228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9229      */
9230     emptyText : "",
9231     
9232     /**
9233      * @cfg {String} text to display on mask (default Loading)
9234      */
9235     mask : false,
9236     /**
9237      * @cfg {Boolean} multiSelect Allow multiple selection
9238      */
9239     multiSelect : false,
9240     /**
9241      * @cfg {Boolean} singleSelect Allow single selection
9242      */
9243     singleSelect:  false,
9244     
9245     /**
9246      * @cfg {Boolean} toggleSelect - selecting 
9247      */
9248     toggleSelect : false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.wrapEl;
9256     },
9257     
9258     
9259
9260     /**
9261      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9262      */
9263     refresh : function(){
9264         Roo.log('refresh');
9265         var t = this.tpl;
9266         
9267         // if we are using something like 'domtemplate', then
9268         // the what gets used is:
9269         // t.applySubtemplate(NAME, data, wrapping data..)
9270         // the outer template then get' applied with
9271         //     the store 'extra data'
9272         // and the body get's added to the
9273         //      roo-name="data" node?
9274         //      <span class='roo-tpl-{name}'></span> ?????
9275         
9276         
9277         
9278         this.clearSelections();
9279         this.el.update("");
9280         var html = [];
9281         var records = this.store.getRange();
9282         if(records.length < 1) {
9283             
9284             // is this valid??  = should it render a template??
9285             
9286             this.el.update(this.emptyText);
9287             return;
9288         }
9289         var el = this.el;
9290         if (this.dataName) {
9291             this.el.update(t.apply(this.store.meta)); //????
9292             el = this.el.child('.roo-tpl-' + this.dataName);
9293         }
9294         
9295         for(var i = 0, len = records.length; i < len; i++){
9296             var data = this.prepareData(records[i].data, i, records[i]);
9297             this.fireEvent("preparedata", this, data, i, records[i]);
9298             html[html.length] = Roo.util.Format.trim(
9299                 this.dataName ?
9300                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9301                     t.apply(data)
9302             );
9303         }
9304         
9305         
9306         
9307         el.update(html.join(""));
9308         this.nodes = el.dom.childNodes;
9309         this.updateIndexes(0);
9310     },
9311     
9312
9313     /**
9314      * Function to override to reformat the data that is sent to
9315      * the template for each node.
9316      * DEPRICATED - use the preparedata event handler.
9317      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9318      * a JSON object for an UpdateManager bound view).
9319      */
9320     prepareData : function(data, index, record)
9321     {
9322         this.fireEvent("preparedata", this, data, index, record);
9323         return data;
9324     },
9325
9326     onUpdate : function(ds, record){
9327          Roo.log('on update');   
9328         this.clearSelections();
9329         var index = this.store.indexOf(record);
9330         var n = this.nodes[index];
9331         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9332         n.parentNode.removeChild(n);
9333         this.updateIndexes(index, index);
9334     },
9335
9336     
9337     
9338 // --------- FIXME     
9339     onAdd : function(ds, records, index)
9340     {
9341         Roo.log(['on Add', ds, records, index] );        
9342         this.clearSelections();
9343         if(this.nodes.length == 0){
9344             this.refresh();
9345             return;
9346         }
9347         var n = this.nodes[index];
9348         for(var i = 0, len = records.length; i < len; i++){
9349             var d = this.prepareData(records[i].data, i, records[i]);
9350             if(n){
9351                 this.tpl.insertBefore(n, d);
9352             }else{
9353                 
9354                 this.tpl.append(this.el, d);
9355             }
9356         }
9357         this.updateIndexes(index);
9358     },
9359
9360     onRemove : function(ds, record, index){
9361         Roo.log('onRemove');
9362         this.clearSelections();
9363         var el = this.dataName  ?
9364             this.el.child('.roo-tpl-' + this.dataName) :
9365             this.el; 
9366         
9367         el.dom.removeChild(this.nodes[index]);
9368         this.updateIndexes(index);
9369     },
9370
9371     /**
9372      * Refresh an individual node.
9373      * @param {Number} index
9374      */
9375     refreshNode : function(index){
9376         this.onUpdate(this.store, this.store.getAt(index));
9377     },
9378
9379     updateIndexes : function(startIndex, endIndex){
9380         var ns = this.nodes;
9381         startIndex = startIndex || 0;
9382         endIndex = endIndex || ns.length - 1;
9383         for(var i = startIndex; i <= endIndex; i++){
9384             ns[i].nodeIndex = i;
9385         }
9386     },
9387
9388     /**
9389      * Changes the data store this view uses and refresh the view.
9390      * @param {Store} store
9391      */
9392     setStore : function(store, initial){
9393         if(!initial && this.store){
9394             this.store.un("datachanged", this.refresh);
9395             this.store.un("add", this.onAdd);
9396             this.store.un("remove", this.onRemove);
9397             this.store.un("update", this.onUpdate);
9398             this.store.un("clear", this.refresh);
9399             this.store.un("beforeload", this.onBeforeLoad);
9400             this.store.un("load", this.onLoad);
9401             this.store.un("loadexception", this.onLoad);
9402         }
9403         if(store){
9404           
9405             store.on("datachanged", this.refresh, this);
9406             store.on("add", this.onAdd, this);
9407             store.on("remove", this.onRemove, this);
9408             store.on("update", this.onUpdate, this);
9409             store.on("clear", this.refresh, this);
9410             store.on("beforeload", this.onBeforeLoad, this);
9411             store.on("load", this.onLoad, this);
9412             store.on("loadexception", this.onLoad, this);
9413         }
9414         
9415         if(store){
9416             this.refresh();
9417         }
9418     },
9419     /**
9420      * onbeforeLoad - masks the loading area.
9421      *
9422      */
9423     onBeforeLoad : function(store,opts)
9424     {
9425          Roo.log('onBeforeLoad');   
9426         if (!opts.add) {
9427             this.el.update("");
9428         }
9429         this.el.mask(this.mask ? this.mask : "Loading" ); 
9430     },
9431     onLoad : function ()
9432     {
9433         this.el.unmask();
9434     },
9435     
9436
9437     /**
9438      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9439      * @param {HTMLElement} node
9440      * @return {HTMLElement} The template node
9441      */
9442     findItemFromChild : function(node){
9443         var el = this.dataName  ?
9444             this.el.child('.roo-tpl-' + this.dataName,true) :
9445             this.el.dom; 
9446         
9447         if(!node || node.parentNode == el){
9448                     return node;
9449             }
9450             var p = node.parentNode;
9451             while(p && p != el){
9452             if(p.parentNode == el){
9453                 return p;
9454             }
9455             p = p.parentNode;
9456         }
9457             return null;
9458     },
9459
9460     /** @ignore */
9461     onClick : function(e){
9462         var item = this.findItemFromChild(e.getTarget());
9463         if(item){
9464             var index = this.indexOf(item);
9465             if(this.onItemClick(item, index, e) !== false){
9466                 this.fireEvent("click", this, index, item, e);
9467             }
9468         }else{
9469             this.clearSelections();
9470         }
9471     },
9472
9473     /** @ignore */
9474     onContextMenu : function(e){
9475         var item = this.findItemFromChild(e.getTarget());
9476         if(item){
9477             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9478         }
9479     },
9480
9481     /** @ignore */
9482     onDblClick : function(e){
9483         var item = this.findItemFromChild(e.getTarget());
9484         if(item){
9485             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9486         }
9487     },
9488
9489     onItemClick : function(item, index, e)
9490     {
9491         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9492             return false;
9493         }
9494         if (this.toggleSelect) {
9495             var m = this.isSelected(item) ? 'unselect' : 'select';
9496             Roo.log(m);
9497             var _t = this;
9498             _t[m](item, true, false);
9499             return true;
9500         }
9501         if(this.multiSelect || this.singleSelect){
9502             if(this.multiSelect && e.shiftKey && this.lastSelection){
9503                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9504             }else{
9505                 this.select(item, this.multiSelect && e.ctrlKey);
9506                 this.lastSelection = item;
9507             }
9508             e.preventDefault();
9509         }
9510         return true;
9511     },
9512
9513     /**
9514      * Get the number of selected nodes.
9515      * @return {Number}
9516      */
9517     getSelectionCount : function(){
9518         return this.selections.length;
9519     },
9520
9521     /**
9522      * Get the currently selected nodes.
9523      * @return {Array} An array of HTMLElements
9524      */
9525     getSelectedNodes : function(){
9526         return this.selections;
9527     },
9528
9529     /**
9530      * Get the indexes of the selected nodes.
9531      * @return {Array}
9532      */
9533     getSelectedIndexes : function(){
9534         var indexes = [], s = this.selections;
9535         for(var i = 0, len = s.length; i < len; i++){
9536             indexes.push(s[i].nodeIndex);
9537         }
9538         return indexes;
9539     },
9540
9541     /**
9542      * Clear all selections
9543      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9544      */
9545     clearSelections : function(suppressEvent){
9546         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9547             this.cmp.elements = this.selections;
9548             this.cmp.removeClass(this.selectedClass);
9549             this.selections = [];
9550             if(!suppressEvent){
9551                 this.fireEvent("selectionchange", this, this.selections);
9552             }
9553         }
9554     },
9555
9556     /**
9557      * Returns true if the passed node is selected
9558      * @param {HTMLElement/Number} node The node or node index
9559      * @return {Boolean}
9560      */
9561     isSelected : function(node){
9562         var s = this.selections;
9563         if(s.length < 1){
9564             return false;
9565         }
9566         node = this.getNode(node);
9567         return s.indexOf(node) !== -1;
9568     },
9569
9570     /**
9571      * Selects nodes.
9572      * @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
9573      * @param {Boolean} keepExisting (optional) true to keep existing selections
9574      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9575      */
9576     select : function(nodeInfo, keepExisting, suppressEvent){
9577         if(nodeInfo instanceof Array){
9578             if(!keepExisting){
9579                 this.clearSelections(true);
9580             }
9581             for(var i = 0, len = nodeInfo.length; i < len; i++){
9582                 this.select(nodeInfo[i], true, true);
9583             }
9584             return;
9585         } 
9586         var node = this.getNode(nodeInfo);
9587         if(!node || this.isSelected(node)){
9588             return; // already selected.
9589         }
9590         if(!keepExisting){
9591             this.clearSelections(true);
9592         }
9593         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9594             Roo.fly(node).addClass(this.selectedClass);
9595             this.selections.push(node);
9596             if(!suppressEvent){
9597                 this.fireEvent("selectionchange", this, this.selections);
9598             }
9599         }
9600         
9601         
9602     },
9603       /**
9604      * Unselects nodes.
9605      * @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
9606      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9608      */
9609     unselect : function(nodeInfo, keepExisting, suppressEvent)
9610     {
9611         if(nodeInfo instanceof Array){
9612             Roo.each(this.selections, function(s) {
9613                 this.unselect(s, nodeInfo);
9614             }, this);
9615             return;
9616         }
9617         var node = this.getNode(nodeInfo);
9618         if(!node || !this.isSelected(node)){
9619             Roo.log("not selected");
9620             return; // not selected.
9621         }
9622         // fireevent???
9623         var ns = [];
9624         Roo.each(this.selections, function(s) {
9625             if (s == node ) {
9626                 Roo.fly(node).removeClass(this.selectedClass);
9627
9628                 return;
9629             }
9630             ns.push(s);
9631         },this);
9632         
9633         this.selections= ns;
9634         this.fireEvent("selectionchange", this, this.selections);
9635     },
9636
9637     /**
9638      * Gets a template node.
9639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9640      * @return {HTMLElement} The node or null if it wasn't found
9641      */
9642     getNode : function(nodeInfo){
9643         if(typeof nodeInfo == "string"){
9644             return document.getElementById(nodeInfo);
9645         }else if(typeof nodeInfo == "number"){
9646             return this.nodes[nodeInfo];
9647         }
9648         return nodeInfo;
9649     },
9650
9651     /**
9652      * Gets a range template nodes.
9653      * @param {Number} startIndex
9654      * @param {Number} endIndex
9655      * @return {Array} An array of nodes
9656      */
9657     getNodes : function(start, end){
9658         var ns = this.nodes;
9659         start = start || 0;
9660         end = typeof end == "undefined" ? ns.length - 1 : end;
9661         var nodes = [];
9662         if(start <= end){
9663             for(var i = start; i <= end; i++){
9664                 nodes.push(ns[i]);
9665             }
9666         } else{
9667             for(var i = start; i >= end; i--){
9668                 nodes.push(ns[i]);
9669             }
9670         }
9671         return nodes;
9672     },
9673
9674     /**
9675      * Finds the index of the passed node
9676      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9677      * @return {Number} The index of the node or -1
9678      */
9679     indexOf : function(node){
9680         node = this.getNode(node);
9681         if(typeof node.nodeIndex == "number"){
9682             return node.nodeIndex;
9683         }
9684         var ns = this.nodes;
9685         for(var i = 0, len = ns.length; i < len; i++){
9686             if(ns[i] == node){
9687                 return i;
9688             }
9689         }
9690         return -1;
9691     }
9692 });
9693 /*
9694  * - LGPL
9695  *
9696  * based on jquery fullcalendar
9697  * 
9698  */
9699
9700 Roo.bootstrap = Roo.bootstrap || {};
9701 /**
9702  * @class Roo.bootstrap.Calendar
9703  * @extends Roo.bootstrap.Component
9704  * Bootstrap Calendar class
9705  * @cfg {Boolean} loadMask (true|false) default false
9706     
9707  * @constructor
9708  * Create a new Container
9709  * @param {Object} config The config object
9710  */
9711
9712
9713
9714 Roo.bootstrap.Calendar = function(config){
9715     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9716      this.addEvents({
9717         /**
9718              * @event select
9719              * Fires when a date is selected
9720              * @param {DatePicker} this
9721              * @param {Date} date The selected date
9722              */
9723         'select': true,
9724         /**
9725              * @event monthchange
9726              * Fires when the displayed month changes 
9727              * @param {DatePicker} this
9728              * @param {Date} date The selected month
9729              */
9730         'monthchange': true,
9731         /**
9732              * @event evententer
9733              * Fires when mouse over an event
9734              * @param {Calendar} this
9735              * @param {event} Event
9736              */
9737         'evententer': true,
9738         /**
9739              * @event eventleave
9740              * Fires when the mouse leaves an
9741              * @param {Calendar} this
9742              * @param {event}
9743              */
9744         'eventleave': true,
9745         /**
9746              * @event eventclick
9747              * Fires when the mouse click an
9748              * @param {Calendar} this
9749              * @param {event}
9750              */
9751         'eventclick': true
9752         
9753     });
9754
9755 };
9756
9757 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9758     
9759      /**
9760      * @cfg {Number} startDay
9761      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9762      */
9763     startDay : 0,
9764     
9765     loadMask : false,
9766       
9767     getAutoCreate : function(){
9768         
9769         
9770         var fc_button = function(name, corner, style, content ) {
9771             return Roo.apply({},{
9772                 tag : 'span',
9773                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9774                          (corner.length ?
9775                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9776                             ''
9777                         ),
9778                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9779                 unselectable: 'on'
9780             });
9781         };
9782         
9783         var header = {
9784             tag : 'table',
9785             cls : 'fc-header',
9786             style : 'width:100%',
9787             cn : [
9788                 {
9789                     tag: 'tr',
9790                     cn : [
9791                         {
9792                             tag : 'td',
9793                             cls : 'fc-header-left',
9794                             cn : [
9795                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9796                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9797                                 { tag: 'span', cls: 'fc-header-space' },
9798                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9799                                 
9800                                 
9801                             ]
9802                         },
9803                         
9804                         {
9805                             tag : 'td',
9806                             cls : 'fc-header-center',
9807                             cn : [
9808                                 {
9809                                     tag: 'span',
9810                                     cls: 'fc-header-title',
9811                                     cn : {
9812                                         tag: 'H2',
9813                                         html : 'month / year'
9814                                     }
9815                                 }
9816                                 
9817                             ]
9818                         },
9819                         {
9820                             tag : 'td',
9821                             cls : 'fc-header-right',
9822                             cn : [
9823                           /*      fc_button('month', 'left', '', 'month' ),
9824                                 fc_button('week', '', '', 'week' ),
9825                                 fc_button('day', 'right', '', 'day' )
9826                             */    
9827                                 
9828                             ]
9829                         }
9830                         
9831                     ]
9832                 }
9833             ]
9834         };
9835         
9836        
9837         var cal_heads = function() {
9838             var ret = [];
9839             // fixme - handle this.
9840             
9841             for (var i =0; i < Date.dayNames.length; i++) {
9842                 var d = Date.dayNames[i];
9843                 ret.push({
9844                     tag: 'th',
9845                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9846                     html : d.substring(0,3)
9847                 });
9848                 
9849             }
9850             ret[0].cls += ' fc-first';
9851             ret[6].cls += ' fc-last';
9852             return ret;
9853         };
9854         var cal_cell = function(n) {
9855             return  {
9856                 tag: 'td',
9857                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9858                 cn : [
9859                     {
9860                         cn : [
9861                             {
9862                                 cls: 'fc-day-number',
9863                                 html: 'D'
9864                             },
9865                             {
9866                                 cls: 'fc-day-content',
9867                              
9868                                 cn : [
9869                                      {
9870                                         style: 'position: relative;' // height: 17px;
9871                                     }
9872                                 ]
9873                             }
9874                             
9875                             
9876                         ]
9877                     }
9878                 ]
9879                 
9880             }
9881         };
9882         var cal_rows = function() {
9883             
9884             var ret = []
9885             for (var r = 0; r < 6; r++) {
9886                 var row= {
9887                     tag : 'tr',
9888                     cls : 'fc-week',
9889                     cn : []
9890                 };
9891                 
9892                 for (var i =0; i < Date.dayNames.length; i++) {
9893                     var d = Date.dayNames[i];
9894                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9895
9896                 }
9897                 row.cn[0].cls+=' fc-first';
9898                 row.cn[0].cn[0].style = 'min-height:90px';
9899                 row.cn[6].cls+=' fc-last';
9900                 ret.push(row);
9901                 
9902             }
9903             ret[0].cls += ' fc-first';
9904             ret[4].cls += ' fc-prev-last';
9905             ret[5].cls += ' fc-last';
9906             return ret;
9907             
9908         };
9909         
9910         var cal_table = {
9911             tag: 'table',
9912             cls: 'fc-border-separate',
9913             style : 'width:100%',
9914             cellspacing  : 0,
9915             cn : [
9916                 { 
9917                     tag: 'thead',
9918                     cn : [
9919                         { 
9920                             tag: 'tr',
9921                             cls : 'fc-first fc-last',
9922                             cn : cal_heads()
9923                         }
9924                     ]
9925                 },
9926                 { 
9927                     tag: 'tbody',
9928                     cn : cal_rows()
9929                 }
9930                   
9931             ]
9932         };
9933          
9934          var cfg = {
9935             cls : 'fc fc-ltr',
9936             cn : [
9937                 header,
9938                 {
9939                     cls : 'fc-content',
9940                     style : "position: relative;",
9941                     cn : [
9942                         {
9943                             cls : 'fc-view fc-view-month fc-grid',
9944                             style : 'position: relative',
9945                             unselectable : 'on',
9946                             cn : [
9947                                 {
9948                                     cls : 'fc-event-container',
9949                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9950                                 },
9951                                 cal_table
9952                             ]
9953                         }
9954                     ]
9955     
9956                 }
9957            ] 
9958             
9959         };
9960         
9961          
9962         
9963         return cfg;
9964     },
9965     
9966     
9967     initEvents : function()
9968     {
9969         if(!this.store){
9970             throw "can not find store for calendar";
9971         }
9972         
9973         var mark = {
9974             tag: "div",
9975             cls:"x-dlg-mask",
9976             style: "text-align:center",
9977             cn: [
9978                 {
9979                     tag: "div",
9980                     style: "background-color:white;width:50%;margin:250 auto",
9981                     cn: [
9982                         {
9983                             tag: "img",
9984                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9985                         },
9986                         {
9987                             tag: "span",
9988                             html: "Loading"
9989                         }
9990                         
9991                     ]
9992                 }
9993             ]
9994         }
9995         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9996         
9997         var size = this.el.select('.fc-content', true).first().getSize();
9998         this.maskEl.setSize(size.width, size.height);
9999         this.maskEl.enableDisplayMode("block");
10000         if(!this.loadMask){
10001             this.maskEl.hide();
10002         }
10003         
10004         this.store = Roo.factory(this.store, Roo.data);
10005         this.store.on('load', this.onLoad, this);
10006         this.store.on('beforeload', this.onBeforeLoad, this);
10007         
10008         this.resize();
10009         
10010         this.cells = this.el.select('.fc-day',true);
10011         //Roo.log(this.cells);
10012         this.textNodes = this.el.query('.fc-day-number');
10013         this.cells.addClassOnOver('fc-state-hover');
10014         
10015         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10016         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10017         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10018         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10019         
10020         this.on('monthchange', this.onMonthChange, this);
10021         
10022         this.update(new Date().clearTime());
10023     },
10024     
10025     resize : function() {
10026         var sz  = this.el.getSize();
10027         
10028         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10029         this.el.select('.fc-day-content div',true).setHeight(34);
10030     },
10031     
10032     
10033     // private
10034     showPrevMonth : function(e){
10035         this.update(this.activeDate.add("mo", -1));
10036     },
10037     showToday : function(e){
10038         this.update(new Date().clearTime());
10039     },
10040     // private
10041     showNextMonth : function(e){
10042         this.update(this.activeDate.add("mo", 1));
10043     },
10044
10045     // private
10046     showPrevYear : function(){
10047         this.update(this.activeDate.add("y", -1));
10048     },
10049
10050     // private
10051     showNextYear : function(){
10052         this.update(this.activeDate.add("y", 1));
10053     },
10054
10055     
10056    // private
10057     update : function(date)
10058     {
10059         var vd = this.activeDate;
10060         this.activeDate = date;
10061 //        if(vd && this.el){
10062 //            var t = date.getTime();
10063 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10064 //                Roo.log('using add remove');
10065 //                
10066 //                this.fireEvent('monthchange', this, date);
10067 //                
10068 //                this.cells.removeClass("fc-state-highlight");
10069 //                this.cells.each(function(c){
10070 //                   if(c.dateValue == t){
10071 //                       c.addClass("fc-state-highlight");
10072 //                       setTimeout(function(){
10073 //                            try{c.dom.firstChild.focus();}catch(e){}
10074 //                       }, 50);
10075 //                       return false;
10076 //                   }
10077 //                   return true;
10078 //                });
10079 //                return;
10080 //            }
10081 //        }
10082         
10083         var days = date.getDaysInMonth();
10084         
10085         var firstOfMonth = date.getFirstDateOfMonth();
10086         var startingPos = firstOfMonth.getDay()-this.startDay;
10087         
10088         if(startingPos < this.startDay){
10089             startingPos += 7;
10090         }
10091         
10092         var pm = date.add(Date.MONTH, -1);
10093         var prevStart = pm.getDaysInMonth()-startingPos;
10094 //        
10095         this.cells = this.el.select('.fc-day',true);
10096         this.textNodes = this.el.query('.fc-day-number');
10097         this.cells.addClassOnOver('fc-state-hover');
10098         
10099         var cells = this.cells.elements;
10100         var textEls = this.textNodes;
10101         
10102         Roo.each(cells, function(cell){
10103             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10104         });
10105         
10106         days += startingPos;
10107
10108         // convert everything to numbers so it's fast
10109         var day = 86400000;
10110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10111         //Roo.log(d);
10112         //Roo.log(pm);
10113         //Roo.log(prevStart);
10114         
10115         var today = new Date().clearTime().getTime();
10116         var sel = date.clearTime().getTime();
10117         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10118         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10119         var ddMatch = this.disabledDatesRE;
10120         var ddText = this.disabledDatesText;
10121         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10122         var ddaysText = this.disabledDaysText;
10123         var format = this.format;
10124         
10125         var setCellClass = function(cal, cell){
10126             
10127             //Roo.log('set Cell Class');
10128             cell.title = "";
10129             var t = d.getTime();
10130             
10131             //Roo.log(d);
10132             
10133             cell.dateValue = t;
10134             if(t == today){
10135                 cell.className += " fc-today";
10136                 cell.className += " fc-state-highlight";
10137                 cell.title = cal.todayText;
10138             }
10139             if(t == sel){
10140                 // disable highlight in other month..
10141                 //cell.className += " fc-state-highlight";
10142                 
10143             }
10144             // disabling
10145             if(t < min) {
10146                 cell.className = " fc-state-disabled";
10147                 cell.title = cal.minText;
10148                 return;
10149             }
10150             if(t > max) {
10151                 cell.className = " fc-state-disabled";
10152                 cell.title = cal.maxText;
10153                 return;
10154             }
10155             if(ddays){
10156                 if(ddays.indexOf(d.getDay()) != -1){
10157                     cell.title = ddaysText;
10158                     cell.className = " fc-state-disabled";
10159                 }
10160             }
10161             if(ddMatch && format){
10162                 var fvalue = d.dateFormat(format);
10163                 if(ddMatch.test(fvalue)){
10164                     cell.title = ddText.replace("%0", fvalue);
10165                     cell.className = " fc-state-disabled";
10166                 }
10167             }
10168             
10169             if (!cell.initialClassName) {
10170                 cell.initialClassName = cell.dom.className;
10171             }
10172             
10173             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10174         };
10175
10176         var i = 0;
10177         
10178         for(; i < startingPos; i++) {
10179             textEls[i].innerHTML = (++prevStart);
10180             d.setDate(d.getDate()+1);
10181             
10182             cells[i].className = "fc-past fc-other-month";
10183             setCellClass(this, cells[i]);
10184         }
10185         
10186         var intDay = 0;
10187         
10188         for(; i < days; i++){
10189             intDay = i - startingPos + 1;
10190             textEls[i].innerHTML = (intDay);
10191             d.setDate(d.getDate()+1);
10192             
10193             cells[i].className = ''; // "x-date-active";
10194             setCellClass(this, cells[i]);
10195         }
10196         var extraDays = 0;
10197         
10198         for(; i < 42; i++) {
10199             textEls[i].innerHTML = (++extraDays);
10200             d.setDate(d.getDate()+1);
10201             
10202             cells[i].className = "fc-future fc-other-month";
10203             setCellClass(this, cells[i]);
10204         }
10205         
10206         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10207         
10208         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10209         
10210         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10211         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10212         
10213         if(totalRows != 6){
10214             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10215             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10216         }
10217         
10218         this.fireEvent('monthchange', this, date);
10219         
10220         
10221         /*
10222         if(!this.internalRender){
10223             var main = this.el.dom.firstChild;
10224             var w = main.offsetWidth;
10225             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10226             Roo.fly(main).setWidth(w);
10227             this.internalRender = true;
10228             // opera does not respect the auto grow header center column
10229             // then, after it gets a width opera refuses to recalculate
10230             // without a second pass
10231             if(Roo.isOpera && !this.secondPass){
10232                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10233                 this.secondPass = true;
10234                 this.update.defer(10, this, [date]);
10235             }
10236         }
10237         */
10238         
10239     },
10240     
10241     findCell : function(dt) {
10242         dt = dt.clearTime().getTime();
10243         var ret = false;
10244         this.cells.each(function(c){
10245             //Roo.log("check " +c.dateValue + '?=' + dt);
10246             if(c.dateValue == dt){
10247                 ret = c;
10248                 return false;
10249             }
10250             return true;
10251         });
10252         
10253         return ret;
10254     },
10255     
10256     findCells : function(ev) {
10257         var s = ev.start.clone().clearTime().getTime();
10258        // Roo.log(s);
10259         var e= ev.end.clone().clearTime().getTime();
10260        // Roo.log(e);
10261         var ret = [];
10262         this.cells.each(function(c){
10263              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10264             
10265             if(c.dateValue > e){
10266                 return ;
10267             }
10268             if(c.dateValue < s){
10269                 return ;
10270             }
10271             ret.push(c);
10272         });
10273         
10274         return ret;    
10275     },
10276     
10277     findBestRow: function(cells)
10278     {
10279         var ret = 0;
10280         
10281         for (var i =0 ; i < cells.length;i++) {
10282             ret  = Math.max(cells[i].rows || 0,ret);
10283         }
10284         return ret;
10285         
10286     },
10287     
10288     
10289     addItem : function(ev)
10290     {
10291         // look for vertical location slot in
10292         var cells = this.findCells(ev);
10293         
10294         ev.row = this.findBestRow(cells);
10295         
10296         // work out the location.
10297         
10298         var crow = false;
10299         var rows = [];
10300         for(var i =0; i < cells.length; i++) {
10301             if (!crow) {
10302                 crow = {
10303                     start : cells[i],
10304                     end :  cells[i]
10305                 };
10306                 continue;
10307             }
10308             if (crow.start.getY() == cells[i].getY()) {
10309                 // on same row.
10310                 crow.end = cells[i];
10311                 continue;
10312             }
10313             // different row.
10314             rows.push(crow);
10315             crow = {
10316                 start: cells[i],
10317                 end : cells[i]
10318             };
10319             
10320         }
10321         
10322         rows.push(crow);
10323         ev.els = [];
10324         ev.rows = rows;
10325         ev.cells = cells;
10326         for (var i = 0; i < cells.length;i++) {
10327             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10328             
10329         }
10330         
10331         this.calevents.push(ev);
10332     },
10333     
10334     clearEvents: function() {
10335         
10336         if(!this.calevents){
10337             return;
10338         }
10339         
10340         Roo.each(this.cells.elements, function(c){
10341             c.rows = 0;
10342         });
10343         
10344         Roo.each(this.calevents, function(e) {
10345             Roo.each(e.els, function(el) {
10346                 el.un('mouseenter' ,this.onEventEnter, this);
10347                 el.un('mouseleave' ,this.onEventLeave, this);
10348                 el.remove();
10349             },this);
10350         },this);
10351         
10352     },
10353     
10354     renderEvents: function()
10355     {   
10356         // first make sure there is enough space..
10357         
10358         this.cells.each(function(c) {
10359         
10360             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10361         });
10362         
10363         for (var e = 0; e < this.calevents.length; e++) {
10364             var ev = this.calevents[e];
10365             var cells = ev.cells;
10366             var rows = ev.rows;
10367             
10368             for(var i =0; i < rows.length; i++) {
10369                 
10370                  
10371                 // how many rows should it span..
10372                 
10373                 var  cfg = {
10374                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10375                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10376                     
10377                     unselectable : "on",
10378                     cn : [
10379                         {
10380                             cls: 'fc-event-inner',
10381                             cn : [
10382 //                                {
10383 //                                  tag:'span',
10384 //                                  cls: 'fc-event-time',
10385 //                                  html : cells.length > 1 ? '' : ev.time
10386 //                                },
10387                                 {
10388                                   tag:'span',
10389                                   cls: 'fc-event-title',
10390                                   html : String.format('{0}', ev.title)
10391                                 }
10392                                 
10393                                 
10394                             ]
10395                         },
10396                         {
10397                             cls: 'ui-resizable-handle ui-resizable-e',
10398                             html : '&nbsp;&nbsp;&nbsp'
10399                         }
10400                         
10401                     ]
10402                 };
10403                 if (i == 0) {
10404                     cfg.cls += ' fc-event-start';
10405                 }
10406                 if ((i+1) == rows.length) {
10407                     cfg.cls += ' fc-event-end';
10408                 }
10409                 
10410                 var ctr = this.el.select('.fc-event-container',true).first();
10411                 var cg = ctr.createChild(cfg);
10412                 
10413                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10414                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10415                 cg.on('click', this.onEventClick, this, ev);
10416                 
10417                 ev.els.push(cg);
10418                 
10419                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10420                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10421                 //Roo.log(cg);
10422                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10423                 cg.setWidth(ebox.right - sbox.x -2);
10424             }
10425             
10426             
10427         }
10428         
10429     },
10430     
10431     onEventEnter: function (e, el,event,d) {
10432         this.fireEvent('evententer', this, el, event);
10433     },
10434     
10435     onEventLeave: function (e, el,event,d) {
10436         this.fireEvent('eventleave', this, el, event);
10437     },
10438     
10439     onEventClick: function (e, el,event,d) {
10440         this.fireEvent('eventclick', this, el, event);
10441     },
10442     
10443     onMonthChange: function () {
10444         this.store.load();
10445     },
10446     
10447     onLoad: function () 
10448     {   
10449         this.calevents = [];
10450         var cal = this;
10451         
10452         if(this.store.getCount() > 0){
10453             this.store.data.each(function(d){
10454                cal.addItem({
10455                     id : d.data.id,
10456                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10457                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10458                     time : d.data.start_time,
10459                     title : d.data.title,
10460                     description : d.data.description,
10461                     venue : d.data.venue
10462                 });
10463             });
10464         }
10465         
10466         this.renderEvents();
10467         
10468         if(this.loadMask){
10469             this.maskEl.hide();
10470         }
10471     },
10472     
10473     onBeforeLoad: function()
10474     {
10475         this.clearEvents();
10476         
10477         if(this.loadMask){
10478             this.maskEl.show();
10479         }
10480     }
10481 });
10482
10483  
10484  /*
10485  * - LGPL
10486  *
10487  * element
10488  * 
10489  */
10490
10491 /**
10492  * @class Roo.bootstrap.Popover
10493  * @extends Roo.bootstrap.Component
10494  * Bootstrap Popover class
10495  * @cfg {String} html contents of the popover   (or false to use children..)
10496  * @cfg {String} title of popover (or false to hide)
10497  * @cfg {String} placement how it is placed
10498  * @cfg {String} trigger click || hover (or false to trigger manually)
10499  * @cfg {String} over what (parent or false to trigger manually.)
10500  * 
10501  * @constructor
10502  * Create a new Popover
10503  * @param {Object} config The config object
10504  */
10505
10506 Roo.bootstrap.Popover = function(config){
10507     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10508 };
10509
10510 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10511     
10512     title: 'Fill in a title',
10513     html: false,
10514     
10515     placement : 'right',
10516     trigger : 'hover', // hover
10517     
10518     over: 'parent',
10519     
10520     can_build_overlaid : false,
10521     
10522     getChildContainer : function()
10523     {
10524         return this.el.select('.popover-content',true).first();
10525     },
10526     
10527     getAutoCreate : function(){
10528          Roo.log('make popover?');
10529         var cfg = {
10530            cls : 'popover roo-dynamic',
10531            style: 'display:block',
10532            cn : [
10533                 {
10534                     cls : 'arrow'
10535                 },
10536                 {
10537                     cls : 'popover-inner',
10538                     cn : [
10539                         {
10540                             tag: 'h3',
10541                             cls: 'popover-title',
10542                             html : this.title
10543                         },
10544                         {
10545                             cls : 'popover-content',
10546                             html : this.html
10547                         }
10548                     ]
10549                     
10550                 }
10551            ]
10552         };
10553         
10554         return cfg;
10555     },
10556     setTitle: function(str)
10557     {
10558         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10559     },
10560     setContent: function(str)
10561     {
10562         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10563     },
10564     // as it get's added to the bottom of the page.
10565     onRender : function(ct, position)
10566     {
10567         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10568         if(!this.el){
10569             var cfg = Roo.apply({},  this.getAutoCreate());
10570             cfg.id = Roo.id();
10571             
10572             if (this.cls) {
10573                 cfg.cls += ' ' + this.cls;
10574             }
10575             if (this.style) {
10576                 cfg.style = this.style;
10577             }
10578             Roo.log("adding to ")
10579             this.el = Roo.get(document.body).createChild(cfg, position);
10580             Roo.log(this.el);
10581         }
10582         this.initEvents();
10583     },
10584     
10585     initEvents : function()
10586     {
10587         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10588         this.el.enableDisplayMode('block');
10589         this.el.hide();
10590         if (this.over === false) {
10591             return; 
10592         }
10593         if (this.triggers === false) {
10594             return;
10595         }
10596         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10597         var triggers = this.trigger ? this.trigger.split(' ') : [];
10598         Roo.each(triggers, function(trigger) {
10599         
10600             if (trigger == 'click') {
10601                 on_el.on('click', this.toggle, this);
10602             } else if (trigger != 'manual') {
10603                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10604                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10605       
10606                 on_el.on(eventIn  ,this.enter, this);
10607                 on_el.on(eventOut, this.leave, this);
10608             }
10609         }, this);
10610         
10611     },
10612     
10613     
10614     // private
10615     timeout : null,
10616     hoverState : null,
10617     
10618     toggle : function () {
10619         this.hoverState == 'in' ? this.leave() : this.enter();
10620     },
10621     
10622     enter : function () {
10623        
10624     
10625         clearTimeout(this.timeout);
10626     
10627         this.hoverState = 'in'
10628     
10629         if (!this.delay || !this.delay.show) {
10630             this.show();
10631             return 
10632         }
10633         var _t = this;
10634         this.timeout = setTimeout(function () {
10635             if (_t.hoverState == 'in') {
10636                 _t.show();
10637             }
10638         }, this.delay.show)
10639     },
10640     leave : function() {
10641         clearTimeout(this.timeout);
10642     
10643         this.hoverState = 'out'
10644     
10645         if (!this.delay || !this.delay.hide) {
10646             this.hide();
10647             return 
10648         }
10649         var _t = this;
10650         this.timeout = setTimeout(function () {
10651             if (_t.hoverState == 'out') {
10652                 _t.hide();
10653             }
10654         }, this.delay.hide)
10655     },
10656     
10657     show : function (on_el)
10658     {
10659         if (!on_el) {
10660             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10661         }
10662         // set content.
10663         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10664         if (this.html !== false) {
10665             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10666         }
10667         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10668         if (!this.title.length) {
10669             this.el.select('.popover-title',true).hide();
10670         }
10671         
10672         var placement = typeof this.placement == 'function' ?
10673             this.placement.call(this, this.el, on_el) :
10674             this.placement;
10675             
10676         var autoToken = /\s?auto?\s?/i;
10677         var autoPlace = autoToken.test(placement);
10678         if (autoPlace) {
10679             placement = placement.replace(autoToken, '') || 'top';
10680         }
10681         
10682         //this.el.detach()
10683         //this.el.setXY([0,0]);
10684         this.el.show();
10685         this.el.dom.style.display='block';
10686         this.el.addClass(placement);
10687         
10688         //this.el.appendTo(on_el);
10689         
10690         var p = this.getPosition();
10691         var box = this.el.getBox();
10692         
10693         if (autoPlace) {
10694             // fixme..
10695         }
10696         var align = Roo.bootstrap.Popover.alignment[placement]
10697         this.el.alignTo(on_el, align[0],align[1]);
10698         //var arrow = this.el.select('.arrow',true).first();
10699         //arrow.set(align[2], 
10700         
10701         this.el.addClass('in');
10702         this.hoverState = null;
10703         
10704         if (this.el.hasClass('fade')) {
10705             // fade it?
10706         }
10707         
10708     },
10709     hide : function()
10710     {
10711         this.el.setXY([0,0]);
10712         this.el.removeClass('in');
10713         this.el.hide();
10714         
10715     }
10716     
10717 });
10718
10719 Roo.bootstrap.Popover.alignment = {
10720     'left' : ['r-l', [-10,0], 'right'],
10721     'right' : ['l-r', [10,0], 'left'],
10722     'bottom' : ['t-b', [0,10], 'top'],
10723     'top' : [ 'b-t', [0,-10], 'bottom']
10724 };
10725
10726  /*
10727  * - LGPL
10728  *
10729  * Progress
10730  * 
10731  */
10732
10733 /**
10734  * @class Roo.bootstrap.Progress
10735  * @extends Roo.bootstrap.Component
10736  * Bootstrap Progress class
10737  * @cfg {Boolean} striped striped of the progress bar
10738  * @cfg {Boolean} active animated of the progress bar
10739  * 
10740  * 
10741  * @constructor
10742  * Create a new Progress
10743  * @param {Object} config The config object
10744  */
10745
10746 Roo.bootstrap.Progress = function(config){
10747     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10748 };
10749
10750 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10751     
10752     striped : false,
10753     active: false,
10754     
10755     getAutoCreate : function(){
10756         var cfg = {
10757             tag: 'div',
10758             cls: 'progress'
10759         };
10760         
10761         
10762         if(this.striped){
10763             cfg.cls += ' progress-striped';
10764         }
10765       
10766         if(this.active){
10767             cfg.cls += ' active';
10768         }
10769         
10770         
10771         return cfg;
10772     }
10773    
10774 });
10775
10776  
10777
10778  /*
10779  * - LGPL
10780  *
10781  * ProgressBar
10782  * 
10783  */
10784
10785 /**
10786  * @class Roo.bootstrap.ProgressBar
10787  * @extends Roo.bootstrap.Component
10788  * Bootstrap ProgressBar class
10789  * @cfg {Number} aria_valuenow aria-value now
10790  * @cfg {Number} aria_valuemin aria-value min
10791  * @cfg {Number} aria_valuemax aria-value max
10792  * @cfg {String} label label for the progress bar
10793  * @cfg {String} panel (success | info | warning | danger )
10794  * @cfg {String} role role of the progress bar
10795  * @cfg {String} sr_only text
10796  * 
10797  * 
10798  * @constructor
10799  * Create a new ProgressBar
10800  * @param {Object} config The config object
10801  */
10802
10803 Roo.bootstrap.ProgressBar = function(config){
10804     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10805 };
10806
10807 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10808     
10809     aria_valuenow : 0,
10810     aria_valuemin : 0,
10811     aria_valuemax : 100,
10812     label : false,
10813     panel : false,
10814     role : false,
10815     sr_only: false,
10816     
10817     getAutoCreate : function()
10818     {
10819         
10820         var cfg = {
10821             tag: 'div',
10822             cls: 'progress-bar',
10823             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10824         };
10825         
10826         if(this.sr_only){
10827             cfg.cn = {
10828                 tag: 'span',
10829                 cls: 'sr-only',
10830                 html: this.sr_only
10831             }
10832         }
10833         
10834         if(this.role){
10835             cfg.role = this.role;
10836         }
10837         
10838         if(this.aria_valuenow){
10839             cfg['aria-valuenow'] = this.aria_valuenow;
10840         }
10841         
10842         if(this.aria_valuemin){
10843             cfg['aria-valuemin'] = this.aria_valuemin;
10844         }
10845         
10846         if(this.aria_valuemax){
10847             cfg['aria-valuemax'] = this.aria_valuemax;
10848         }
10849         
10850         if(this.label && !this.sr_only){
10851             cfg.html = this.label;
10852         }
10853         
10854         if(this.panel){
10855             cfg.cls += ' progress-bar-' + this.panel;
10856         }
10857         
10858         return cfg;
10859     },
10860     
10861     update : function(aria_valuenow)
10862     {
10863         this.aria_valuenow = aria_valuenow;
10864         
10865         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10866     }
10867    
10868 });
10869
10870  
10871
10872  /*
10873  * - LGPL
10874  *
10875  * TabPanel
10876  * 
10877  */
10878
10879 /**
10880  * @class Roo.bootstrap.TabPanel
10881  * @extends Roo.bootstrap.Component
10882  * Bootstrap TabPanel class
10883  * @cfg {Boolean} active panel active
10884  * @cfg {String} html panel content
10885  * @cfg {String} tabId tab relate id
10886  * 
10887  * 
10888  * @constructor
10889  * Create a new TabPanel
10890  * @param {Object} config The config object
10891  */
10892
10893 Roo.bootstrap.TabPanel = function(config){
10894     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10895 };
10896
10897 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10898     
10899     active: false,
10900     html: false,
10901     tabId: false,
10902     
10903     getAutoCreate : function(){
10904         var cfg = {
10905             tag: 'div',
10906             cls: 'tab-pane',
10907             html: this.html || ''
10908         };
10909         
10910         if(this.active){
10911             cfg.cls += ' active';
10912         }
10913         
10914         if(this.tabId){
10915             cfg.tabId = this.tabId;
10916         }
10917         
10918         return cfg;
10919     }
10920    
10921 });
10922
10923  
10924
10925  /*
10926  * - LGPL
10927  *
10928  * DateField
10929  * 
10930  */
10931
10932 /**
10933  * @class Roo.bootstrap.DateField
10934  * @extends Roo.bootstrap.Input
10935  * Bootstrap DateField class
10936  * @cfg {Number} weekStart default 0
10937  * @cfg {Number} weekStart default 0
10938  * @cfg {Number} viewMode default empty, (months|years)
10939  * @cfg {Number} minViewMode default empty, (months|years)
10940  * @cfg {Number} startDate default -Infinity
10941  * @cfg {Number} endDate default Infinity
10942  * @cfg {Boolean} todayHighlight default false
10943  * @cfg {Boolean} todayBtn default false
10944  * @cfg {Boolean} calendarWeeks default false
10945  * @cfg {Object} daysOfWeekDisabled default empty
10946  * 
10947  * @cfg {Boolean} keyboardNavigation default true
10948  * @cfg {String} language default en
10949  * 
10950  * @constructor
10951  * Create a new DateField
10952  * @param {Object} config The config object
10953  */
10954
10955 Roo.bootstrap.DateField = function(config){
10956     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10957      this.addEvents({
10958             /**
10959              * @event show
10960              * Fires when this field show.
10961              * @param {Roo.bootstrap.DateField} this
10962              * @param {Mixed} date The date value
10963              */
10964             show : true,
10965             /**
10966              * @event show
10967              * Fires when this field hide.
10968              * @param {Roo.bootstrap.DateField} this
10969              * @param {Mixed} date The date value
10970              */
10971             hide : true,
10972             /**
10973              * @event select
10974              * Fires when select a date.
10975              * @param {Roo.bootstrap.DateField} this
10976              * @param {Mixed} date The date value
10977              */
10978             select : true
10979         });
10980 };
10981
10982 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10983     
10984     /**
10985      * @cfg {String} format
10986      * The default date format string which can be overriden for localization support.  The format must be
10987      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10988      */
10989     format : "m/d/y",
10990     /**
10991      * @cfg {String} altFormats
10992      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10993      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10994      */
10995     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10996     
10997     weekStart : 0,
10998     
10999     viewMode : '',
11000     
11001     minViewMode : '',
11002     
11003     todayHighlight : false,
11004     
11005     todayBtn: false,
11006     
11007     language: 'en',
11008     
11009     keyboardNavigation: true,
11010     
11011     calendarWeeks: false,
11012     
11013     startDate: -Infinity,
11014     
11015     endDate: Infinity,
11016     
11017     daysOfWeekDisabled: [],
11018     
11019     _events: [],
11020     
11021     UTCDate: function()
11022     {
11023         return new Date(Date.UTC.apply(Date, arguments));
11024     },
11025     
11026     UTCToday: function()
11027     {
11028         var today = new Date();
11029         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11030     },
11031     
11032     getDate: function() {
11033             var d = this.getUTCDate();
11034             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11035     },
11036     
11037     getUTCDate: function() {
11038             return this.date;
11039     },
11040     
11041     setDate: function(d) {
11042             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11043     },
11044     
11045     setUTCDate: function(d) {
11046             this.date = d;
11047             this.setValue(this.formatDate(this.date));
11048     },
11049         
11050     onRender: function(ct, position)
11051     {
11052         
11053         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11054         
11055         this.language = this.language || 'en';
11056         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11057         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11058         
11059         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11060         this.format = this.format || 'm/d/y';
11061         this.isInline = false;
11062         this.isInput = true;
11063         this.component = this.el.select('.add-on', true).first() || false;
11064         this.component = (this.component && this.component.length === 0) ? false : this.component;
11065         this.hasInput = this.component && this.inputEL().length;
11066         
11067         if (typeof(this.minViewMode === 'string')) {
11068             switch (this.minViewMode) {
11069                 case 'months':
11070                     this.minViewMode = 1;
11071                     break;
11072                 case 'years':
11073                     this.minViewMode = 2;
11074                     break;
11075                 default:
11076                     this.minViewMode = 0;
11077                     break;
11078             }
11079         }
11080         
11081         if (typeof(this.viewMode === 'string')) {
11082             switch (this.viewMode) {
11083                 case 'months':
11084                     this.viewMode = 1;
11085                     break;
11086                 case 'years':
11087                     this.viewMode = 2;
11088                     break;
11089                 default:
11090                     this.viewMode = 0;
11091                     break;
11092             }
11093         }
11094                 
11095         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11096         
11097         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11098         
11099         this.picker().on('mousedown', this.onMousedown, this);
11100         this.picker().on('click', this.onClick, this);
11101         
11102         this.picker().addClass('datepicker-dropdown');
11103         
11104         this.startViewMode = this.viewMode;
11105         
11106         
11107         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11108             if(!this.calendarWeeks){
11109                 v.remove();
11110                 return;
11111             };
11112             
11113             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11114             v.attr('colspan', function(i, val){
11115                 return parseInt(val) + 1;
11116             });
11117         })
11118                         
11119         
11120         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11121         
11122         this.setStartDate(this.startDate);
11123         this.setEndDate(this.endDate);
11124         
11125         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11126         
11127         this.fillDow();
11128         this.fillMonths();
11129         this.update();
11130         this.showMode();
11131         
11132         if(this.isInline) {
11133             this.show();
11134         }
11135     },
11136     
11137     picker : function()
11138     {
11139         return this.el.select('.datepicker', true).first();
11140     },
11141     
11142     fillDow: function()
11143     {
11144         var dowCnt = this.weekStart;
11145         
11146         var dow = {
11147             tag: 'tr',
11148             cn: [
11149                 
11150             ]
11151         };
11152         
11153         if(this.calendarWeeks){
11154             dow.cn.push({
11155                 tag: 'th',
11156                 cls: 'cw',
11157                 html: '&nbsp;'
11158             })
11159         }
11160         
11161         while (dowCnt < this.weekStart + 7) {
11162             dow.cn.push({
11163                 tag: 'th',
11164                 cls: 'dow',
11165                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11166             });
11167         }
11168         
11169         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11170     },
11171     
11172     fillMonths: function()
11173     {    
11174         var i = 0
11175         var months = this.picker().select('>.datepicker-months td', true).first();
11176         
11177         months.dom.innerHTML = '';
11178         
11179         while (i < 12) {
11180             var month = {
11181                 tag: 'span',
11182                 cls: 'month',
11183                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11184             }
11185             
11186             months.createChild(month);
11187         }
11188         
11189     },
11190     
11191     update: function(){
11192         
11193         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11194         
11195         if (this.date < this.startDate) {
11196             this.viewDate = new Date(this.startDate);
11197         } else if (this.date > this.endDate) {
11198             this.viewDate = new Date(this.endDate);
11199         } else {
11200             this.viewDate = new Date(this.date);
11201         }
11202         
11203         this.fill();
11204     },
11205     
11206     fill: function() {
11207         var d = new Date(this.viewDate),
11208                 year = d.getUTCFullYear(),
11209                 month = d.getUTCMonth(),
11210                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11211                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11212                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11213                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11214                 currentDate = this.date && this.date.valueOf(),
11215                 today = this.UTCToday();
11216         
11217         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11218         
11219 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11220         
11221 //        this.picker.select('>tfoot th.today').
11222 //                                              .text(dates[this.language].today)
11223 //                                              .toggle(this.todayBtn !== false);
11224     
11225         this.updateNavArrows();
11226         this.fillMonths();
11227                                                 
11228         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11229         
11230         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11231          
11232         prevMonth.setUTCDate(day);
11233         
11234         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11235         
11236         var nextMonth = new Date(prevMonth);
11237         
11238         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11239         
11240         nextMonth = nextMonth.valueOf();
11241         
11242         var fillMonths = false;
11243         
11244         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11245         
11246         while(prevMonth.valueOf() < nextMonth) {
11247             var clsName = '';
11248             
11249             if (prevMonth.getUTCDay() === this.weekStart) {
11250                 if(fillMonths){
11251                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11252                 }
11253                     
11254                 fillMonths = {
11255                     tag: 'tr',
11256                     cn: []
11257                 };
11258                 
11259                 if(this.calendarWeeks){
11260                     // ISO 8601: First week contains first thursday.
11261                     // ISO also states week starts on Monday, but we can be more abstract here.
11262                     var
11263                     // Start of current week: based on weekstart/current date
11264                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11265                     // Thursday of this week
11266                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11267                     // First Thursday of year, year from thursday
11268                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11269                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11270                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11271                     
11272                     fillMonths.cn.push({
11273                         tag: 'td',
11274                         cls: 'cw',
11275                         html: calWeek
11276                     });
11277                 }
11278             }
11279             
11280             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11281                 clsName += ' old';
11282             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11283                 clsName += ' new';
11284             }
11285             if (this.todayHighlight &&
11286                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11287                 prevMonth.getUTCMonth() == today.getMonth() &&
11288                 prevMonth.getUTCDate() == today.getDate()) {
11289                 clsName += ' today';
11290             }
11291             
11292             if (currentDate && prevMonth.valueOf() === currentDate) {
11293                 clsName += ' active';
11294             }
11295             
11296             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11297                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11298                     clsName += ' disabled';
11299             }
11300             
11301             fillMonths.cn.push({
11302                 tag: 'td',
11303                 cls: 'day ' + clsName,
11304                 html: prevMonth.getDate()
11305             })
11306             
11307             prevMonth.setDate(prevMonth.getDate()+1);
11308         }
11309           
11310         var currentYear = this.date && this.date.getUTCFullYear();
11311         var currentMonth = this.date && this.date.getUTCMonth();
11312         
11313         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11314         
11315         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11316             v.removeClass('active');
11317             
11318             if(currentYear === year && k === currentMonth){
11319                 v.addClass('active');
11320             }
11321             
11322             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11323                 v.addClass('disabled');
11324             }
11325             
11326         });
11327         
11328         
11329         year = parseInt(year/10, 10) * 10;
11330         
11331         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11332         
11333         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11334         
11335         year -= 1;
11336         for (var i = -1; i < 11; i++) {
11337             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11338                 tag: 'span',
11339                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11340                 html: year
11341             })
11342             
11343             year += 1;
11344         }
11345     },
11346     
11347     showMode: function(dir) {
11348         if (dir) {
11349             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11350         }
11351         Roo.each(this.picker().select('>div',true).elements, function(v){
11352             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11353             v.hide();
11354         });
11355         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11356     },
11357     
11358     place: function()
11359     {
11360         if(this.isInline) return;
11361         
11362         this.picker().removeClass(['bottom', 'top']);
11363         
11364         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11365             /*
11366              * place to the top of element!
11367              *
11368              */
11369             
11370             this.picker().addClass('top');
11371             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11372             
11373             return;
11374         }
11375         
11376         this.picker().addClass('bottom');
11377         
11378         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11379     },
11380     
11381     parseDate : function(value){
11382         if(!value || value instanceof Date){
11383             return value;
11384         }
11385         var v = Date.parseDate(value, this.format);
11386         if (!v && this.useIso) {
11387             v = Date.parseDate(value, 'Y-m-d');
11388         }
11389         if(!v && this.altFormats){
11390             if(!this.altFormatsArray){
11391                 this.altFormatsArray = this.altFormats.split("|");
11392             }
11393             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11394                 v = Date.parseDate(value, this.altFormatsArray[i]);
11395             }
11396         }
11397         return v;
11398     },
11399     
11400     formatDate : function(date, fmt){
11401         return (!date || !(date instanceof Date)) ?
11402         date : date.dateFormat(fmt || this.format);
11403     },
11404     
11405     onFocus : function()
11406     {
11407         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11408         this.show();
11409     },
11410     
11411     onBlur : function()
11412     {
11413         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11414         this.hide();
11415     },
11416     
11417     show : function()
11418     {
11419         this.picker().show();
11420         this.update();
11421         this.place();
11422         
11423         this.fireEvent('show', this, this.date);
11424     },
11425     
11426     hide : function()
11427     {
11428         if(this.isInline) return;
11429         this.picker().hide();
11430         this.viewMode = this.startViewMode;
11431         this.showMode();
11432         
11433         this.fireEvent('hide', this, this.date);
11434         
11435     },
11436     
11437     onMousedown: function(e){
11438         e.stopPropagation();
11439         e.preventDefault();
11440     },
11441     
11442     keyup: function(e){
11443         Roo.bootstrap.DateField.superclass.keyup.call(this);
11444         this.update();
11445         
11446     },
11447
11448     setValue: function(v){
11449         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11450         
11451         this.fireEvent('select', this, this.date);
11452         
11453     },
11454     
11455     fireKey: function(e){
11456         if (!this.picker().isVisible()){
11457             if (e.keyCode == 27) // allow escape to hide and re-show picker
11458                 this.show();
11459             return;
11460         }
11461         var dateChanged = false,
11462         dir, day, month,
11463         newDate, newViewDate;
11464         switch(e.keyCode){
11465             case 27: // escape
11466                 this.hide();
11467                 e.preventDefault();
11468                 break;
11469             case 37: // left
11470             case 39: // right
11471                 if (!this.keyboardNavigation) break;
11472                 dir = e.keyCode == 37 ? -1 : 1;
11473                 
11474                 if (e.ctrlKey){
11475                     newDate = this.moveYear(this.date, dir);
11476                     newViewDate = this.moveYear(this.viewDate, dir);
11477                 } else if (e.shiftKey){
11478                     newDate = this.moveMonth(this.date, dir);
11479                     newViewDate = this.moveMonth(this.viewDate, dir);
11480                 } else {
11481                     newDate = new Date(this.date);
11482                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11483                     newViewDate = new Date(this.viewDate);
11484                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11485                 }
11486                 if (this.dateWithinRange(newDate)){
11487                     this.date = newDate;
11488                     this.viewDate = newViewDate;
11489                     this.setValue(this.formatDate(this.date));
11490                     this.update();
11491                     e.preventDefault();
11492                     dateChanged = true;
11493                 }
11494                 break;
11495             case 38: // up
11496             case 40: // down
11497                 if (!this.keyboardNavigation) break;
11498                 dir = e.keyCode == 38 ? -1 : 1;
11499                 if (e.ctrlKey){
11500                     newDate = this.moveYear(this.date, dir);
11501                     newViewDate = this.moveYear(this.viewDate, dir);
11502                 } else if (e.shiftKey){
11503                     newDate = this.moveMonth(this.date, dir);
11504                     newViewDate = this.moveMonth(this.viewDate, dir);
11505                 } else {
11506                     newDate = new Date(this.date);
11507                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11508                     newViewDate = new Date(this.viewDate);
11509                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11510                 }
11511                 if (this.dateWithinRange(newDate)){
11512                     this.date = newDate;
11513                     this.viewDate = newViewDate;
11514                     this.setValue(this.formatDate(this.date));
11515                     this.update();
11516                     e.preventDefault();
11517                     dateChanged = true;
11518                 }
11519                 break;
11520             case 13: // enter
11521                 this.setValue(this.formatDate(this.date));
11522                 this.hide();
11523                 e.preventDefault();
11524                 break;
11525             case 9: // tab
11526                 this.setValue(this.formatDate(this.date));
11527                 this.hide();
11528                 break;
11529         }
11530     },
11531     
11532     
11533     onClick: function(e) {
11534         e.stopPropagation();
11535         e.preventDefault();
11536         
11537         var target = e.getTarget();
11538         
11539         if(target.nodeName.toLowerCase() === 'i'){
11540             target = Roo.get(target).dom.parentNode;
11541         }
11542         
11543         var nodeName = target.nodeName;
11544         var className = target.className;
11545         var html = target.innerHTML;
11546         
11547         switch(nodeName.toLowerCase()) {
11548             case 'th':
11549                 switch(className) {
11550                     case 'switch':
11551                         this.showMode(1);
11552                         break;
11553                     case 'prev':
11554                     case 'next':
11555                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11556                         switch(this.viewMode){
11557                                 case 0:
11558                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11559                                         break;
11560                                 case 1:
11561                                 case 2:
11562                                         this.viewDate = this.moveYear(this.viewDate, dir);
11563                                         break;
11564                         }
11565                         this.fill();
11566                         break;
11567                     case 'today':
11568                         var date = new Date();
11569                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11570                         this.fill()
11571                         this.setValue(this.formatDate(this.date));
11572                         this.hide();
11573                         break;
11574                 }
11575                 break;
11576             case 'span':
11577                 if (className.indexOf('disabled') === -1) {
11578                     this.viewDate.setUTCDate(1);
11579                     if (className.indexOf('month') !== -1) {
11580                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11581                     } else {
11582                         var year = parseInt(html, 10) || 0;
11583                         this.viewDate.setUTCFullYear(year);
11584                         
11585                     }
11586                     this.showMode(-1);
11587                     this.fill();
11588                 }
11589                 break;
11590                 
11591             case 'td':
11592                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11593                     var day = parseInt(html, 10) || 1;
11594                     var year = this.viewDate.getUTCFullYear(),
11595                         month = this.viewDate.getUTCMonth();
11596
11597                     if (className.indexOf('old') !== -1) {
11598                         if(month === 0 ){
11599                             month = 11;
11600                             year -= 1;
11601                         }else{
11602                             month -= 1;
11603                         }
11604                     } else if (className.indexOf('new') !== -1) {
11605                         if (month == 11) {
11606                             month = 0;
11607                             year += 1;
11608                         } else {
11609                             month += 1;
11610                         }
11611                     }
11612                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11613                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11614                     this.fill();
11615                     this.setValue(this.formatDate(this.date));
11616                     this.hide();
11617                 }
11618                 break;
11619         }
11620     },
11621     
11622     setStartDate: function(startDate){
11623         this.startDate = startDate || -Infinity;
11624         if (this.startDate !== -Infinity) {
11625             this.startDate = this.parseDate(this.startDate);
11626         }
11627         this.update();
11628         this.updateNavArrows();
11629     },
11630
11631     setEndDate: function(endDate){
11632         this.endDate = endDate || Infinity;
11633         if (this.endDate !== Infinity) {
11634             this.endDate = this.parseDate(this.endDate);
11635         }
11636         this.update();
11637         this.updateNavArrows();
11638     },
11639     
11640     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11641         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11642         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11643             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11644         }
11645         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11646             return parseInt(d, 10);
11647         });
11648         this.update();
11649         this.updateNavArrows();
11650     },
11651     
11652     updateNavArrows: function() {
11653         var d = new Date(this.viewDate),
11654         year = d.getUTCFullYear(),
11655         month = d.getUTCMonth();
11656         
11657         Roo.each(this.picker().select('.prev', true).elements, function(v){
11658             v.show();
11659             switch (this.viewMode) {
11660                 case 0:
11661
11662                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11663                         v.hide();
11664                     }
11665                     break;
11666                 case 1:
11667                 case 2:
11668                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11669                         v.hide();
11670                     }
11671                     break;
11672             }
11673         });
11674         
11675         Roo.each(this.picker().select('.next', true).elements, function(v){
11676             v.show();
11677             switch (this.viewMode) {
11678                 case 0:
11679
11680                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11681                         v.hide();
11682                     }
11683                     break;
11684                 case 1:
11685                 case 2:
11686                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11687                         v.hide();
11688                     }
11689                     break;
11690             }
11691         })
11692     },
11693     
11694     moveMonth: function(date, dir){
11695         if (!dir) return date;
11696         var new_date = new Date(date.valueOf()),
11697         day = new_date.getUTCDate(),
11698         month = new_date.getUTCMonth(),
11699         mag = Math.abs(dir),
11700         new_month, test;
11701         dir = dir > 0 ? 1 : -1;
11702         if (mag == 1){
11703             test = dir == -1
11704             // If going back one month, make sure month is not current month
11705             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11706             ? function(){
11707                 return new_date.getUTCMonth() == month;
11708             }
11709             // If going forward one month, make sure month is as expected
11710             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11711             : function(){
11712                 return new_date.getUTCMonth() != new_month;
11713             };
11714             new_month = month + dir;
11715             new_date.setUTCMonth(new_month);
11716             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11717             if (new_month < 0 || new_month > 11)
11718                 new_month = (new_month + 12) % 12;
11719         } else {
11720             // For magnitudes >1, move one month at a time...
11721             for (var i=0; i<mag; i++)
11722                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11723                 new_date = this.moveMonth(new_date, dir);
11724             // ...then reset the day, keeping it in the new month
11725             new_month = new_date.getUTCMonth();
11726             new_date.setUTCDate(day);
11727             test = function(){
11728                 return new_month != new_date.getUTCMonth();
11729             };
11730         }
11731         // Common date-resetting loop -- if date is beyond end of month, make it
11732         // end of month
11733         while (test()){
11734             new_date.setUTCDate(--day);
11735             new_date.setUTCMonth(new_month);
11736         }
11737         return new_date;
11738     },
11739
11740     moveYear: function(date, dir){
11741         return this.moveMonth(date, dir*12);
11742     },
11743
11744     dateWithinRange: function(date){
11745         return date >= this.startDate && date <= this.endDate;
11746     },
11747
11748     
11749     remove: function() {
11750         this.picker().remove();
11751     }
11752    
11753 });
11754
11755 Roo.apply(Roo.bootstrap.DateField,  {
11756     
11757     head : {
11758         tag: 'thead',
11759         cn: [
11760         {
11761             tag: 'tr',
11762             cn: [
11763             {
11764                 tag: 'th',
11765                 cls: 'prev',
11766                 html: '<i class="icon-arrow-left"/>'
11767             },
11768             {
11769                 tag: 'th',
11770                 cls: 'switch',
11771                 colspan: '5'
11772             },
11773             {
11774                 tag: 'th',
11775                 cls: 'next',
11776                 html: '<i class="icon-arrow-right"/>'
11777             }
11778
11779             ]
11780         }
11781         ]
11782     },
11783     
11784     content : {
11785         tag: 'tbody',
11786         cn: [
11787         {
11788             tag: 'tr',
11789             cn: [
11790             {
11791                 tag: 'td',
11792                 colspan: '7'
11793             }
11794             ]
11795         }
11796         ]
11797     },
11798     
11799     footer : {
11800         tag: 'tfoot',
11801         cn: [
11802         {
11803             tag: 'tr',
11804             cn: [
11805             {
11806                 tag: 'th',
11807                 colspan: '7',
11808                 cls: 'today'
11809             }
11810                     
11811             ]
11812         }
11813         ]
11814     },
11815     
11816     dates:{
11817         en: {
11818             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11819             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11820             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11821             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11822             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11823             today: "Today"
11824         }
11825     },
11826     
11827     modes: [
11828     {
11829         clsName: 'days',
11830         navFnc: 'Month',
11831         navStep: 1
11832     },
11833     {
11834         clsName: 'months',
11835         navFnc: 'FullYear',
11836         navStep: 1
11837     },
11838     {
11839         clsName: 'years',
11840         navFnc: 'FullYear',
11841         navStep: 10
11842     }]
11843 });
11844
11845 Roo.apply(Roo.bootstrap.DateField,  {
11846   
11847     template : {
11848         tag: 'div',
11849         cls: 'datepicker dropdown-menu',
11850         cn: [
11851         {
11852             tag: 'div',
11853             cls: 'datepicker-days',
11854             cn: [
11855             {
11856                 tag: 'table',
11857                 cls: 'table-condensed',
11858                 cn:[
11859                 Roo.bootstrap.DateField.head,
11860                 {
11861                     tag: 'tbody'
11862                 },
11863                 Roo.bootstrap.DateField.footer
11864                 ]
11865             }
11866             ]
11867         },
11868         {
11869             tag: 'div',
11870             cls: 'datepicker-months',
11871             cn: [
11872             {
11873                 tag: 'table',
11874                 cls: 'table-condensed',
11875                 cn:[
11876                 Roo.bootstrap.DateField.head,
11877                 Roo.bootstrap.DateField.content,
11878                 Roo.bootstrap.DateField.footer
11879                 ]
11880             }
11881             ]
11882         },
11883         {
11884             tag: 'div',
11885             cls: 'datepicker-years',
11886             cn: [
11887             {
11888                 tag: 'table',
11889                 cls: 'table-condensed',
11890                 cn:[
11891                 Roo.bootstrap.DateField.head,
11892                 Roo.bootstrap.DateField.content,
11893                 Roo.bootstrap.DateField.footer
11894                 ]
11895             }
11896             ]
11897         }
11898         ]
11899     }
11900 });
11901
11902  
11903
11904  /*
11905  * - LGPL
11906  *
11907  * TimeField
11908  * 
11909  */
11910
11911 /**
11912  * @class Roo.bootstrap.TimeField
11913  * @extends Roo.bootstrap.Input
11914  * Bootstrap DateField class
11915  * 
11916  * 
11917  * @constructor
11918  * Create a new TimeField
11919  * @param {Object} config The config object
11920  */
11921
11922 Roo.bootstrap.TimeField = function(config){
11923     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11924     this.addEvents({
11925             /**
11926              * @event show
11927              * Fires when this field show.
11928              * @param {Roo.bootstrap.DateField} this
11929              * @param {Mixed} date The date value
11930              */
11931             show : true,
11932             /**
11933              * @event show
11934              * Fires when this field hide.
11935              * @param {Roo.bootstrap.DateField} this
11936              * @param {Mixed} date The date value
11937              */
11938             hide : true,
11939             /**
11940              * @event select
11941              * Fires when select a date.
11942              * @param {Roo.bootstrap.DateField} this
11943              * @param {Mixed} date The date value
11944              */
11945             select : true
11946         });
11947 };
11948
11949 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11950     
11951     /**
11952      * @cfg {String} format
11953      * The default time format string which can be overriden for localization support.  The format must be
11954      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11955      */
11956     format : "H:i",
11957        
11958     onRender: function(ct, position)
11959     {
11960         
11961         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11962                 
11963         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11964         
11965         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11966         
11967         this.pop = this.picker().select('>.datepicker-time',true).first();
11968         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11969         
11970         this.picker().on('mousedown', this.onMousedown, this);
11971         this.picker().on('click', this.onClick, this);
11972         
11973         this.picker().addClass('datepicker-dropdown');
11974     
11975         this.fillTime();
11976         this.update();
11977             
11978         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11979         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11980         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11981         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11982         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11983         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11984
11985     },
11986     
11987     fireKey: function(e){
11988         if (!this.picker().isVisible()){
11989             if (e.keyCode == 27) // allow escape to hide and re-show picker
11990                 this.show();
11991             return;
11992         }
11993
11994         e.preventDefault();
11995         
11996         switch(e.keyCode){
11997             case 27: // escape
11998                 this.hide();
11999                 break;
12000             case 37: // left
12001             case 39: // right
12002                 this.onTogglePeriod();
12003                 break;
12004             case 38: // up
12005                 this.onIncrementMinutes();
12006                 break;
12007             case 40: // down
12008                 this.onDecrementMinutes();
12009                 break;
12010             case 13: // enter
12011             case 9: // tab
12012                 this.setTime();
12013                 break;
12014         }
12015     },
12016     
12017     onClick: function(e) {
12018         e.stopPropagation();
12019         e.preventDefault();
12020     },
12021     
12022     picker : function()
12023     {
12024         return this.el.select('.datepicker', true).first();
12025     },
12026     
12027     fillTime: function()
12028     {    
12029         var time = this.pop.select('tbody', true).first();
12030         
12031         time.dom.innerHTML = '';
12032         
12033         time.createChild({
12034             tag: 'tr',
12035             cn: [
12036                 {
12037                     tag: 'td',
12038                     cn: [
12039                         {
12040                             tag: 'a',
12041                             href: '#',
12042                             cls: 'btn',
12043                             cn: [
12044                                 {
12045                                     tag: 'span',
12046                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12047                                 }
12048                             ]
12049                         } 
12050                     ]
12051                 },
12052                 {
12053                     tag: 'td',
12054                     cls: 'separator'
12055                 },
12056                 {
12057                     tag: 'td',
12058                     cn: [
12059                         {
12060                             tag: 'a',
12061                             href: '#',
12062                             cls: 'btn',
12063                             cn: [
12064                                 {
12065                                     tag: 'span',
12066                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12067                                 }
12068                             ]
12069                         }
12070                     ]
12071                 },
12072                 {
12073                     tag: 'td',
12074                     cls: 'separator'
12075                 }
12076             ]
12077         });
12078         
12079         time.createChild({
12080             tag: 'tr',
12081             cn: [
12082                 {
12083                     tag: 'td',
12084                     cn: [
12085                         {
12086                             tag: 'span',
12087                             cls: 'timepicker-hour',
12088                             html: '00'
12089                         }  
12090                     ]
12091                 },
12092                 {
12093                     tag: 'td',
12094                     cls: 'separator',
12095                     html: ':'
12096                 },
12097                 {
12098                     tag: 'td',
12099                     cn: [
12100                         {
12101                             tag: 'span',
12102                             cls: 'timepicker-minute',
12103                             html: '00'
12104                         }  
12105                     ]
12106                 },
12107                 {
12108                     tag: 'td',
12109                     cls: 'separator'
12110                 },
12111                 {
12112                     tag: 'td',
12113                     cn: [
12114                         {
12115                             tag: 'button',
12116                             type: 'button',
12117                             cls: 'btn btn-primary period',
12118                             html: 'AM'
12119                             
12120                         }
12121                     ]
12122                 }
12123             ]
12124         });
12125         
12126         time.createChild({
12127             tag: 'tr',
12128             cn: [
12129                 {
12130                     tag: 'td',
12131                     cn: [
12132                         {
12133                             tag: 'a',
12134                             href: '#',
12135                             cls: 'btn',
12136                             cn: [
12137                                 {
12138                                     tag: 'span',
12139                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12140                                 }
12141                             ]
12142                         }
12143                     ]
12144                 },
12145                 {
12146                     tag: 'td',
12147                     cls: 'separator'
12148                 },
12149                 {
12150                     tag: 'td',
12151                     cn: [
12152                         {
12153                             tag: 'a',
12154                             href: '#',
12155                             cls: 'btn',
12156                             cn: [
12157                                 {
12158                                     tag: 'span',
12159                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12160                                 }
12161                             ]
12162                         }
12163                     ]
12164                 },
12165                 {
12166                     tag: 'td',
12167                     cls: 'separator'
12168                 }
12169             ]
12170         });
12171         
12172     },
12173     
12174     update: function()
12175     {
12176         
12177         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12178         
12179         this.fill();
12180     },
12181     
12182     fill: function() 
12183     {
12184         var hours = this.time.getHours();
12185         var minutes = this.time.getMinutes();
12186         var period = 'AM';
12187         
12188         if(hours > 11){
12189             period = 'PM';
12190         }
12191         
12192         if(hours == 0){
12193             hours = 12;
12194         }
12195         
12196         
12197         if(hours > 12){
12198             hours = hours - 12;
12199         }
12200         
12201         if(hours < 10){
12202             hours = '0' + hours;
12203         }
12204         
12205         if(minutes < 10){
12206             minutes = '0' + minutes;
12207         }
12208         
12209         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12210         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12211         this.pop.select('button', true).first().dom.innerHTML = period;
12212         
12213     },
12214     
12215     place: function()
12216     {   
12217         this.picker().removeClass(['bottom', 'top']);
12218         
12219         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12220             /*
12221              * place to the top of element!
12222              *
12223              */
12224             
12225             this.picker().addClass('top');
12226             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12227             
12228             return;
12229         }
12230         
12231         this.picker().addClass('bottom');
12232         
12233         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12234     },
12235   
12236     onFocus : function()
12237     {
12238         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12239         this.show();
12240     },
12241     
12242     onBlur : function()
12243     {
12244         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12245         this.hide();
12246     },
12247     
12248     show : function()
12249     {
12250         this.picker().show();
12251         this.pop.show();
12252         this.update();
12253         this.place();
12254         
12255         this.fireEvent('show', this, this.date);
12256     },
12257     
12258     hide : function()
12259     {
12260         this.picker().hide();
12261         this.pop.hide();
12262         
12263         this.fireEvent('hide', this, this.date);
12264     },
12265     
12266     setTime : function()
12267     {
12268         this.hide();
12269         this.setValue(this.time.format(this.format));
12270         
12271         this.fireEvent('select', this, this.date);
12272         
12273         
12274     },
12275     
12276     onMousedown: function(e){
12277         e.stopPropagation();
12278         e.preventDefault();
12279     },
12280     
12281     onIncrementHours: function()
12282     {
12283         Roo.log('onIncrementHours');
12284         this.time = this.time.add(Date.HOUR, 1);
12285         this.update();
12286         
12287     },
12288     
12289     onDecrementHours: function()
12290     {
12291         Roo.log('onDecrementHours');
12292         this.time = this.time.add(Date.HOUR, -1);
12293         this.update();
12294     },
12295     
12296     onIncrementMinutes: function()
12297     {
12298         Roo.log('onIncrementMinutes');
12299         this.time = this.time.add(Date.MINUTE, 1);
12300         this.update();
12301     },
12302     
12303     onDecrementMinutes: function()
12304     {
12305         Roo.log('onDecrementMinutes');
12306         this.time = this.time.add(Date.MINUTE, -1);
12307         this.update();
12308     },
12309     
12310     onTogglePeriod: function()
12311     {
12312         Roo.log('onTogglePeriod');
12313         this.time = this.time.add(Date.HOUR, 12);
12314         this.update();
12315     }
12316     
12317    
12318 });
12319
12320 Roo.apply(Roo.bootstrap.TimeField,  {
12321     
12322     content : {
12323         tag: 'tbody',
12324         cn: [
12325             {
12326                 tag: 'tr',
12327                 cn: [
12328                 {
12329                     tag: 'td',
12330                     colspan: '7'
12331                 }
12332                 ]
12333             }
12334         ]
12335     },
12336     
12337     footer : {
12338         tag: 'tfoot',
12339         cn: [
12340             {
12341                 tag: 'tr',
12342                 cn: [
12343                 {
12344                     tag: 'th',
12345                     colspan: '7',
12346                     cls: '',
12347                     cn: [
12348                         {
12349                             tag: 'button',
12350                             cls: 'btn btn-info ok',
12351                             html: 'OK'
12352                         }
12353                     ]
12354                 }
12355
12356                 ]
12357             }
12358         ]
12359     }
12360 });
12361
12362 Roo.apply(Roo.bootstrap.TimeField,  {
12363   
12364     template : {
12365         tag: 'div',
12366         cls: 'datepicker dropdown-menu',
12367         cn: [
12368             {
12369                 tag: 'div',
12370                 cls: 'datepicker-time',
12371                 cn: [
12372                 {
12373                     tag: 'table',
12374                     cls: 'table-condensed',
12375                     cn:[
12376                     Roo.bootstrap.TimeField.content,
12377                     Roo.bootstrap.TimeField.footer
12378                     ]
12379                 }
12380                 ]
12381             }
12382         ]
12383     }
12384 });
12385
12386  
12387
12388  /*
12389  * - LGPL
12390  *
12391  * CheckBox
12392  * 
12393  */
12394
12395 /**
12396  * @class Roo.bootstrap.CheckBox
12397  * @extends Roo.bootstrap.Input
12398  * Bootstrap CheckBox class
12399  * 
12400  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12401  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12402  * @cfg {String} boxLabel The text that appears beside the checkbox
12403  * @cfg {Boolean} checked initnal the element
12404  * 
12405  * @constructor
12406  * Create a new CheckBox
12407  * @param {Object} config The config object
12408  */
12409
12410 Roo.bootstrap.CheckBox = function(config){
12411     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12412    
12413         this.addEvents({
12414             /**
12415             * @event check
12416             * Fires when the element is checked or unchecked.
12417             * @param {Roo.bootstrap.CheckBox} this This input
12418             * @param {Boolean} checked The new checked value
12419             */
12420            check : true
12421         });
12422 };
12423
12424 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12425     
12426     inputType: 'checkbox',
12427     inputValue: 1,
12428     valueOff: 0,
12429     boxLabel: false,
12430     checked: false,
12431     
12432     getAutoCreate : function()
12433     {
12434         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12435         
12436         var id = Roo.id();
12437         
12438         var cfg = {};
12439         
12440         cfg.cls = 'form-group' //input-group
12441         
12442         var input =  {
12443             tag: 'input',
12444             id : id,
12445             type : this.inputType,
12446             value : (!this.checked) ? this.valueOff : this.inputValue,
12447             cls : 'form-box',
12448             placeholder : this.placeholder || ''
12449             
12450         };
12451         
12452         if (this.disabled) {
12453             input.disabled=true;
12454         }
12455         
12456         if(this.checked){
12457             input.checked = this.checked;
12458         }
12459         
12460         if (this.name) {
12461             input.name = this.name;
12462         }
12463         
12464         if (this.size) {
12465             input.cls += ' input-' + this.size;
12466         }
12467         
12468         var settings=this;
12469         ['xs','sm','md','lg'].map(function(size){
12470             if (settings[size]) {
12471                 cfg.cls += ' col-' + size + '-' + settings[size];
12472             }
12473         });
12474         
12475         var inputblock = input;
12476         
12477         if (this.before || this.after) {
12478             
12479             inputblock = {
12480                 cls : 'input-group',
12481                 cn :  [] 
12482             };
12483             if (this.before) {
12484                 inputblock.cn.push({
12485                     tag :'span',
12486                     cls : 'input-group-addon',
12487                     html : this.before
12488                 });
12489             }
12490             inputblock.cn.push(input);
12491             if (this.after) {
12492                 inputblock.cn.push({
12493                     tag :'span',
12494                     cls : 'input-group-addon',
12495                     html : this.after
12496                 });
12497             }
12498             
12499         };
12500         
12501         if (align ==='left' && this.fieldLabel.length) {
12502                 Roo.log("left and has label");
12503                 cfg.cn = [
12504                     
12505                     {
12506                         tag: 'label',
12507                         'for' :  id,
12508                         cls : 'control-label col-md-' + this.labelWidth,
12509                         html : this.fieldLabel
12510                         
12511                     },
12512                     {
12513                         cls : "col-md-" + (12 - this.labelWidth), 
12514                         cn: [
12515                             inputblock
12516                         ]
12517                     }
12518                     
12519                 ];
12520         } else if ( this.fieldLabel.length) {
12521                 Roo.log(" label");
12522                 cfg.cn = [
12523                    
12524                     {
12525                         tag: this.boxLabel ? 'span' : 'label',
12526                         'for': id,
12527                         cls: 'control-label box-input-label',
12528                         //cls : 'input-group-addon',
12529                         html : this.fieldLabel
12530                         
12531                     },
12532                     
12533                     inputblock
12534                     
12535                 ];
12536
12537         } else {
12538             
12539                    Roo.log(" no label && no align");
12540                 cfg.cn = [
12541                     
12542                         inputblock
12543                     
12544                 ];
12545                 
12546                 
12547         };
12548         
12549         if(this.boxLabel){
12550             cfg.cn.push({
12551                 tag: 'label',
12552                 'for': id,
12553                 cls: 'box-label',
12554                 html: this.boxLabel
12555             })
12556         }
12557         
12558         return cfg;
12559         
12560     },
12561     
12562     /**
12563      * return the real input element.
12564      */
12565     inputEl: function ()
12566     {
12567         return this.el.select('input.form-box',true).first();
12568     },
12569     
12570     initEvents : function()
12571     {
12572 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12573         
12574         this.inputEl().on('click', this.onClick,  this);
12575         
12576     },
12577     
12578     onClick : function()
12579     {   
12580         this.setChecked(!this.checked);
12581     },
12582     
12583     setChecked : function(state,suppressEvent)
12584     {
12585         this.checked = state;
12586         
12587         this.inputEl().dom.checked = state;
12588         
12589         if(suppressEvent !== true){
12590             this.fireEvent('check', this, state);
12591         }
12592         
12593         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12594         
12595     },
12596     
12597     setValue : function(v,suppressEvent)
12598     {
12599         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12600     }
12601     
12602 });
12603
12604  
12605 /*
12606  * - LGPL
12607  *
12608  * Radio
12609  * 
12610  */
12611
12612 /**
12613  * @class Roo.bootstrap.Radio
12614  * @extends Roo.bootstrap.CheckBox
12615  * Bootstrap Radio class
12616
12617  * @constructor
12618  * Create a new Radio
12619  * @param {Object} config The config object
12620  */
12621
12622 Roo.bootstrap.Radio = function(config){
12623     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12624    
12625 };
12626
12627 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12628     
12629     inputType: 'radio',
12630     inputValue: '',
12631     valueOff: '',
12632     
12633     getAutoCreate : function()
12634     {
12635         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12636         
12637         var id = Roo.id();
12638         
12639         var cfg = {};
12640         
12641         cfg.cls = 'form-group' //input-group
12642         
12643         var input =  {
12644             tag: 'input',
12645             id : id,
12646             type : this.inputType,
12647             value : (!this.checked) ? this.valueOff : this.inputValue,
12648             cls : 'form-box',
12649             placeholder : this.placeholder || ''
12650             
12651         };
12652         
12653         if (this.disabled) {
12654             input.disabled=true;
12655         }
12656         
12657         if(this.checked){
12658             input.checked = this.checked;
12659         }
12660         
12661         if (this.name) {
12662             input.name = this.name;
12663         }
12664         
12665         if (this.size) {
12666             input.cls += ' input-' + this.size;
12667         }
12668         
12669         var settings=this;
12670         ['xs','sm','md','lg'].map(function(size){
12671             if (settings[size]) {
12672                 cfg.cls += ' col-' + size + '-' + settings[size];
12673             }
12674         });
12675         
12676         var inputblock = input;
12677         
12678         if (this.before || this.after) {
12679             
12680             inputblock = {
12681                 cls : 'input-group',
12682                 cn :  [] 
12683             };
12684             if (this.before) {
12685                 inputblock.cn.push({
12686                     tag :'span',
12687                     cls : 'input-group-addon',
12688                     html : this.before
12689                 });
12690             }
12691             inputblock.cn.push(input);
12692             if (this.after) {
12693                 inputblock.cn.push({
12694                     tag :'span',
12695                     cls : 'input-group-addon',
12696                     html : this.after
12697                 });
12698             }
12699             
12700         };
12701         
12702         if (align ==='left' && this.fieldLabel.length) {
12703                 Roo.log("left and has label");
12704                 cfg.cn = [
12705                     
12706                     {
12707                         tag: 'label',
12708                         'for' :  id,
12709                         cls : 'control-label col-md-' + this.labelWidth,
12710                         html : this.fieldLabel
12711                         
12712                     },
12713                     {
12714                         cls : "col-md-" + (12 - this.labelWidth), 
12715                         cn: [
12716                             inputblock
12717                         ]
12718                     }
12719                     
12720                 ];
12721         } else if ( this.fieldLabel.length) {
12722                 Roo.log(" label");
12723                  cfg.cn = [
12724                    
12725                     {
12726                         tag: 'label',
12727                         'for': id,
12728                         cls: 'control-label box-input-label',
12729                         //cls : 'input-group-addon',
12730                         html : this.fieldLabel
12731                         
12732                     },
12733                     
12734                     inputblock
12735                     
12736                 ];
12737
12738         } else {
12739             
12740                    Roo.log(" no label && no align");
12741                 cfg.cn = [
12742                     
12743                         inputblock
12744                     
12745                 ];
12746                 
12747                 
12748         };
12749         
12750         if(this.boxLabel){
12751             cfg.cn.push({
12752                 tag: 'label',
12753                 'for': id,
12754                 cls: 'box-label',
12755                 html: this.boxLabel
12756             })
12757         }
12758         
12759         return cfg;
12760         
12761     },
12762    
12763     onClick : function()
12764     {   
12765         this.setChecked(true);
12766     },
12767     
12768     setChecked : function(state,suppressEvent)
12769     {
12770         if(state){
12771             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12772                 v.dom.checked = false;
12773             });
12774         }
12775         
12776         this.checked = state;
12777         this.inputEl().dom.checked = state;
12778         
12779         if(suppressEvent !== true){
12780             this.fireEvent('check', this, state);
12781         }
12782         
12783         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12784         
12785     },
12786     
12787     getGroupValue : function()
12788     {
12789         var value = ''
12790         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12791             if(v.dom.checked == true){
12792                 value = v.dom.value;
12793             }
12794         });
12795         
12796         return value;
12797     },
12798     
12799     /**
12800      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12801      * @return {Mixed} value The field value
12802      */
12803     getValue : function(){
12804         return this.getGroupValue();
12805     }
12806     
12807 });
12808
12809  
12810 //<script type="text/javascript">
12811
12812 /*
12813  * Based  Ext JS Library 1.1.1
12814  * Copyright(c) 2006-2007, Ext JS, LLC.
12815  * LGPL
12816  *
12817  */
12818  
12819 /**
12820  * @class Roo.HtmlEditorCore
12821  * @extends Roo.Component
12822  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12823  *
12824  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12825  */
12826
12827 Roo.HtmlEditorCore = function(config){
12828     
12829     
12830     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12831     this.addEvents({
12832         /**
12833          * @event initialize
12834          * Fires when the editor is fully initialized (including the iframe)
12835          * @param {Roo.HtmlEditorCore} this
12836          */
12837         initialize: true,
12838         /**
12839          * @event activate
12840          * Fires when the editor is first receives the focus. Any insertion must wait
12841          * until after this event.
12842          * @param {Roo.HtmlEditorCore} this
12843          */
12844         activate: true,
12845          /**
12846          * @event beforesync
12847          * Fires before the textarea is updated with content from the editor iframe. Return false
12848          * to cancel the sync.
12849          * @param {Roo.HtmlEditorCore} this
12850          * @param {String} html
12851          */
12852         beforesync: true,
12853          /**
12854          * @event beforepush
12855          * Fires before the iframe editor is updated with content from the textarea. Return false
12856          * to cancel the push.
12857          * @param {Roo.HtmlEditorCore} this
12858          * @param {String} html
12859          */
12860         beforepush: true,
12861          /**
12862          * @event sync
12863          * Fires when the textarea is updated with content from the editor iframe.
12864          * @param {Roo.HtmlEditorCore} this
12865          * @param {String} html
12866          */
12867         sync: true,
12868          /**
12869          * @event push
12870          * Fires when the iframe editor is updated with content from the textarea.
12871          * @param {Roo.HtmlEditorCore} this
12872          * @param {String} html
12873          */
12874         push: true,
12875         
12876         /**
12877          * @event editorevent
12878          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12879          * @param {Roo.HtmlEditorCore} this
12880          */
12881         editorevent: true
12882     });
12883      
12884 };
12885
12886
12887 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12888
12889
12890      /**
12891      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12892      */
12893     
12894     owner : false,
12895     
12896      /**
12897      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12898      *                        Roo.resizable.
12899      */
12900     resizable : false,
12901      /**
12902      * @cfg {Number} height (in pixels)
12903      */   
12904     height: 300,
12905    /**
12906      * @cfg {Number} width (in pixels)
12907      */   
12908     width: 500,
12909     
12910     /**
12911      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12912      * 
12913      */
12914     stylesheets: false,
12915     
12916     // id of frame..
12917     frameId: false,
12918     
12919     // private properties
12920     validationEvent : false,
12921     deferHeight: true,
12922     initialized : false,
12923     activated : false,
12924     sourceEditMode : false,
12925     onFocus : Roo.emptyFn,
12926     iframePad:3,
12927     hideMode:'offsets',
12928     
12929     clearUp: true,
12930     
12931      
12932     
12933
12934     /**
12935      * Protected method that will not generally be called directly. It
12936      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12937      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12938      */
12939     getDocMarkup : function(){
12940         // body styles..
12941         var st = '';
12942         Roo.log(this.stylesheets);
12943         
12944         // inherit styels from page...?? 
12945         if (this.stylesheets === false) {
12946             
12947             Roo.get(document.head).select('style').each(function(node) {
12948                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12949             });
12950             
12951             Roo.get(document.head).select('link').each(function(node) { 
12952                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12953             });
12954             
12955         } else if (!this.stylesheets.length) {
12956                 // simple..
12957                 st = '<style type="text/css">' +
12958                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12959                    '</style>';
12960         } else {
12961             Roo.each(this.stylesheets, function(s) {
12962                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12963             });
12964             
12965         }
12966         
12967         st +=  '<style type="text/css">' +
12968             'IMG { cursor: pointer } ' +
12969         '</style>';
12970
12971         
12972         return '<html><head>' + st  +
12973             //<style type="text/css">' +
12974             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12975             //'</style>' +
12976             ' </head><body class="roo-htmleditor-body"></body></html>';
12977     },
12978
12979     // private
12980     onRender : function(ct, position)
12981     {
12982         var _t = this;
12983         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12984         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12985         
12986         
12987         this.el.dom.style.border = '0 none';
12988         this.el.dom.setAttribute('tabIndex', -1);
12989         this.el.addClass('x-hidden hide');
12990         
12991         
12992         
12993         if(Roo.isIE){ // fix IE 1px bogus margin
12994             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12995         }
12996        
12997         
12998         this.frameId = Roo.id();
12999         
13000          
13001         
13002         var iframe = this.owner.wrap.createChild({
13003             tag: 'iframe',
13004             cls: 'form-control', // bootstrap..
13005             id: this.frameId,
13006             name: this.frameId,
13007             frameBorder : 'no',
13008             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13009         }, this.el
13010         );
13011         
13012         
13013         this.iframe = iframe.dom;
13014
13015          this.assignDocWin();
13016         
13017         this.doc.designMode = 'on';
13018        
13019         this.doc.open();
13020         this.doc.write(this.getDocMarkup());
13021         this.doc.close();
13022
13023         
13024         var task = { // must defer to wait for browser to be ready
13025             run : function(){
13026                 //console.log("run task?" + this.doc.readyState);
13027                 this.assignDocWin();
13028                 if(this.doc.body || this.doc.readyState == 'complete'){
13029                     try {
13030                         this.doc.designMode="on";
13031                     } catch (e) {
13032                         return;
13033                     }
13034                     Roo.TaskMgr.stop(task);
13035                     this.initEditor.defer(10, this);
13036                 }
13037             },
13038             interval : 10,
13039             duration: 10000,
13040             scope: this
13041         };
13042         Roo.TaskMgr.start(task);
13043
13044         
13045          
13046     },
13047
13048     // private
13049     onResize : function(w, h)
13050     {
13051          Roo.log('resize: ' +w + ',' + h );
13052         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13053         if(!this.iframe){
13054             return;
13055         }
13056         if(typeof w == 'number'){
13057             
13058             this.iframe.style.width = w + 'px';
13059         }
13060         if(typeof h == 'number'){
13061             
13062             this.iframe.style.height = h + 'px';
13063             if(this.doc){
13064                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13065             }
13066         }
13067         
13068     },
13069
13070     /**
13071      * Toggles the editor between standard and source edit mode.
13072      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13073      */
13074     toggleSourceEdit : function(sourceEditMode){
13075         
13076         this.sourceEditMode = sourceEditMode === true;
13077         
13078         if(this.sourceEditMode){
13079  
13080             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13081             
13082         }else{
13083             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13084             //this.iframe.className = '';
13085             this.deferFocus();
13086         }
13087         //this.setSize(this.owner.wrap.getSize());
13088         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13089     },
13090
13091     
13092   
13093
13094     /**
13095      * Protected method that will not generally be called directly. If you need/want
13096      * custom HTML cleanup, this is the method you should override.
13097      * @param {String} html The HTML to be cleaned
13098      * return {String} The cleaned HTML
13099      */
13100     cleanHtml : function(html){
13101         html = String(html);
13102         if(html.length > 5){
13103             if(Roo.isSafari){ // strip safari nonsense
13104                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13105             }
13106         }
13107         if(html == '&nbsp;'){
13108             html = '';
13109         }
13110         return html;
13111     },
13112
13113     /**
13114      * HTML Editor -> Textarea
13115      * Protected method that will not generally be called directly. Syncs the contents
13116      * of the editor iframe with the textarea.
13117      */
13118     syncValue : function(){
13119         if(this.initialized){
13120             var bd = (this.doc.body || this.doc.documentElement);
13121             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13122             var html = bd.innerHTML;
13123             if(Roo.isSafari){
13124                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13125                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13126                 if(m && m[1]){
13127                     html = '<div style="'+m[0]+'">' + html + '</div>';
13128                 }
13129             }
13130             html = this.cleanHtml(html);
13131             // fix up the special chars.. normaly like back quotes in word...
13132             // however we do not want to do this with chinese..
13133             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13134                 var cc = b.charCodeAt();
13135                 if (
13136                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13137                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13138                     (cc >= 0xf900 && cc < 0xfb00 )
13139                 ) {
13140                         return b;
13141                 }
13142                 return "&#"+cc+";" 
13143             });
13144             if(this.owner.fireEvent('beforesync', this, html) !== false){
13145                 this.el.dom.value = html;
13146                 this.owner.fireEvent('sync', this, html);
13147             }
13148         }
13149     },
13150
13151     /**
13152      * Protected method that will not generally be called directly. Pushes the value of the textarea
13153      * into the iframe editor.
13154      */
13155     pushValue : function(){
13156         if(this.initialized){
13157             var v = this.el.dom.value.trim();
13158             
13159 //            if(v.length < 1){
13160 //                v = '&#160;';
13161 //            }
13162             
13163             if(this.owner.fireEvent('beforepush', this, v) !== false){
13164                 var d = (this.doc.body || this.doc.documentElement);
13165                 d.innerHTML = v;
13166                 this.cleanUpPaste();
13167                 this.el.dom.value = d.innerHTML;
13168                 this.owner.fireEvent('push', this, v);
13169             }
13170         }
13171     },
13172
13173     // private
13174     deferFocus : function(){
13175         this.focus.defer(10, this);
13176     },
13177
13178     // doc'ed in Field
13179     focus : function(){
13180         if(this.win && !this.sourceEditMode){
13181             this.win.focus();
13182         }else{
13183             this.el.focus();
13184         }
13185     },
13186     
13187     assignDocWin: function()
13188     {
13189         var iframe = this.iframe;
13190         
13191          if(Roo.isIE){
13192             this.doc = iframe.contentWindow.document;
13193             this.win = iframe.contentWindow;
13194         } else {
13195             if (!Roo.get(this.frameId)) {
13196                 return;
13197             }
13198             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13199             this.win = Roo.get(this.frameId).dom.contentWindow;
13200         }
13201     },
13202     
13203     // private
13204     initEditor : function(){
13205         //console.log("INIT EDITOR");
13206         this.assignDocWin();
13207         
13208         
13209         
13210         this.doc.designMode="on";
13211         this.doc.open();
13212         this.doc.write(this.getDocMarkup());
13213         this.doc.close();
13214         
13215         var dbody = (this.doc.body || this.doc.documentElement);
13216         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13217         // this copies styles from the containing element into thsi one..
13218         // not sure why we need all of this..
13219         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13220         ss['background-attachment'] = 'fixed'; // w3c
13221         dbody.bgProperties = 'fixed'; // ie
13222         Roo.DomHelper.applyStyles(dbody, ss);
13223         Roo.EventManager.on(this.doc, {
13224             //'mousedown': this.onEditorEvent,
13225             'mouseup': this.onEditorEvent,
13226             'dblclick': this.onEditorEvent,
13227             'click': this.onEditorEvent,
13228             'keyup': this.onEditorEvent,
13229             buffer:100,
13230             scope: this
13231         });
13232         if(Roo.isGecko){
13233             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13234         }
13235         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13236             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13237         }
13238         this.initialized = true;
13239
13240         this.owner.fireEvent('initialize', this);
13241         this.pushValue();
13242     },
13243
13244     // private
13245     onDestroy : function(){
13246         
13247         
13248         
13249         if(this.rendered){
13250             
13251             //for (var i =0; i < this.toolbars.length;i++) {
13252             //    // fixme - ask toolbars for heights?
13253             //    this.toolbars[i].onDestroy();
13254            // }
13255             
13256             //this.wrap.dom.innerHTML = '';
13257             //this.wrap.remove();
13258         }
13259     },
13260
13261     // private
13262     onFirstFocus : function(){
13263         
13264         this.assignDocWin();
13265         
13266         
13267         this.activated = true;
13268          
13269     
13270         if(Roo.isGecko){ // prevent silly gecko errors
13271             this.win.focus();
13272             var s = this.win.getSelection();
13273             if(!s.focusNode || s.focusNode.nodeType != 3){
13274                 var r = s.getRangeAt(0);
13275                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13276                 r.collapse(true);
13277                 this.deferFocus();
13278             }
13279             try{
13280                 this.execCmd('useCSS', true);
13281                 this.execCmd('styleWithCSS', false);
13282             }catch(e){}
13283         }
13284         this.owner.fireEvent('activate', this);
13285     },
13286
13287     // private
13288     adjustFont: function(btn){
13289         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13290         //if(Roo.isSafari){ // safari
13291         //    adjust *= 2;
13292        // }
13293         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13294         if(Roo.isSafari){ // safari
13295             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13296             v =  (v < 10) ? 10 : v;
13297             v =  (v > 48) ? 48 : v;
13298             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13299             
13300         }
13301         
13302         
13303         v = Math.max(1, v+adjust);
13304         
13305         this.execCmd('FontSize', v  );
13306     },
13307
13308     onEditorEvent : function(e){
13309         this.owner.fireEvent('editorevent', this, e);
13310       //  this.updateToolbar();
13311         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13312     },
13313
13314     insertTag : function(tg)
13315     {
13316         // could be a bit smarter... -> wrap the current selected tRoo..
13317         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13318             
13319             range = this.createRange(this.getSelection());
13320             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13321             wrappingNode.appendChild(range.extractContents());
13322             range.insertNode(wrappingNode);
13323
13324             return;
13325             
13326             
13327             
13328         }
13329         this.execCmd("formatblock",   tg);
13330         
13331     },
13332     
13333     insertText : function(txt)
13334     {
13335         
13336         
13337         var range = this.createRange();
13338         range.deleteContents();
13339                //alert(Sender.getAttribute('label'));
13340                
13341         range.insertNode(this.doc.createTextNode(txt));
13342     } ,
13343     
13344      
13345
13346     /**
13347      * Executes a Midas editor command on the editor document and performs necessary focus and
13348      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13349      * @param {String} cmd The Midas command
13350      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13351      */
13352     relayCmd : function(cmd, value){
13353         this.win.focus();
13354         this.execCmd(cmd, value);
13355         this.owner.fireEvent('editorevent', this);
13356         //this.updateToolbar();
13357         this.owner.deferFocus();
13358     },
13359
13360     /**
13361      * Executes a Midas editor command directly on the editor document.
13362      * For visual commands, you should use {@link #relayCmd} instead.
13363      * <b>This should only be called after the editor is initialized.</b>
13364      * @param {String} cmd The Midas command
13365      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13366      */
13367     execCmd : function(cmd, value){
13368         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13369         this.syncValue();
13370     },
13371  
13372  
13373    
13374     /**
13375      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13376      * to insert tRoo.
13377      * @param {String} text | dom node.. 
13378      */
13379     insertAtCursor : function(text)
13380     {
13381         
13382         
13383         
13384         if(!this.activated){
13385             return;
13386         }
13387         /*
13388         if(Roo.isIE){
13389             this.win.focus();
13390             var r = this.doc.selection.createRange();
13391             if(r){
13392                 r.collapse(true);
13393                 r.pasteHTML(text);
13394                 this.syncValue();
13395                 this.deferFocus();
13396             
13397             }
13398             return;
13399         }
13400         */
13401         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13402             this.win.focus();
13403             
13404             
13405             // from jquery ui (MIT licenced)
13406             var range, node;
13407             var win = this.win;
13408             
13409             if (win.getSelection && win.getSelection().getRangeAt) {
13410                 range = win.getSelection().getRangeAt(0);
13411                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13412                 range.insertNode(node);
13413             } else if (win.document.selection && win.document.selection.createRange) {
13414                 // no firefox support
13415                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13416                 win.document.selection.createRange().pasteHTML(txt);
13417             } else {
13418                 // no firefox support
13419                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13420                 this.execCmd('InsertHTML', txt);
13421             } 
13422             
13423             this.syncValue();
13424             
13425             this.deferFocus();
13426         }
13427     },
13428  // private
13429     mozKeyPress : function(e){
13430         if(e.ctrlKey){
13431             var c = e.getCharCode(), cmd;
13432           
13433             if(c > 0){
13434                 c = String.fromCharCode(c).toLowerCase();
13435                 switch(c){
13436                     case 'b':
13437                         cmd = 'bold';
13438                         break;
13439                     case 'i':
13440                         cmd = 'italic';
13441                         break;
13442                     
13443                     case 'u':
13444                         cmd = 'underline';
13445                         break;
13446                     
13447                     case 'v':
13448                         this.cleanUpPaste.defer(100, this);
13449                         return;
13450                         
13451                 }
13452                 if(cmd){
13453                     this.win.focus();
13454                     this.execCmd(cmd);
13455                     this.deferFocus();
13456                     e.preventDefault();
13457                 }
13458                 
13459             }
13460         }
13461     },
13462
13463     // private
13464     fixKeys : function(){ // load time branching for fastest keydown performance
13465         if(Roo.isIE){
13466             return function(e){
13467                 var k = e.getKey(), r;
13468                 if(k == e.TAB){
13469                     e.stopEvent();
13470                     r = this.doc.selection.createRange();
13471                     if(r){
13472                         r.collapse(true);
13473                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13474                         this.deferFocus();
13475                     }
13476                     return;
13477                 }
13478                 
13479                 if(k == e.ENTER){
13480                     r = this.doc.selection.createRange();
13481                     if(r){
13482                         var target = r.parentElement();
13483                         if(!target || target.tagName.toLowerCase() != 'li'){
13484                             e.stopEvent();
13485                             r.pasteHTML('<br />');
13486                             r.collapse(false);
13487                             r.select();
13488                         }
13489                     }
13490                 }
13491                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13492                     this.cleanUpPaste.defer(100, this);
13493                     return;
13494                 }
13495                 
13496                 
13497             };
13498         }else if(Roo.isOpera){
13499             return function(e){
13500                 var k = e.getKey();
13501                 if(k == e.TAB){
13502                     e.stopEvent();
13503                     this.win.focus();
13504                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13505                     this.deferFocus();
13506                 }
13507                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13508                     this.cleanUpPaste.defer(100, this);
13509                     return;
13510                 }
13511                 
13512             };
13513         }else if(Roo.isSafari){
13514             return function(e){
13515                 var k = e.getKey();
13516                 
13517                 if(k == e.TAB){
13518                     e.stopEvent();
13519                     this.execCmd('InsertText','\t');
13520                     this.deferFocus();
13521                     return;
13522                 }
13523                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13524                     this.cleanUpPaste.defer(100, this);
13525                     return;
13526                 }
13527                 
13528              };
13529         }
13530     }(),
13531     
13532     getAllAncestors: function()
13533     {
13534         var p = this.getSelectedNode();
13535         var a = [];
13536         if (!p) {
13537             a.push(p); // push blank onto stack..
13538             p = this.getParentElement();
13539         }
13540         
13541         
13542         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13543             a.push(p);
13544             p = p.parentNode;
13545         }
13546         a.push(this.doc.body);
13547         return a;
13548     },
13549     lastSel : false,
13550     lastSelNode : false,
13551     
13552     
13553     getSelection : function() 
13554     {
13555         this.assignDocWin();
13556         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13557     },
13558     
13559     getSelectedNode: function() 
13560     {
13561         // this may only work on Gecko!!!
13562         
13563         // should we cache this!!!!
13564         
13565         
13566         
13567          
13568         var range = this.createRange(this.getSelection()).cloneRange();
13569         
13570         if (Roo.isIE) {
13571             var parent = range.parentElement();
13572             while (true) {
13573                 var testRange = range.duplicate();
13574                 testRange.moveToElementText(parent);
13575                 if (testRange.inRange(range)) {
13576                     break;
13577                 }
13578                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13579                     break;
13580                 }
13581                 parent = parent.parentElement;
13582             }
13583             return parent;
13584         }
13585         
13586         // is ancestor a text element.
13587         var ac =  range.commonAncestorContainer;
13588         if (ac.nodeType == 3) {
13589             ac = ac.parentNode;
13590         }
13591         
13592         var ar = ac.childNodes;
13593          
13594         var nodes = [];
13595         var other_nodes = [];
13596         var has_other_nodes = false;
13597         for (var i=0;i<ar.length;i++) {
13598             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13599                 continue;
13600             }
13601             // fullly contained node.
13602             
13603             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13604                 nodes.push(ar[i]);
13605                 continue;
13606             }
13607             
13608             // probably selected..
13609             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13610                 other_nodes.push(ar[i]);
13611                 continue;
13612             }
13613             // outer..
13614             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13615                 continue;
13616             }
13617             
13618             
13619             has_other_nodes = true;
13620         }
13621         if (!nodes.length && other_nodes.length) {
13622             nodes= other_nodes;
13623         }
13624         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13625             return false;
13626         }
13627         
13628         return nodes[0];
13629     },
13630     createRange: function(sel)
13631     {
13632         // this has strange effects when using with 
13633         // top toolbar - not sure if it's a great idea.
13634         //this.editor.contentWindow.focus();
13635         if (typeof sel != "undefined") {
13636             try {
13637                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13638             } catch(e) {
13639                 return this.doc.createRange();
13640             }
13641         } else {
13642             return this.doc.createRange();
13643         }
13644     },
13645     getParentElement: function()
13646     {
13647         
13648         this.assignDocWin();
13649         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13650         
13651         var range = this.createRange(sel);
13652          
13653         try {
13654             var p = range.commonAncestorContainer;
13655             while (p.nodeType == 3) { // text node
13656                 p = p.parentNode;
13657             }
13658             return p;
13659         } catch (e) {
13660             return null;
13661         }
13662     
13663     },
13664     /***
13665      *
13666      * Range intersection.. the hard stuff...
13667      *  '-1' = before
13668      *  '0' = hits..
13669      *  '1' = after.
13670      *         [ -- selected range --- ]
13671      *   [fail]                        [fail]
13672      *
13673      *    basically..
13674      *      if end is before start or  hits it. fail.
13675      *      if start is after end or hits it fail.
13676      *
13677      *   if either hits (but other is outside. - then it's not 
13678      *   
13679      *    
13680      **/
13681     
13682     
13683     // @see http://www.thismuchiknow.co.uk/?p=64.
13684     rangeIntersectsNode : function(range, node)
13685     {
13686         var nodeRange = node.ownerDocument.createRange();
13687         try {
13688             nodeRange.selectNode(node);
13689         } catch (e) {
13690             nodeRange.selectNodeContents(node);
13691         }
13692     
13693         var rangeStartRange = range.cloneRange();
13694         rangeStartRange.collapse(true);
13695     
13696         var rangeEndRange = range.cloneRange();
13697         rangeEndRange.collapse(false);
13698     
13699         var nodeStartRange = nodeRange.cloneRange();
13700         nodeStartRange.collapse(true);
13701     
13702         var nodeEndRange = nodeRange.cloneRange();
13703         nodeEndRange.collapse(false);
13704     
13705         return rangeStartRange.compareBoundaryPoints(
13706                  Range.START_TO_START, nodeEndRange) == -1 &&
13707                rangeEndRange.compareBoundaryPoints(
13708                  Range.START_TO_START, nodeStartRange) == 1;
13709         
13710          
13711     },
13712     rangeCompareNode : function(range, node)
13713     {
13714         var nodeRange = node.ownerDocument.createRange();
13715         try {
13716             nodeRange.selectNode(node);
13717         } catch (e) {
13718             nodeRange.selectNodeContents(node);
13719         }
13720         
13721         
13722         range.collapse(true);
13723     
13724         nodeRange.collapse(true);
13725      
13726         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13727         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13728          
13729         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13730         
13731         var nodeIsBefore   =  ss == 1;
13732         var nodeIsAfter    = ee == -1;
13733         
13734         if (nodeIsBefore && nodeIsAfter)
13735             return 0; // outer
13736         if (!nodeIsBefore && nodeIsAfter)
13737             return 1; //right trailed.
13738         
13739         if (nodeIsBefore && !nodeIsAfter)
13740             return 2;  // left trailed.
13741         // fully contined.
13742         return 3;
13743     },
13744
13745     // private? - in a new class?
13746     cleanUpPaste :  function()
13747     {
13748         // cleans up the whole document..
13749         Roo.log('cleanuppaste');
13750         
13751         this.cleanUpChildren(this.doc.body);
13752         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13753         if (clean != this.doc.body.innerHTML) {
13754             this.doc.body.innerHTML = clean;
13755         }
13756         
13757     },
13758     
13759     cleanWordChars : function(input) {// change the chars to hex code
13760         var he = Roo.HtmlEditorCore;
13761         
13762         var output = input;
13763         Roo.each(he.swapCodes, function(sw) { 
13764             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13765             
13766             output = output.replace(swapper, sw[1]);
13767         });
13768         
13769         return output;
13770     },
13771     
13772     
13773     cleanUpChildren : function (n)
13774     {
13775         if (!n.childNodes.length) {
13776             return;
13777         }
13778         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13779            this.cleanUpChild(n.childNodes[i]);
13780         }
13781     },
13782     
13783     
13784         
13785     
13786     cleanUpChild : function (node)
13787     {
13788         var ed = this;
13789         //console.log(node);
13790         if (node.nodeName == "#text") {
13791             // clean up silly Windows -- stuff?
13792             return; 
13793         }
13794         if (node.nodeName == "#comment") {
13795             node.parentNode.removeChild(node);
13796             // clean up silly Windows -- stuff?
13797             return; 
13798         }
13799         
13800         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13801             // remove node.
13802             node.parentNode.removeChild(node);
13803             return;
13804             
13805         }
13806         
13807         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13808         
13809         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13810         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13811         
13812         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13813         //    remove_keep_children = true;
13814         //}
13815         
13816         if (remove_keep_children) {
13817             this.cleanUpChildren(node);
13818             // inserts everything just before this node...
13819             while (node.childNodes.length) {
13820                 var cn = node.childNodes[0];
13821                 node.removeChild(cn);
13822                 node.parentNode.insertBefore(cn, node);
13823             }
13824             node.parentNode.removeChild(node);
13825             return;
13826         }
13827         
13828         if (!node.attributes || !node.attributes.length) {
13829             this.cleanUpChildren(node);
13830             return;
13831         }
13832         
13833         function cleanAttr(n,v)
13834         {
13835             
13836             if (v.match(/^\./) || v.match(/^\//)) {
13837                 return;
13838             }
13839             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13840                 return;
13841             }
13842             if (v.match(/^#/)) {
13843                 return;
13844             }
13845 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13846             node.removeAttribute(n);
13847             
13848         }
13849         
13850         function cleanStyle(n,v)
13851         {
13852             if (v.match(/expression/)) { //XSS?? should we even bother..
13853                 node.removeAttribute(n);
13854                 return;
13855             }
13856             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13857             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13858             
13859             
13860             var parts = v.split(/;/);
13861             var clean = [];
13862             
13863             Roo.each(parts, function(p) {
13864                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13865                 if (!p.length) {
13866                     return true;
13867                 }
13868                 var l = p.split(':').shift().replace(/\s+/g,'');
13869                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13870                 
13871                 if ( cblack.indexOf(l) > -1) {
13872 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13873                     //node.removeAttribute(n);
13874                     return true;
13875                 }
13876                 //Roo.log()
13877                 // only allow 'c whitelisted system attributes'
13878                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13879 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13880                     //node.removeAttribute(n);
13881                     return true;
13882                 }
13883                 
13884                 
13885                  
13886                 
13887                 clean.push(p);
13888                 return true;
13889             });
13890             if (clean.length) { 
13891                 node.setAttribute(n, clean.join(';'));
13892             } else {
13893                 node.removeAttribute(n);
13894             }
13895             
13896         }
13897         
13898         
13899         for (var i = node.attributes.length-1; i > -1 ; i--) {
13900             var a = node.attributes[i];
13901             //console.log(a);
13902             
13903             if (a.name.toLowerCase().substr(0,2)=='on')  {
13904                 node.removeAttribute(a.name);
13905                 continue;
13906             }
13907             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13908                 node.removeAttribute(a.name);
13909                 continue;
13910             }
13911             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13912                 cleanAttr(a.name,a.value); // fixme..
13913                 continue;
13914             }
13915             if (a.name == 'style') {
13916                 cleanStyle(a.name,a.value);
13917                 continue;
13918             }
13919             /// clean up MS crap..
13920             // tecnically this should be a list of valid class'es..
13921             
13922             
13923             if (a.name == 'class') {
13924                 if (a.value.match(/^Mso/)) {
13925                     node.className = '';
13926                 }
13927                 
13928                 if (a.value.match(/body/)) {
13929                     node.className = '';
13930                 }
13931                 continue;
13932             }
13933             
13934             // style cleanup!?
13935             // class cleanup?
13936             
13937         }
13938         
13939         
13940         this.cleanUpChildren(node);
13941         
13942         
13943     }
13944     
13945     
13946     // hide stuff that is not compatible
13947     /**
13948      * @event blur
13949      * @hide
13950      */
13951     /**
13952      * @event change
13953      * @hide
13954      */
13955     /**
13956      * @event focus
13957      * @hide
13958      */
13959     /**
13960      * @event specialkey
13961      * @hide
13962      */
13963     /**
13964      * @cfg {String} fieldClass @hide
13965      */
13966     /**
13967      * @cfg {String} focusClass @hide
13968      */
13969     /**
13970      * @cfg {String} autoCreate @hide
13971      */
13972     /**
13973      * @cfg {String} inputType @hide
13974      */
13975     /**
13976      * @cfg {String} invalidClass @hide
13977      */
13978     /**
13979      * @cfg {String} invalidText @hide
13980      */
13981     /**
13982      * @cfg {String} msgFx @hide
13983      */
13984     /**
13985      * @cfg {String} validateOnBlur @hide
13986      */
13987 });
13988
13989 Roo.HtmlEditorCore.white = [
13990         'area', 'br', 'img', 'input', 'hr', 'wbr',
13991         
13992        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13993        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13994        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13995        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13996        'table',   'ul',         'xmp', 
13997        
13998        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13999       'thead',   'tr', 
14000      
14001       'dir', 'menu', 'ol', 'ul', 'dl',
14002        
14003       'embed',  'object'
14004 ];
14005
14006
14007 Roo.HtmlEditorCore.black = [
14008     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14009         'applet', // 
14010         'base',   'basefont', 'bgsound', 'blink',  'body', 
14011         'frame',  'frameset', 'head',    'html',   'ilayer', 
14012         'iframe', 'layer',  'link',     'meta',    'object',   
14013         'script', 'style' ,'title',  'xml' // clean later..
14014 ];
14015 Roo.HtmlEditorCore.clean = [
14016     'script', 'style', 'title', 'xml'
14017 ];
14018 Roo.HtmlEditorCore.remove = [
14019     'font'
14020 ];
14021 // attributes..
14022
14023 Roo.HtmlEditorCore.ablack = [
14024     'on'
14025 ];
14026     
14027 Roo.HtmlEditorCore.aclean = [ 
14028     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14029 ];
14030
14031 // protocols..
14032 Roo.HtmlEditorCore.pwhite= [
14033         'http',  'https',  'mailto'
14034 ];
14035
14036 // white listed style attributes.
14037 Roo.HtmlEditorCore.cwhite= [
14038       //  'text-align', /// default is to allow most things..
14039       
14040          
14041 //        'font-size'//??
14042 ];
14043
14044 // black listed style attributes.
14045 Roo.HtmlEditorCore.cblack= [
14046       //  'font-size' -- this can be set by the project 
14047 ];
14048
14049
14050 Roo.HtmlEditorCore.swapCodes   =[ 
14051     [    8211, "--" ], 
14052     [    8212, "--" ], 
14053     [    8216,  "'" ],  
14054     [    8217, "'" ],  
14055     [    8220, '"' ],  
14056     [    8221, '"' ],  
14057     [    8226, "*" ],  
14058     [    8230, "..." ]
14059 ]; 
14060
14061     /*
14062  * - LGPL
14063  *
14064  * HtmlEditor
14065  * 
14066  */
14067
14068 /**
14069  * @class Roo.bootstrap.HtmlEditor
14070  * @extends Roo.bootstrap.TextArea
14071  * Bootstrap HtmlEditor class
14072
14073  * @constructor
14074  * Create a new HtmlEditor
14075  * @param {Object} config The config object
14076  */
14077
14078 Roo.bootstrap.HtmlEditor = function(config){
14079     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14080     if (!this.toolbars) {
14081         this.toolbars = [];
14082     }
14083     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14084     this.addEvents({
14085             /**
14086              * @event initialize
14087              * Fires when the editor is fully initialized (including the iframe)
14088              * @param {HtmlEditor} this
14089              */
14090             initialize: true,
14091             /**
14092              * @event activate
14093              * Fires when the editor is first receives the focus. Any insertion must wait
14094              * until after this event.
14095              * @param {HtmlEditor} this
14096              */
14097             activate: true,
14098              /**
14099              * @event beforesync
14100              * Fires before the textarea is updated with content from the editor iframe. Return false
14101              * to cancel the sync.
14102              * @param {HtmlEditor} this
14103              * @param {String} html
14104              */
14105             beforesync: true,
14106              /**
14107              * @event beforepush
14108              * Fires before the iframe editor is updated with content from the textarea. Return false
14109              * to cancel the push.
14110              * @param {HtmlEditor} this
14111              * @param {String} html
14112              */
14113             beforepush: true,
14114              /**
14115              * @event sync
14116              * Fires when the textarea is updated with content from the editor iframe.
14117              * @param {HtmlEditor} this
14118              * @param {String} html
14119              */
14120             sync: true,
14121              /**
14122              * @event push
14123              * Fires when the iframe editor is updated with content from the textarea.
14124              * @param {HtmlEditor} this
14125              * @param {String} html
14126              */
14127             push: true,
14128              /**
14129              * @event editmodechange
14130              * Fires when the editor switches edit modes
14131              * @param {HtmlEditor} this
14132              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14133              */
14134             editmodechange: true,
14135             /**
14136              * @event editorevent
14137              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14138              * @param {HtmlEditor} this
14139              */
14140             editorevent: true,
14141             /**
14142              * @event firstfocus
14143              * Fires when on first focus - needed by toolbars..
14144              * @param {HtmlEditor} this
14145              */
14146             firstfocus: true,
14147             /**
14148              * @event autosave
14149              * Auto save the htmlEditor value as a file into Events
14150              * @param {HtmlEditor} this
14151              */
14152             autosave: true,
14153             /**
14154              * @event savedpreview
14155              * preview the saved version of htmlEditor
14156              * @param {HtmlEditor} this
14157              */
14158             savedpreview: true
14159         });
14160 };
14161
14162
14163 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14164     
14165     
14166       /**
14167      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14168      */
14169     toolbars : false,
14170    
14171      /**
14172      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14173      *                        Roo.resizable.
14174      */
14175     resizable : false,
14176      /**
14177      * @cfg {Number} height (in pixels)
14178      */   
14179     height: 300,
14180    /**
14181      * @cfg {Number} width (in pixels)
14182      */   
14183     width: false,
14184     
14185     /**
14186      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14187      * 
14188      */
14189     stylesheets: false,
14190     
14191     // id of frame..
14192     frameId: false,
14193     
14194     // private properties
14195     validationEvent : false,
14196     deferHeight: true,
14197     initialized : false,
14198     activated : false,
14199     
14200     onFocus : Roo.emptyFn,
14201     iframePad:3,
14202     hideMode:'offsets',
14203     
14204     
14205     tbContainer : false,
14206     
14207     toolbarContainer :function() {
14208         return this.wrap.select('.x-html-editor-tb',true).first();
14209     },
14210
14211     /**
14212      * Protected method that will not generally be called directly. It
14213      * is called when the editor creates its toolbar. Override this method if you need to
14214      * add custom toolbar buttons.
14215      * @param {HtmlEditor} editor
14216      */
14217     createToolbar : function(){
14218         
14219         Roo.log("create toolbars");
14220         
14221         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14222         this.toolbars[0].render(this.toolbarContainer());
14223         
14224         return;
14225         
14226 //        if (!editor.toolbars || !editor.toolbars.length) {
14227 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14228 //        }
14229 //        
14230 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14231 //            editor.toolbars[i] = Roo.factory(
14232 //                    typeof(editor.toolbars[i]) == 'string' ?
14233 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14234 //                Roo.bootstrap.HtmlEditor);
14235 //            editor.toolbars[i].init(editor);
14236 //        }
14237     },
14238
14239      
14240     // private
14241     onRender : function(ct, position)
14242     {
14243        // Roo.log("Call onRender: " + this.xtype);
14244         var _t = this;
14245         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14246       
14247         this.wrap = this.inputEl().wrap({
14248             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14249         });
14250         
14251         this.editorcore.onRender(ct, position);
14252          
14253         if (this.resizable) {
14254             this.resizeEl = new Roo.Resizable(this.wrap, {
14255                 pinned : true,
14256                 wrap: true,
14257                 dynamic : true,
14258                 minHeight : this.height,
14259                 height: this.height,
14260                 handles : this.resizable,
14261                 width: this.width,
14262                 listeners : {
14263                     resize : function(r, w, h) {
14264                         _t.onResize(w,h); // -something
14265                     }
14266                 }
14267             });
14268             
14269         }
14270         this.createToolbar(this);
14271        
14272         
14273         if(!this.width && this.resizable){
14274             this.setSize(this.wrap.getSize());
14275         }
14276         if (this.resizeEl) {
14277             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14278             // should trigger onReize..
14279         }
14280         
14281     },
14282
14283     // private
14284     onResize : function(w, h)
14285     {
14286         Roo.log('resize: ' +w + ',' + h );
14287         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14288         var ew = false;
14289         var eh = false;
14290         
14291         if(this.inputEl() ){
14292             if(typeof w == 'number'){
14293                 var aw = w - this.wrap.getFrameWidth('lr');
14294                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14295                 ew = aw;
14296             }
14297             if(typeof h == 'number'){
14298                  var tbh = -11;  // fixme it needs to tool bar size!
14299                 for (var i =0; i < this.toolbars.length;i++) {
14300                     // fixme - ask toolbars for heights?
14301                     tbh += this.toolbars[i].el.getHeight();
14302                     //if (this.toolbars[i].footer) {
14303                     //    tbh += this.toolbars[i].footer.el.getHeight();
14304                     //}
14305                 }
14306               
14307                 
14308                 
14309                 
14310                 
14311                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14312                 ah -= 5; // knock a few pixes off for look..
14313                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14314                 var eh = ah;
14315             }
14316         }
14317         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14318         this.editorcore.onResize(ew,eh);
14319         
14320     },
14321
14322     /**
14323      * Toggles the editor between standard and source edit mode.
14324      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14325      */
14326     toggleSourceEdit : function(sourceEditMode)
14327     {
14328         this.editorcore.toggleSourceEdit(sourceEditMode);
14329         
14330         if(this.editorcore.sourceEditMode){
14331             Roo.log('editor - showing textarea');
14332             
14333 //            Roo.log('in');
14334 //            Roo.log(this.syncValue());
14335             this.syncValue();
14336             this.inputEl().removeClass('hide');
14337             this.inputEl().dom.removeAttribute('tabIndex');
14338             this.inputEl().focus();
14339         }else{
14340             Roo.log('editor - hiding textarea');
14341 //            Roo.log('out')
14342 //            Roo.log(this.pushValue()); 
14343             this.pushValue();
14344             
14345             this.inputEl().addClass('hide');
14346             this.inputEl().dom.setAttribute('tabIndex', -1);
14347             //this.deferFocus();
14348         }
14349          
14350         if(this.resizable){
14351             this.setSize(this.wrap.getSize());
14352         }
14353         
14354         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14355     },
14356  
14357     // private (for BoxComponent)
14358     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14359
14360     // private (for BoxComponent)
14361     getResizeEl : function(){
14362         return this.wrap;
14363     },
14364
14365     // private (for BoxComponent)
14366     getPositionEl : function(){
14367         return this.wrap;
14368     },
14369
14370     // private
14371     initEvents : function(){
14372         this.originalValue = this.getValue();
14373     },
14374
14375 //    /**
14376 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14377 //     * @method
14378 //     */
14379 //    markInvalid : Roo.emptyFn,
14380 //    /**
14381 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14382 //     * @method
14383 //     */
14384 //    clearInvalid : Roo.emptyFn,
14385
14386     setValue : function(v){
14387         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14388         this.editorcore.pushValue();
14389     },
14390
14391      
14392     // private
14393     deferFocus : function(){
14394         this.focus.defer(10, this);
14395     },
14396
14397     // doc'ed in Field
14398     focus : function(){
14399         this.editorcore.focus();
14400         
14401     },
14402       
14403
14404     // private
14405     onDestroy : function(){
14406         
14407         
14408         
14409         if(this.rendered){
14410             
14411             for (var i =0; i < this.toolbars.length;i++) {
14412                 // fixme - ask toolbars for heights?
14413                 this.toolbars[i].onDestroy();
14414             }
14415             
14416             this.wrap.dom.innerHTML = '';
14417             this.wrap.remove();
14418         }
14419     },
14420
14421     // private
14422     onFirstFocus : function(){
14423         //Roo.log("onFirstFocus");
14424         this.editorcore.onFirstFocus();
14425          for (var i =0; i < this.toolbars.length;i++) {
14426             this.toolbars[i].onFirstFocus();
14427         }
14428         
14429     },
14430     
14431     // private
14432     syncValue : function()
14433     {   
14434         this.editorcore.syncValue();
14435     },
14436     
14437     pushValue : function()
14438     {   
14439         this.editorcore.pushValue();
14440     }
14441      
14442     
14443     // hide stuff that is not compatible
14444     /**
14445      * @event blur
14446      * @hide
14447      */
14448     /**
14449      * @event change
14450      * @hide
14451      */
14452     /**
14453      * @event focus
14454      * @hide
14455      */
14456     /**
14457      * @event specialkey
14458      * @hide
14459      */
14460     /**
14461      * @cfg {String} fieldClass @hide
14462      */
14463     /**
14464      * @cfg {String} focusClass @hide
14465      */
14466     /**
14467      * @cfg {String} autoCreate @hide
14468      */
14469     /**
14470      * @cfg {String} inputType @hide
14471      */
14472     /**
14473      * @cfg {String} invalidClass @hide
14474      */
14475     /**
14476      * @cfg {String} invalidText @hide
14477      */
14478     /**
14479      * @cfg {String} msgFx @hide
14480      */
14481     /**
14482      * @cfg {String} validateOnBlur @hide
14483      */
14484 });
14485  
14486     
14487    
14488    
14489    
14490       
14491
14492 /**
14493  * @class Roo.bootstrap.HtmlEditorToolbar1
14494  * Basic Toolbar
14495  * 
14496  * Usage:
14497  *
14498  new Roo.bootstrap.HtmlEditor({
14499     ....
14500     toolbars : [
14501         new Roo.bootstrap.HtmlEditorToolbar1({
14502             disable : { fonts: 1 , format: 1, ..., ... , ...],
14503             btns : [ .... ]
14504         })
14505     }
14506      
14507  * 
14508  * @cfg {Object} disable List of elements to disable..
14509  * @cfg {Array} btns List of additional buttons.
14510  * 
14511  * 
14512  * NEEDS Extra CSS? 
14513  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14514  */
14515  
14516 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14517 {
14518     
14519     Roo.apply(this, config);
14520     
14521     // default disabled, based on 'good practice'..
14522     this.disable = this.disable || {};
14523     Roo.applyIf(this.disable, {
14524         fontSize : true,
14525         colors : true,
14526         specialElements : true
14527     });
14528     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14529     
14530     this.editor = config.editor;
14531     this.editorcore = config.editor.editorcore;
14532     
14533     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14534     
14535     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14536     // dont call parent... till later.
14537 }
14538 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14539     
14540     
14541     bar : true,
14542     
14543     editor : false,
14544     editorcore : false,
14545     
14546     
14547     formats : [
14548         "p" ,  
14549         "h1","h2","h3","h4","h5","h6", 
14550         "pre", "code", 
14551         "abbr", "acronym", "address", "cite", "samp", "var",
14552         'div','span'
14553     ],
14554     
14555     onRender : function(ct, position)
14556     {
14557        // Roo.log("Call onRender: " + this.xtype);
14558         
14559        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14560        Roo.log(this.el);
14561        this.el.dom.style.marginBottom = '0';
14562        var _this = this;
14563        var editorcore = this.editorcore;
14564        var editor= this.editor;
14565        
14566        var children = [];
14567        var btn = function(id,cmd , toggle, handler){
14568        
14569             var  event = toggle ? 'toggle' : 'click';
14570        
14571             var a = {
14572                 size : 'sm',
14573                 xtype: 'Button',
14574                 xns: Roo.bootstrap,
14575                 glyphicon : id,
14576                 cmd : id || cmd,
14577                 enableToggle:toggle !== false,
14578                 //html : 'submit'
14579                 pressed : toggle ? false : null,
14580                 listeners : {}
14581             }
14582             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14583                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14584             }
14585             children.push(a);
14586             return a;
14587        }
14588         
14589         var style = {
14590                 xtype: 'Button',
14591                 size : 'sm',
14592                 xns: Roo.bootstrap,
14593                 glyphicon : 'font',
14594                 //html : 'submit'
14595                 menu : {
14596                     xtype: 'Menu',
14597                     xns: Roo.bootstrap,
14598                     items:  []
14599                 }
14600         };
14601         Roo.each(this.formats, function(f) {
14602             style.menu.items.push({
14603                 xtype :'MenuItem',
14604                 xns: Roo.bootstrap,
14605                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14606                 tagname : f,
14607                 listeners : {
14608                     click : function()
14609                     {
14610                         editorcore.insertTag(this.tagname);
14611                         editor.focus();
14612                     }
14613                 }
14614                 
14615             });
14616         });
14617          children.push(style);   
14618             
14619             
14620         btn('bold',false,true);
14621         btn('italic',false,true);
14622         btn('align-left', 'justifyleft',true);
14623         btn('align-center', 'justifycenter',true);
14624         btn('align-right' , 'justifyright',true);
14625         btn('link', false, false, function(btn) {
14626             //Roo.log("create link?");
14627             var url = prompt(this.createLinkText, this.defaultLinkValue);
14628             if(url && url != 'http:/'+'/'){
14629                 this.editorcore.relayCmd('createlink', url);
14630             }
14631         }),
14632         btn('list','insertunorderedlist',true);
14633         btn('pencil', false,true, function(btn){
14634                 Roo.log(this);
14635                 
14636                 this.toggleSourceEdit(btn.pressed);
14637         });
14638         /*
14639         var cog = {
14640                 xtype: 'Button',
14641                 size : 'sm',
14642                 xns: Roo.bootstrap,
14643                 glyphicon : 'cog',
14644                 //html : 'submit'
14645                 menu : {
14646                     xtype: 'Menu',
14647                     xns: Roo.bootstrap,
14648                     items:  []
14649                 }
14650         };
14651         
14652         cog.menu.items.push({
14653             xtype :'MenuItem',
14654             xns: Roo.bootstrap,
14655             html : Clean styles,
14656             tagname : f,
14657             listeners : {
14658                 click : function()
14659                 {
14660                     editorcore.insertTag(this.tagname);
14661                     editor.focus();
14662                 }
14663             }
14664             
14665         });
14666        */
14667         
14668          
14669        this.xtype = 'Navbar';
14670         
14671         for(var i=0;i< children.length;i++) {
14672             
14673             this.buttons.add(this.addxtypeChild(children[i]));
14674             
14675         }
14676         
14677         editor.on('editorevent', this.updateToolbar, this);
14678     },
14679     onBtnClick : function(id)
14680     {
14681        this.editorcore.relayCmd(id);
14682        this.editorcore.focus();
14683     },
14684     
14685     /**
14686      * Protected method that will not generally be called directly. It triggers
14687      * a toolbar update by reading the markup state of the current selection in the editor.
14688      */
14689     updateToolbar: function(){
14690
14691         if(!this.editorcore.activated){
14692             this.editor.onFirstFocus(); // is this neeed?
14693             return;
14694         }
14695
14696         var btns = this.buttons; 
14697         var doc = this.editorcore.doc;
14698         btns.get('bold').setActive(doc.queryCommandState('bold'));
14699         btns.get('italic').setActive(doc.queryCommandState('italic'));
14700         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14701         
14702         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14703         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14704         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14705         
14706         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14707         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14708          /*
14709         
14710         var ans = this.editorcore.getAllAncestors();
14711         if (this.formatCombo) {
14712             
14713             
14714             var store = this.formatCombo.store;
14715             this.formatCombo.setValue("");
14716             for (var i =0; i < ans.length;i++) {
14717                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14718                     // select it..
14719                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14720                     break;
14721                 }
14722             }
14723         }
14724         
14725         
14726         
14727         // hides menus... - so this cant be on a menu...
14728         Roo.bootstrap.MenuMgr.hideAll();
14729         */
14730         Roo.bootstrap.MenuMgr.hideAll();
14731         //this.editorsyncValue();
14732     },
14733     onFirstFocus: function() {
14734         this.buttons.each(function(item){
14735            item.enable();
14736         });
14737     },
14738     toggleSourceEdit : function(sourceEditMode){
14739         
14740           
14741         if(sourceEditMode){
14742             Roo.log("disabling buttons");
14743            this.buttons.each( function(item){
14744                 if(item.cmd != 'pencil'){
14745                     item.disable();
14746                 }
14747             });
14748           
14749         }else{
14750             Roo.log("enabling buttons");
14751             if(this.editorcore.initialized){
14752                 this.buttons.each( function(item){
14753                     item.enable();
14754                 });
14755             }
14756             
14757         }
14758         Roo.log("calling toggole on editor");
14759         // tell the editor that it's been pressed..
14760         this.editor.toggleSourceEdit(sourceEditMode);
14761        
14762     }
14763 });
14764
14765
14766
14767
14768
14769 /**
14770  * @class Roo.bootstrap.Table.AbstractSelectionModel
14771  * @extends Roo.util.Observable
14772  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14773  * implemented by descendant classes.  This class should not be directly instantiated.
14774  * @constructor
14775  */
14776 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14777     this.locked = false;
14778     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14779 };
14780
14781
14782 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14783     /** @ignore Called by the grid automatically. Do not call directly. */
14784     init : function(grid){
14785         this.grid = grid;
14786         this.initEvents();
14787     },
14788
14789     /**
14790      * Locks the selections.
14791      */
14792     lock : function(){
14793         this.locked = true;
14794     },
14795
14796     /**
14797      * Unlocks the selections.
14798      */
14799     unlock : function(){
14800         this.locked = false;
14801     },
14802
14803     /**
14804      * Returns true if the selections are locked.
14805      * @return {Boolean}
14806      */
14807     isLocked : function(){
14808         return this.locked;
14809     }
14810 });
14811 /**
14812  * @class Roo.bootstrap.Table.ColumnModel
14813  * @extends Roo.util.Observable
14814  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14815  * the columns in the table.
14816  
14817  * @constructor
14818  * @param {Object} config An Array of column config objects. See this class's
14819  * config objects for details.
14820 */
14821 Roo.bootstrap.Table.ColumnModel = function(config){
14822         /**
14823      * The config passed into the constructor
14824      */
14825     this.config = config;
14826     this.lookup = {};
14827
14828     // if no id, create one
14829     // if the column does not have a dataIndex mapping,
14830     // map it to the order it is in the config
14831     for(var i = 0, len = config.length; i < len; i++){
14832         var c = config[i];
14833         if(typeof c.dataIndex == "undefined"){
14834             c.dataIndex = i;
14835         }
14836         if(typeof c.renderer == "string"){
14837             c.renderer = Roo.util.Format[c.renderer];
14838         }
14839         if(typeof c.id == "undefined"){
14840             c.id = Roo.id();
14841         }
14842 //        if(c.editor && c.editor.xtype){
14843 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14844 //        }
14845 //        if(c.editor && c.editor.isFormField){
14846 //            c.editor = new Roo.grid.GridEditor(c.editor);
14847 //        }
14848
14849         this.lookup[c.id] = c;
14850     }
14851
14852     /**
14853      * The width of columns which have no width specified (defaults to 100)
14854      * @type Number
14855      */
14856     this.defaultWidth = 100;
14857
14858     /**
14859      * Default sortable of columns which have no sortable specified (defaults to false)
14860      * @type Boolean
14861      */
14862     this.defaultSortable = false;
14863
14864     this.addEvents({
14865         /**
14866              * @event widthchange
14867              * Fires when the width of a column changes.
14868              * @param {ColumnModel} this
14869              * @param {Number} columnIndex The column index
14870              * @param {Number} newWidth The new width
14871              */
14872             "widthchange": true,
14873         /**
14874              * @event headerchange
14875              * Fires when the text of a header changes.
14876              * @param {ColumnModel} this
14877              * @param {Number} columnIndex The column index
14878              * @param {Number} newText The new header text
14879              */
14880             "headerchange": true,
14881         /**
14882              * @event hiddenchange
14883              * Fires when a column is hidden or "unhidden".
14884              * @param {ColumnModel} this
14885              * @param {Number} columnIndex The column index
14886              * @param {Boolean} hidden true if hidden, false otherwise
14887              */
14888             "hiddenchange": true,
14889             /**
14890          * @event columnmoved
14891          * Fires when a column is moved.
14892          * @param {ColumnModel} this
14893          * @param {Number} oldIndex
14894          * @param {Number} newIndex
14895          */
14896         "columnmoved" : true,
14897         /**
14898          * @event columlockchange
14899          * Fires when a column's locked state is changed
14900          * @param {ColumnModel} this
14901          * @param {Number} colIndex
14902          * @param {Boolean} locked true if locked
14903          */
14904         "columnlockchange" : true
14905     });
14906     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14907 };
14908 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14909     /**
14910      * @cfg {String} header The header text to display in the Grid view.
14911      */
14912     /**
14913      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14914      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14915      * specified, the column's index is used as an index into the Record's data Array.
14916      */
14917     /**
14918      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14919      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14920      */
14921     /**
14922      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14923      * Defaults to the value of the {@link #defaultSortable} property.
14924      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14925      */
14926     /**
14927      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14928      */
14929     /**
14930      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14931      */
14932     /**
14933      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14934      */
14935     /**
14936      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14937      */
14938     /**
14939      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14940      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14941      * default renderer uses the raw data value.
14942      */
14943     /**
14944      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14945      */
14946
14947     /**
14948      * Returns the id of the column at the specified index.
14949      * @param {Number} index The column index
14950      * @return {String} the id
14951      */
14952     getColumnId : function(index){
14953         return this.config[index].id;
14954     },
14955
14956     /**
14957      * Returns the column for a specified id.
14958      * @param {String} id The column id
14959      * @return {Object} the column
14960      */
14961     getColumnById : function(id){
14962         return this.lookup[id];
14963     },
14964
14965     
14966     /**
14967      * Returns the column for a specified dataIndex.
14968      * @param {String} dataIndex The column dataIndex
14969      * @return {Object|Boolean} the column or false if not found
14970      */
14971     getColumnByDataIndex: function(dataIndex){
14972         var index = this.findColumnIndex(dataIndex);
14973         return index > -1 ? this.config[index] : false;
14974     },
14975     
14976     /**
14977      * Returns the index for a specified column id.
14978      * @param {String} id The column id
14979      * @return {Number} the index, or -1 if not found
14980      */
14981     getIndexById : function(id){
14982         for(var i = 0, len = this.config.length; i < len; i++){
14983             if(this.config[i].id == id){
14984                 return i;
14985             }
14986         }
14987         return -1;
14988     },
14989     
14990     /**
14991      * Returns the index for a specified column dataIndex.
14992      * @param {String} dataIndex The column dataIndex
14993      * @return {Number} the index, or -1 if not found
14994      */
14995     
14996     findColumnIndex : function(dataIndex){
14997         for(var i = 0, len = this.config.length; i < len; i++){
14998             if(this.config[i].dataIndex == dataIndex){
14999                 return i;
15000             }
15001         }
15002         return -1;
15003     },
15004     
15005     
15006     moveColumn : function(oldIndex, newIndex){
15007         var c = this.config[oldIndex];
15008         this.config.splice(oldIndex, 1);
15009         this.config.splice(newIndex, 0, c);
15010         this.dataMap = null;
15011         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15012     },
15013
15014     isLocked : function(colIndex){
15015         return this.config[colIndex].locked === true;
15016     },
15017
15018     setLocked : function(colIndex, value, suppressEvent){
15019         if(this.isLocked(colIndex) == value){
15020             return;
15021         }
15022         this.config[colIndex].locked = value;
15023         if(!suppressEvent){
15024             this.fireEvent("columnlockchange", this, colIndex, value);
15025         }
15026     },
15027
15028     getTotalLockedWidth : function(){
15029         var totalWidth = 0;
15030         for(var i = 0; i < this.config.length; i++){
15031             if(this.isLocked(i) && !this.isHidden(i)){
15032                 this.totalWidth += this.getColumnWidth(i);
15033             }
15034         }
15035         return totalWidth;
15036     },
15037
15038     getLockedCount : function(){
15039         for(var i = 0, len = this.config.length; i < len; i++){
15040             if(!this.isLocked(i)){
15041                 return i;
15042             }
15043         }
15044     },
15045
15046     /**
15047      * Returns the number of columns.
15048      * @return {Number}
15049      */
15050     getColumnCount : function(visibleOnly){
15051         if(visibleOnly === true){
15052             var c = 0;
15053             for(var i = 0, len = this.config.length; i < len; i++){
15054                 if(!this.isHidden(i)){
15055                     c++;
15056                 }
15057             }
15058             return c;
15059         }
15060         return this.config.length;
15061     },
15062
15063     /**
15064      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15065      * @param {Function} fn
15066      * @param {Object} scope (optional)
15067      * @return {Array} result
15068      */
15069     getColumnsBy : function(fn, scope){
15070         var r = [];
15071         for(var i = 0, len = this.config.length; i < len; i++){
15072             var c = this.config[i];
15073             if(fn.call(scope||this, c, i) === true){
15074                 r[r.length] = c;
15075             }
15076         }
15077         return r;
15078     },
15079
15080     /**
15081      * Returns true if the specified column is sortable.
15082      * @param {Number} col The column index
15083      * @return {Boolean}
15084      */
15085     isSortable : function(col){
15086         if(typeof this.config[col].sortable == "undefined"){
15087             return this.defaultSortable;
15088         }
15089         return this.config[col].sortable;
15090     },
15091
15092     /**
15093      * Returns the rendering (formatting) function defined for the column.
15094      * @param {Number} col The column index.
15095      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15096      */
15097     getRenderer : function(col){
15098         if(!this.config[col].renderer){
15099             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15100         }
15101         return this.config[col].renderer;
15102     },
15103
15104     /**
15105      * Sets the rendering (formatting) function for a column.
15106      * @param {Number} col The column index
15107      * @param {Function} fn The function to use to process the cell's raw data
15108      * to return HTML markup for the grid view. The render function is called with
15109      * the following parameters:<ul>
15110      * <li>Data value.</li>
15111      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15112      * <li>css A CSS style string to apply to the table cell.</li>
15113      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15114      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15115      * <li>Row index</li>
15116      * <li>Column index</li>
15117      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15118      */
15119     setRenderer : function(col, fn){
15120         this.config[col].renderer = fn;
15121     },
15122
15123     /**
15124      * Returns the width for the specified column.
15125      * @param {Number} col The column index
15126      * @return {Number}
15127      */
15128     getColumnWidth : function(col){
15129         return this.config[col].width * 1 || this.defaultWidth;
15130     },
15131
15132     /**
15133      * Sets the width for a column.
15134      * @param {Number} col The column index
15135      * @param {Number} width The new width
15136      */
15137     setColumnWidth : function(col, width, suppressEvent){
15138         this.config[col].width = width;
15139         this.totalWidth = null;
15140         if(!suppressEvent){
15141              this.fireEvent("widthchange", this, col, width);
15142         }
15143     },
15144
15145     /**
15146      * Returns the total width of all columns.
15147      * @param {Boolean} includeHidden True to include hidden column widths
15148      * @return {Number}
15149      */
15150     getTotalWidth : function(includeHidden){
15151         if(!this.totalWidth){
15152             this.totalWidth = 0;
15153             for(var i = 0, len = this.config.length; i < len; i++){
15154                 if(includeHidden || !this.isHidden(i)){
15155                     this.totalWidth += this.getColumnWidth(i);
15156                 }
15157             }
15158         }
15159         return this.totalWidth;
15160     },
15161
15162     /**
15163      * Returns the header for the specified column.
15164      * @param {Number} col The column index
15165      * @return {String}
15166      */
15167     getColumnHeader : function(col){
15168         return this.config[col].header;
15169     },
15170
15171     /**
15172      * Sets the header for a column.
15173      * @param {Number} col The column index
15174      * @param {String} header The new header
15175      */
15176     setColumnHeader : function(col, header){
15177         this.config[col].header = header;
15178         this.fireEvent("headerchange", this, col, header);
15179     },
15180
15181     /**
15182      * Returns the tooltip for the specified column.
15183      * @param {Number} col The column index
15184      * @return {String}
15185      */
15186     getColumnTooltip : function(col){
15187             return this.config[col].tooltip;
15188     },
15189     /**
15190      * Sets the tooltip for a column.
15191      * @param {Number} col The column index
15192      * @param {String} tooltip The new tooltip
15193      */
15194     setColumnTooltip : function(col, tooltip){
15195             this.config[col].tooltip = tooltip;
15196     },
15197
15198     /**
15199      * Returns the dataIndex for the specified column.
15200      * @param {Number} col The column index
15201      * @return {Number}
15202      */
15203     getDataIndex : function(col){
15204         return this.config[col].dataIndex;
15205     },
15206
15207     /**
15208      * Sets the dataIndex for a column.
15209      * @param {Number} col The column index
15210      * @param {Number} dataIndex The new dataIndex
15211      */
15212     setDataIndex : function(col, dataIndex){
15213         this.config[col].dataIndex = dataIndex;
15214     },
15215
15216     
15217     
15218     /**
15219      * Returns true if the cell is editable.
15220      * @param {Number} colIndex The column index
15221      * @param {Number} rowIndex The row index
15222      * @return {Boolean}
15223      */
15224     isCellEditable : function(colIndex, rowIndex){
15225         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15226     },
15227
15228     /**
15229      * Returns the editor defined for the cell/column.
15230      * return false or null to disable editing.
15231      * @param {Number} colIndex The column index
15232      * @param {Number} rowIndex The row index
15233      * @return {Object}
15234      */
15235     getCellEditor : function(colIndex, rowIndex){
15236         return this.config[colIndex].editor;
15237     },
15238
15239     /**
15240      * Sets if a column is editable.
15241      * @param {Number} col The column index
15242      * @param {Boolean} editable True if the column is editable
15243      */
15244     setEditable : function(col, editable){
15245         this.config[col].editable = editable;
15246     },
15247
15248
15249     /**
15250      * Returns true if the column is hidden.
15251      * @param {Number} colIndex The column index
15252      * @return {Boolean}
15253      */
15254     isHidden : function(colIndex){
15255         return this.config[colIndex].hidden;
15256     },
15257
15258
15259     /**
15260      * Returns true if the column width cannot be changed
15261      */
15262     isFixed : function(colIndex){
15263         return this.config[colIndex].fixed;
15264     },
15265
15266     /**
15267      * Returns true if the column can be resized
15268      * @return {Boolean}
15269      */
15270     isResizable : function(colIndex){
15271         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15272     },
15273     /**
15274      * Sets if a column is hidden.
15275      * @param {Number} colIndex The column index
15276      * @param {Boolean} hidden True if the column is hidden
15277      */
15278     setHidden : function(colIndex, hidden){
15279         this.config[colIndex].hidden = hidden;
15280         this.totalWidth = null;
15281         this.fireEvent("hiddenchange", this, colIndex, hidden);
15282     },
15283
15284     /**
15285      * Sets the editor for a column.
15286      * @param {Number} col The column index
15287      * @param {Object} editor The editor object
15288      */
15289     setEditor : function(col, editor){
15290         this.config[col].editor = editor;
15291     }
15292 });
15293
15294 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15295         if(typeof value == "string" && value.length < 1){
15296             return "&#160;";
15297         }
15298         return value;
15299 };
15300
15301 // Alias for backwards compatibility
15302 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15303
15304 /**
15305  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15306  * @class Roo.bootstrap.Table.RowSelectionModel
15307  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15308  * It supports multiple selections and keyboard selection/navigation. 
15309  * @constructor
15310  * @param {Object} config
15311  */
15312
15313 Roo.bootstrap.Table.RowSelectionModel = function(config){
15314     Roo.apply(this, config);
15315     this.selections = new Roo.util.MixedCollection(false, function(o){
15316         return o.id;
15317     });
15318
15319     this.last = false;
15320     this.lastActive = false;
15321
15322     this.addEvents({
15323         /**
15324              * @event selectionchange
15325              * Fires when the selection changes
15326              * @param {SelectionModel} this
15327              */
15328             "selectionchange" : true,
15329         /**
15330              * @event afterselectionchange
15331              * Fires after the selection changes (eg. by key press or clicking)
15332              * @param {SelectionModel} this
15333              */
15334             "afterselectionchange" : true,
15335         /**
15336              * @event beforerowselect
15337              * Fires when a row is selected being selected, return false to cancel.
15338              * @param {SelectionModel} this
15339              * @param {Number} rowIndex The selected index
15340              * @param {Boolean} keepExisting False if other selections will be cleared
15341              */
15342             "beforerowselect" : true,
15343         /**
15344              * @event rowselect
15345              * Fires when a row is selected.
15346              * @param {SelectionModel} this
15347              * @param {Number} rowIndex The selected index
15348              * @param {Roo.data.Record} r The record
15349              */
15350             "rowselect" : true,
15351         /**
15352              * @event rowdeselect
15353              * Fires when a row is deselected.
15354              * @param {SelectionModel} this
15355              * @param {Number} rowIndex The selected index
15356              */
15357         "rowdeselect" : true
15358     });
15359     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15360     this.locked = false;
15361 };
15362
15363 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15364     /**
15365      * @cfg {Boolean} singleSelect
15366      * True to allow selection of only one row at a time (defaults to false)
15367      */
15368     singleSelect : false,
15369
15370     // private
15371     initEvents : function(){
15372
15373         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15374             this.grid.on("mousedown", this.handleMouseDown, this);
15375         }else{ // allow click to work like normal
15376             this.grid.on("rowclick", this.handleDragableRowClick, this);
15377         }
15378
15379         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15380             "up" : function(e){
15381                 if(!e.shiftKey){
15382                     this.selectPrevious(e.shiftKey);
15383                 }else if(this.last !== false && this.lastActive !== false){
15384                     var last = this.last;
15385                     this.selectRange(this.last,  this.lastActive-1);
15386                     this.grid.getView().focusRow(this.lastActive);
15387                     if(last !== false){
15388                         this.last = last;
15389                     }
15390                 }else{
15391                     this.selectFirstRow();
15392                 }
15393                 this.fireEvent("afterselectionchange", this);
15394             },
15395             "down" : function(e){
15396                 if(!e.shiftKey){
15397                     this.selectNext(e.shiftKey);
15398                 }else if(this.last !== false && this.lastActive !== false){
15399                     var last = this.last;
15400                     this.selectRange(this.last,  this.lastActive+1);
15401                     this.grid.getView().focusRow(this.lastActive);
15402                     if(last !== false){
15403                         this.last = last;
15404                     }
15405                 }else{
15406                     this.selectFirstRow();
15407                 }
15408                 this.fireEvent("afterselectionchange", this);
15409             },
15410             scope: this
15411         });
15412
15413         var view = this.grid.view;
15414         view.on("refresh", this.onRefresh, this);
15415         view.on("rowupdated", this.onRowUpdated, this);
15416         view.on("rowremoved", this.onRemove, this);
15417     },
15418
15419     // private
15420     onRefresh : function(){
15421         var ds = this.grid.dataSource, i, v = this.grid.view;
15422         var s = this.selections;
15423         s.each(function(r){
15424             if((i = ds.indexOfId(r.id)) != -1){
15425                 v.onRowSelect(i);
15426             }else{
15427                 s.remove(r);
15428             }
15429         });
15430     },
15431
15432     // private
15433     onRemove : function(v, index, r){
15434         this.selections.remove(r);
15435     },
15436
15437     // private
15438     onRowUpdated : function(v, index, r){
15439         if(this.isSelected(r)){
15440             v.onRowSelect(index);
15441         }
15442     },
15443
15444     /**
15445      * Select records.
15446      * @param {Array} records The records to select
15447      * @param {Boolean} keepExisting (optional) True to keep existing selections
15448      */
15449     selectRecords : function(records, keepExisting){
15450         if(!keepExisting){
15451             this.clearSelections();
15452         }
15453         var ds = this.grid.dataSource;
15454         for(var i = 0, len = records.length; i < len; i++){
15455             this.selectRow(ds.indexOf(records[i]), true);
15456         }
15457     },
15458
15459     /**
15460      * Gets the number of selected rows.
15461      * @return {Number}
15462      */
15463     getCount : function(){
15464         return this.selections.length;
15465     },
15466
15467     /**
15468      * Selects the first row in the grid.
15469      */
15470     selectFirstRow : function(){
15471         this.selectRow(0);
15472     },
15473
15474     /**
15475      * Select the last row.
15476      * @param {Boolean} keepExisting (optional) True to keep existing selections
15477      */
15478     selectLastRow : function(keepExisting){
15479         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15480     },
15481
15482     /**
15483      * Selects the row immediately following the last selected row.
15484      * @param {Boolean} keepExisting (optional) True to keep existing selections
15485      */
15486     selectNext : function(keepExisting){
15487         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15488             this.selectRow(this.last+1, keepExisting);
15489             this.grid.getView().focusRow(this.last);
15490         }
15491     },
15492
15493     /**
15494      * Selects the row that precedes the last selected row.
15495      * @param {Boolean} keepExisting (optional) True to keep existing selections
15496      */
15497     selectPrevious : function(keepExisting){
15498         if(this.last){
15499             this.selectRow(this.last-1, keepExisting);
15500             this.grid.getView().focusRow(this.last);
15501         }
15502     },
15503
15504     /**
15505      * Returns the selected records
15506      * @return {Array} Array of selected records
15507      */
15508     getSelections : function(){
15509         return [].concat(this.selections.items);
15510     },
15511
15512     /**
15513      * Returns the first selected record.
15514      * @return {Record}
15515      */
15516     getSelected : function(){
15517         return this.selections.itemAt(0);
15518     },
15519
15520
15521     /**
15522      * Clears all selections.
15523      */
15524     clearSelections : function(fast){
15525         if(this.locked) return;
15526         if(fast !== true){
15527             var ds = this.grid.dataSource;
15528             var s = this.selections;
15529             s.each(function(r){
15530                 this.deselectRow(ds.indexOfId(r.id));
15531             }, this);
15532             s.clear();
15533         }else{
15534             this.selections.clear();
15535         }
15536         this.last = false;
15537     },
15538
15539
15540     /**
15541      * Selects all rows.
15542      */
15543     selectAll : function(){
15544         if(this.locked) return;
15545         this.selections.clear();
15546         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15547             this.selectRow(i, true);
15548         }
15549     },
15550
15551     /**
15552      * Returns True if there is a selection.
15553      * @return {Boolean}
15554      */
15555     hasSelection : function(){
15556         return this.selections.length > 0;
15557     },
15558
15559     /**
15560      * Returns True if the specified row is selected.
15561      * @param {Number/Record} record The record or index of the record to check
15562      * @return {Boolean}
15563      */
15564     isSelected : function(index){
15565         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15566         return (r && this.selections.key(r.id) ? true : false);
15567     },
15568
15569     /**
15570      * Returns True if the specified record id is selected.
15571      * @param {String} id The id of record to check
15572      * @return {Boolean}
15573      */
15574     isIdSelected : function(id){
15575         return (this.selections.key(id) ? true : false);
15576     },
15577
15578     // private
15579     handleMouseDown : function(e, t){
15580         var view = this.grid.getView(), rowIndex;
15581         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15582             return;
15583         };
15584         if(e.shiftKey && this.last !== false){
15585             var last = this.last;
15586             this.selectRange(last, rowIndex, e.ctrlKey);
15587             this.last = last; // reset the last
15588             view.focusRow(rowIndex);
15589         }else{
15590             var isSelected = this.isSelected(rowIndex);
15591             if(e.button !== 0 && isSelected){
15592                 view.focusRow(rowIndex);
15593             }else if(e.ctrlKey && isSelected){
15594                 this.deselectRow(rowIndex);
15595             }else if(!isSelected){
15596                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15597                 view.focusRow(rowIndex);
15598             }
15599         }
15600         this.fireEvent("afterselectionchange", this);
15601     },
15602     // private
15603     handleDragableRowClick :  function(grid, rowIndex, e) 
15604     {
15605         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15606             this.selectRow(rowIndex, false);
15607             grid.view.focusRow(rowIndex);
15608              this.fireEvent("afterselectionchange", this);
15609         }
15610     },
15611     
15612     /**
15613      * Selects multiple rows.
15614      * @param {Array} rows Array of the indexes of the row to select
15615      * @param {Boolean} keepExisting (optional) True to keep existing selections
15616      */
15617     selectRows : function(rows, keepExisting){
15618         if(!keepExisting){
15619             this.clearSelections();
15620         }
15621         for(var i = 0, len = rows.length; i < len; i++){
15622             this.selectRow(rows[i], true);
15623         }
15624     },
15625
15626     /**
15627      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15628      * @param {Number} startRow The index of the first row in the range
15629      * @param {Number} endRow The index of the last row in the range
15630      * @param {Boolean} keepExisting (optional) True to retain existing selections
15631      */
15632     selectRange : function(startRow, endRow, keepExisting){
15633         if(this.locked) return;
15634         if(!keepExisting){
15635             this.clearSelections();
15636         }
15637         if(startRow <= endRow){
15638             for(var i = startRow; i <= endRow; i++){
15639                 this.selectRow(i, true);
15640             }
15641         }else{
15642             for(var i = startRow; i >= endRow; i--){
15643                 this.selectRow(i, true);
15644             }
15645         }
15646     },
15647
15648     /**
15649      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15650      * @param {Number} startRow The index of the first row in the range
15651      * @param {Number} endRow The index of the last row in the range
15652      */
15653     deselectRange : function(startRow, endRow, preventViewNotify){
15654         if(this.locked) return;
15655         for(var i = startRow; i <= endRow; i++){
15656             this.deselectRow(i, preventViewNotify);
15657         }
15658     },
15659
15660     /**
15661      * Selects a row.
15662      * @param {Number} row The index of the row to select
15663      * @param {Boolean} keepExisting (optional) True to keep existing selections
15664      */
15665     selectRow : function(index, keepExisting, preventViewNotify){
15666         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15667         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15668             if(!keepExisting || this.singleSelect){
15669                 this.clearSelections();
15670             }
15671             var r = this.grid.dataSource.getAt(index);
15672             this.selections.add(r);
15673             this.last = this.lastActive = index;
15674             if(!preventViewNotify){
15675                 this.grid.getView().onRowSelect(index);
15676             }
15677             this.fireEvent("rowselect", this, index, r);
15678             this.fireEvent("selectionchange", this);
15679         }
15680     },
15681
15682     /**
15683      * Deselects a row.
15684      * @param {Number} row The index of the row to deselect
15685      */
15686     deselectRow : function(index, preventViewNotify){
15687         if(this.locked) return;
15688         if(this.last == index){
15689             this.last = false;
15690         }
15691         if(this.lastActive == index){
15692             this.lastActive = false;
15693         }
15694         var r = this.grid.dataSource.getAt(index);
15695         this.selections.remove(r);
15696         if(!preventViewNotify){
15697             this.grid.getView().onRowDeselect(index);
15698         }
15699         this.fireEvent("rowdeselect", this, index);
15700         this.fireEvent("selectionchange", this);
15701     },
15702
15703     // private
15704     restoreLast : function(){
15705         if(this._last){
15706             this.last = this._last;
15707         }
15708     },
15709
15710     // private
15711     acceptsNav : function(row, col, cm){
15712         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15713     },
15714
15715     // private
15716     onEditorKey : function(field, e){
15717         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15718         if(k == e.TAB){
15719             e.stopEvent();
15720             ed.completeEdit();
15721             if(e.shiftKey){
15722                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15723             }else{
15724                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15725             }
15726         }else if(k == e.ENTER && !e.ctrlKey){
15727             e.stopEvent();
15728             ed.completeEdit();
15729             if(e.shiftKey){
15730                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15731             }else{
15732                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15733             }
15734         }else if(k == e.ESC){
15735             ed.cancelEdit();
15736         }
15737         if(newCell){
15738             g.startEditing(newCell[0], newCell[1]);
15739         }
15740     }
15741 });/*
15742  * - LGPL
15743  *
15744  * element
15745  * 
15746  */
15747
15748 /**
15749  * @class Roo.bootstrap.MessageBar
15750  * @extends Roo.bootstrap.Component
15751  * Bootstrap MessageBar class
15752  * @cfg {String} html contents of the MessageBar
15753  * @cfg {String} weight (info | success | warning | danger) default info
15754  * @cfg {String} beforeClass insert the bar before the given class
15755  * @cfg {Boolean} closable (true | false) default false
15756  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15757  * 
15758  * @constructor
15759  * Create a new Element
15760  * @param {Object} config The config object
15761  */
15762
15763 Roo.bootstrap.MessageBar = function(config){
15764     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15765 };
15766
15767 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15768     
15769     html: '',
15770     weight: 'info',
15771     closable: false,
15772     fixed: false,
15773     beforeClass: 'bootstrap-sticky-wrap',
15774     
15775     getAutoCreate : function(){
15776         
15777         var cfg = {
15778             tag: 'div',
15779             cls: 'alert alert-dismissable alert-' + this.weight,
15780             cn: [
15781                 {
15782                     tag: 'span',
15783                     cls: 'message',
15784                     html: this.html || ''
15785                 }
15786             ]
15787         }
15788         
15789         if(this.fixed){
15790             cfg.cls += ' alert-messages-fixed';
15791         }
15792         
15793         if(this.closable){
15794             cfg.cn.push({
15795                 tag: 'button',
15796                 cls: 'close',
15797                 html: 'x'
15798             });
15799         }
15800         
15801         return cfg;
15802     },
15803     
15804     onRender : function(ct, position)
15805     {
15806         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15807         
15808         if(!this.el){
15809             var cfg = Roo.apply({},  this.getAutoCreate());
15810             cfg.id = Roo.id();
15811             
15812             if (this.cls) {
15813                 cfg.cls += ' ' + this.cls;
15814             }
15815             if (this.style) {
15816                 cfg.style = this.style;
15817             }
15818             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15819             
15820             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15821         }
15822         
15823         this.el.select('>button.close').on('click', this.hide, this);
15824         
15825     },
15826     
15827     show : function()
15828     {
15829         if (!this.rendered) {
15830             this.render();
15831         }
15832         
15833         this.el.show();
15834         
15835         this.fireEvent('show', this);
15836         
15837     },
15838     
15839     hide : function()
15840     {
15841         if (!this.rendered) {
15842             this.render();
15843         }
15844         
15845         this.el.hide();
15846         
15847         this.fireEvent('hide', this);
15848     },
15849     
15850     update : function()
15851     {
15852 //        var e = this.el.dom.firstChild;
15853 //        
15854 //        if(this.closable){
15855 //            e = e.nextSibling;
15856 //        }
15857 //        
15858 //        e.data = this.html || '';
15859
15860         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15861     }
15862    
15863 });
15864
15865  
15866
15867