Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(a,b,c)
2972     {
2973         Roo.log(a);
2974         Roo.log(b);
2975         Roo.log(c);
2976         //this.store.sortInfo = {field:'created_dt',direction:'DESC'};
2977         
2978         //this.store.load();
2979     },
2980     
2981     renderHeader : function()
2982     {
2983         var header = {
2984             tag: 'thead',
2985             cn : []
2986         };
2987         
2988         var cm = this.cm;
2989         
2990         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2991             
2992             var config = cm.config[i];
2993             
2994             var c = {
2995                 tag: 'th',
2996                 html: cm.getColumnHeader(i)
2997             };
2998             
2999             if(typeof(config.dataIndex) != 'undefined'){
3000                 c.sort = config.dataIndex;
3001             }
3002             
3003             if(typeof(config.sortable) != 'undefined' && config.sortable){
3004                 c.cls = 'sortable';
3005             }
3006             
3007             header.cn.push(c)
3008         }
3009         
3010         return header;
3011     },
3012     
3013     renderBody : function()
3014     {
3015         var body = {
3016             tag: 'tbody',
3017             cn : []
3018         };
3019         
3020         return body;
3021     },
3022     
3023     renderFooter : function()
3024     {
3025         var footer = {
3026             tag: 'tfoot',
3027             cn : []
3028         };
3029         
3030         return footer;
3031     },
3032     
3033     onLoad : function()
3034     {
3035         Roo.log('ds onload');
3036         
3037         var cm = this.cm;
3038         
3039         var tbody = this.el.select('tbody', true).first();
3040         
3041         var renders = [];
3042         
3043         if(this.store.getCount() > 0){
3044             this.store.data.each(function(d){
3045                 var row = {
3046                     tag : 'tr',
3047                     cn : []
3048                 };
3049                 
3050                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3051                     var renderer = cm.getRenderer(i);
3052                     var config = cm.config[i];
3053                     var value = '';
3054                     var id = Roo.id();
3055                     
3056                     if(typeof(renderer) !== 'undefined'){
3057                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3058                     }
3059                     
3060                     if(typeof(value) === 'object'){
3061                         renders.push({
3062                             id : id,
3063                             cfg : value 
3064                         })
3065                     }
3066                     
3067                     var td = {
3068                         tag: 'td',
3069                         id: id,
3070                         html: (typeof(value) === 'object') ? '' : value
3071                     };
3072                     
3073                     if(typeof(config.width) != 'undefined'){
3074                         td.width = config.width;
3075                     }
3076                     
3077                     row.cn.push(td);
3078                    
3079                 }
3080                 
3081                 tbody.createChild(row);
3082                 
3083             });
3084         }
3085         
3086         
3087         if(renders.length){
3088             var _this = this;
3089             Roo.each(renders, function(r){
3090                 _this.renderColumn(r);
3091             })
3092         }
3093 //        
3094 //        if(this.loadMask){
3095 //            this.maskEl.hide();
3096 //        }
3097     },
3098     
3099     onBeforeLoad : function()
3100     {
3101         Roo.log('ds onBeforeLoad');
3102         
3103         this.clear();
3104         
3105 //        if(this.loadMask){
3106 //            this.maskEl.show();
3107 //        }
3108     },
3109     
3110     clear : function()
3111     {
3112         this.el.select('tbody', true).first().dom.innerHTML = '';
3113     },
3114     
3115     getSelectionModel : function(){
3116         if(!this.selModel){
3117             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3118         }
3119         return this.selModel;
3120     },
3121     
3122     renderColumn : function(r)
3123     {
3124         var _this = this;
3125         r.cfg.render(Roo.get(r.id));
3126         
3127         if(r.cfg.cn){
3128             Roo.each(r.cfg.cn, function(c){
3129                 var child = {
3130                     id: r.id,
3131                     cfg: c
3132                 }
3133                 _this.renderColumn(child);
3134             })
3135         }
3136     }
3137    
3138 });
3139
3140  
3141
3142  /*
3143  * - LGPL
3144  *
3145  * table cell
3146  * 
3147  */
3148
3149 /**
3150  * @class Roo.bootstrap.TableCell
3151  * @extends Roo.bootstrap.Component
3152  * Bootstrap TableCell class
3153  * @cfg {String} html cell contain text
3154  * @cfg {String} cls cell class
3155  * @cfg {String} tag cell tag (td|th) default td
3156  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3157  * @cfg {String} align Aligns the content in a cell
3158  * @cfg {String} axis Categorizes cells
3159  * @cfg {String} bgcolor Specifies the background color of a cell
3160  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3161  * @cfg {Number} colspan Specifies the number of columns a cell should span
3162  * @cfg {String} headers Specifies one or more header cells a cell is related to
3163  * @cfg {Number} height Sets the height of a cell
3164  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3165  * @cfg {Number} rowspan Sets the number of rows a cell should span
3166  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3167  * @cfg {String} valign Vertical aligns the content in a cell
3168  * @cfg {Number} width Specifies the width of a cell
3169  * 
3170  * @constructor
3171  * Create a new TableCell
3172  * @param {Object} config The config object
3173  */
3174
3175 Roo.bootstrap.TableCell = function(config){
3176     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3177 };
3178
3179 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3180     
3181     html: false,
3182     cls: false,
3183     tag: false,
3184     abbr: false,
3185     align: false,
3186     axis: false,
3187     bgcolor: false,
3188     charoff: false,
3189     colspan: false,
3190     headers: false,
3191     height: false,
3192     nowrap: false,
3193     rowspan: false,
3194     scope: false,
3195     valign: false,
3196     width: false,
3197     
3198     
3199     getAutoCreate : function(){
3200         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3201         
3202         cfg = {
3203             tag: 'td'
3204         }
3205         
3206         if(this.tag){
3207             cfg.tag = this.tag;
3208         }
3209         
3210         if (this.html) {
3211             cfg.html=this.html
3212         }
3213         if (this.cls) {
3214             cfg.cls=this.cls
3215         }
3216         if (this.abbr) {
3217             cfg.abbr=this.abbr
3218         }
3219         if (this.align) {
3220             cfg.align=this.align
3221         }
3222         if (this.axis) {
3223             cfg.axis=this.axis
3224         }
3225         if (this.bgcolor) {
3226             cfg.bgcolor=this.bgcolor
3227         }
3228         if (this.charoff) {
3229             cfg.charoff=this.charoff
3230         }
3231         if (this.colspan) {
3232             cfg.colspan=this.colspan
3233         }
3234         if (this.headers) {
3235             cfg.headers=this.headers
3236         }
3237         if (this.height) {
3238             cfg.height=this.height
3239         }
3240         if (this.nowrap) {
3241             cfg.nowrap=this.nowrap
3242         }
3243         if (this.rowspan) {
3244             cfg.rowspan=this.rowspan
3245         }
3246         if (this.scope) {
3247             cfg.scope=this.scope
3248         }
3249         if (this.valign) {
3250             cfg.valign=this.valign
3251         }
3252         if (this.width) {
3253             cfg.width=this.width
3254         }
3255         
3256         
3257         return cfg;
3258     }
3259    
3260 });
3261
3262  
3263
3264  /*
3265  * - LGPL
3266  *
3267  * table row
3268  * 
3269  */
3270
3271 /**
3272  * @class Roo.bootstrap.TableRow
3273  * @extends Roo.bootstrap.Component
3274  * Bootstrap TableRow class
3275  * @cfg {String} cls row class
3276  * @cfg {String} align Aligns the content in a table row
3277  * @cfg {String} bgcolor Specifies a background color for a table row
3278  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3279  * @cfg {String} valign Vertical aligns the content in a table row
3280  * 
3281  * @constructor
3282  * Create a new TableRow
3283  * @param {Object} config The config object
3284  */
3285
3286 Roo.bootstrap.TableRow = function(config){
3287     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3288 };
3289
3290 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3291     
3292     cls: false,
3293     align: false,
3294     bgcolor: false,
3295     charoff: false,
3296     valign: false,
3297     
3298     getAutoCreate : function(){
3299         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3300         
3301         cfg = {
3302             tag: 'tr'
3303         }
3304             
3305         if(this.cls){
3306             cfg.cls = this.cls;
3307         }
3308         if(this.align){
3309             cfg.align = this.align;
3310         }
3311         if(this.bgcolor){
3312             cfg.bgcolor = this.bgcolor;
3313         }
3314         if(this.charoff){
3315             cfg.charoff = this.charoff;
3316         }
3317         if(this.valign){
3318             cfg.valign = this.valign;
3319         }
3320         
3321         return cfg;
3322     }
3323    
3324 });
3325
3326  
3327
3328  /*
3329  * - LGPL
3330  *
3331  * table body
3332  * 
3333  */
3334
3335 /**
3336  * @class Roo.bootstrap.TableBody
3337  * @extends Roo.bootstrap.Component
3338  * Bootstrap TableBody class
3339  * @cfg {String} cls element class
3340  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3341  * @cfg {String} align Aligns the content inside the element
3342  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3343  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3344  * 
3345  * @constructor
3346  * Create a new TableBody
3347  * @param {Object} config The config object
3348  */
3349
3350 Roo.bootstrap.TableBody = function(config){
3351     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3352 };
3353
3354 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3355     
3356     cls: false,
3357     tag: false,
3358     align: false,
3359     charoff: false,
3360     valign: false,
3361     
3362     getAutoCreate : function(){
3363         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3364         
3365         cfg = {
3366             tag: 'tbody'
3367         }
3368             
3369         if (this.cls) {
3370             cfg.cls=this.cls
3371         }
3372         if(this.tag){
3373             cfg.tag = this.tag;
3374         }
3375         
3376         if(this.align){
3377             cfg.align = this.align;
3378         }
3379         if(this.charoff){
3380             cfg.charoff = this.charoff;
3381         }
3382         if(this.valign){
3383             cfg.valign = this.valign;
3384         }
3385         
3386         return cfg;
3387     }
3388     
3389     
3390 //    initEvents : function()
3391 //    {
3392 //        
3393 //        if(!this.store){
3394 //            return;
3395 //        }
3396 //        
3397 //        this.store = Roo.factory(this.store, Roo.data);
3398 //        this.store.on('load', this.onLoad, this);
3399 //        
3400 //        this.store.load();
3401 //        
3402 //    },
3403 //    
3404 //    onLoad: function () 
3405 //    {   
3406 //        this.fireEvent('load', this);
3407 //    }
3408 //    
3409 //   
3410 });
3411
3412  
3413
3414  /*
3415  * Based on:
3416  * Ext JS Library 1.1.1
3417  * Copyright(c) 2006-2007, Ext JS, LLC.
3418  *
3419  * Originally Released Under LGPL - original licence link has changed is not relivant.
3420  *
3421  * Fork - LGPL
3422  * <script type="text/javascript">
3423  */
3424
3425 // as we use this in bootstrap.
3426 Roo.namespace('Roo.form');
3427  /**
3428  * @class Roo.form.Action
3429  * Internal Class used to handle form actions
3430  * @constructor
3431  * @param {Roo.form.BasicForm} el The form element or its id
3432  * @param {Object} config Configuration options
3433  */
3434
3435  
3436  
3437 // define the action interface
3438 Roo.form.Action = function(form, options){
3439     this.form = form;
3440     this.options = options || {};
3441 };
3442 /**
3443  * Client Validation Failed
3444  * @const 
3445  */
3446 Roo.form.Action.CLIENT_INVALID = 'client';
3447 /**
3448  * Server Validation Failed
3449  * @const 
3450  */
3451 Roo.form.Action.SERVER_INVALID = 'server';
3452  /**
3453  * Connect to Server Failed
3454  * @const 
3455  */
3456 Roo.form.Action.CONNECT_FAILURE = 'connect';
3457 /**
3458  * Reading Data from Server Failed
3459  * @const 
3460  */
3461 Roo.form.Action.LOAD_FAILURE = 'load';
3462
3463 Roo.form.Action.prototype = {
3464     type : 'default',
3465     failureType : undefined,
3466     response : undefined,
3467     result : undefined,
3468
3469     // interface method
3470     run : function(options){
3471
3472     },
3473
3474     // interface method
3475     success : function(response){
3476
3477     },
3478
3479     // interface method
3480     handleResponse : function(response){
3481
3482     },
3483
3484     // default connection failure
3485     failure : function(response){
3486         
3487         this.response = response;
3488         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3489         this.form.afterAction(this, false);
3490     },
3491
3492     processResponse : function(response){
3493         this.response = response;
3494         if(!response.responseText){
3495             return true;
3496         }
3497         this.result = this.handleResponse(response);
3498         return this.result;
3499     },
3500
3501     // utility functions used internally
3502     getUrl : function(appendParams){
3503         var url = this.options.url || this.form.url || this.form.el.dom.action;
3504         if(appendParams){
3505             var p = this.getParams();
3506             if(p){
3507                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3508             }
3509         }
3510         return url;
3511     },
3512
3513     getMethod : function(){
3514         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3515     },
3516
3517     getParams : function(){
3518         var bp = this.form.baseParams;
3519         var p = this.options.params;
3520         if(p){
3521             if(typeof p == "object"){
3522                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3523             }else if(typeof p == 'string' && bp){
3524                 p += '&' + Roo.urlEncode(bp);
3525             }
3526         }else if(bp){
3527             p = Roo.urlEncode(bp);
3528         }
3529         return p;
3530     },
3531
3532     createCallback : function(){
3533         return {
3534             success: this.success,
3535             failure: this.failure,
3536             scope: this,
3537             timeout: (this.form.timeout*1000),
3538             upload: this.form.fileUpload ? this.success : undefined
3539         };
3540     }
3541 };
3542
3543 Roo.form.Action.Submit = function(form, options){
3544     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3545 };
3546
3547 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3548     type : 'submit',
3549
3550     haveProgress : false,
3551     uploadComplete : false,
3552     
3553     // uploadProgress indicator.
3554     uploadProgress : function()
3555     {
3556         if (!this.form.progressUrl) {
3557             return;
3558         }
3559         
3560         if (!this.haveProgress) {
3561             Roo.MessageBox.progress("Uploading", "Uploading");
3562         }
3563         if (this.uploadComplete) {
3564            Roo.MessageBox.hide();
3565            return;
3566         }
3567         
3568         this.haveProgress = true;
3569    
3570         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3571         
3572         var c = new Roo.data.Connection();
3573         c.request({
3574             url : this.form.progressUrl,
3575             params: {
3576                 id : uid
3577             },
3578             method: 'GET',
3579             success : function(req){
3580                //console.log(data);
3581                 var rdata = false;
3582                 var edata;
3583                 try  {
3584                    rdata = Roo.decode(req.responseText)
3585                 } catch (e) {
3586                     Roo.log("Invalid data from server..");
3587                     Roo.log(edata);
3588                     return;
3589                 }
3590                 if (!rdata || !rdata.success) {
3591                     Roo.log(rdata);
3592                     Roo.MessageBox.alert(Roo.encode(rdata));
3593                     return;
3594                 }
3595                 var data = rdata.data;
3596                 
3597                 if (this.uploadComplete) {
3598                    Roo.MessageBox.hide();
3599                    return;
3600                 }
3601                    
3602                 if (data){
3603                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3604                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3605                     );
3606                 }
3607                 this.uploadProgress.defer(2000,this);
3608             },
3609        
3610             failure: function(data) {
3611                 Roo.log('progress url failed ');
3612                 Roo.log(data);
3613             },
3614             scope : this
3615         });
3616            
3617     },
3618     
3619     
3620     run : function()
3621     {
3622         // run get Values on the form, so it syncs any secondary forms.
3623         this.form.getValues();
3624         
3625         var o = this.options;
3626         var method = this.getMethod();
3627         var isPost = method == 'POST';
3628         if(o.clientValidation === false || this.form.isValid()){
3629             
3630             if (this.form.progressUrl) {
3631                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3632                     (new Date() * 1) + '' + Math.random());
3633                     
3634             } 
3635             
3636             
3637             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3638                 form:this.form.el.dom,
3639                 url:this.getUrl(!isPost),
3640                 method: method,
3641                 params:isPost ? this.getParams() : null,
3642                 isUpload: this.form.fileUpload
3643             }));
3644             
3645             this.uploadProgress();
3646
3647         }else if (o.clientValidation !== false){ // client validation failed
3648             this.failureType = Roo.form.Action.CLIENT_INVALID;
3649             this.form.afterAction(this, false);
3650         }
3651     },
3652
3653     success : function(response)
3654     {
3655         this.uploadComplete= true;
3656         if (this.haveProgress) {
3657             Roo.MessageBox.hide();
3658         }
3659         
3660         
3661         var result = this.processResponse(response);
3662         if(result === true || result.success){
3663             this.form.afterAction(this, true);
3664             return;
3665         }
3666         if(result.errors){
3667             this.form.markInvalid(result.errors);
3668             this.failureType = Roo.form.Action.SERVER_INVALID;
3669         }
3670         this.form.afterAction(this, false);
3671     },
3672     failure : function(response)
3673     {
3674         this.uploadComplete= true;
3675         if (this.haveProgress) {
3676             Roo.MessageBox.hide();
3677         }
3678         
3679         this.response = response;
3680         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3681         this.form.afterAction(this, false);
3682     },
3683     
3684     handleResponse : function(response){
3685         if(this.form.errorReader){
3686             var rs = this.form.errorReader.read(response);
3687             var errors = [];
3688             if(rs.records){
3689                 for(var i = 0, len = rs.records.length; i < len; i++) {
3690                     var r = rs.records[i];
3691                     errors[i] = r.data;
3692                 }
3693             }
3694             if(errors.length < 1){
3695                 errors = null;
3696             }
3697             return {
3698                 success : rs.success,
3699                 errors : errors
3700             };
3701         }
3702         var ret = false;
3703         try {
3704             ret = Roo.decode(response.responseText);
3705         } catch (e) {
3706             ret = {
3707                 success: false,
3708                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3709                 errors : []
3710             };
3711         }
3712         return ret;
3713         
3714     }
3715 });
3716
3717
3718 Roo.form.Action.Load = function(form, options){
3719     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3720     this.reader = this.form.reader;
3721 };
3722
3723 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3724     type : 'load',
3725
3726     run : function(){
3727         
3728         Roo.Ajax.request(Roo.apply(
3729                 this.createCallback(), {
3730                     method:this.getMethod(),
3731                     url:this.getUrl(false),
3732                     params:this.getParams()
3733         }));
3734     },
3735
3736     success : function(response){
3737         
3738         var result = this.processResponse(response);
3739         if(result === true || !result.success || !result.data){
3740             this.failureType = Roo.form.Action.LOAD_FAILURE;
3741             this.form.afterAction(this, false);
3742             return;
3743         }
3744         this.form.clearInvalid();
3745         this.form.setValues(result.data);
3746         this.form.afterAction(this, true);
3747     },
3748
3749     handleResponse : function(response){
3750         if(this.form.reader){
3751             var rs = this.form.reader.read(response);
3752             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3753             return {
3754                 success : rs.success,
3755                 data : data
3756             };
3757         }
3758         return Roo.decode(response.responseText);
3759     }
3760 });
3761
3762 Roo.form.Action.ACTION_TYPES = {
3763     'load' : Roo.form.Action.Load,
3764     'submit' : Roo.form.Action.Submit
3765 };/*
3766  * - LGPL
3767  *
3768  * form
3769  * 
3770  */
3771
3772 /**
3773  * @class Roo.bootstrap.Form
3774  * @extends Roo.bootstrap.Component
3775  * Bootstrap Form class
3776  * @cfg {String} method  GET | POST (default POST)
3777  * @cfg {String} labelAlign top | left (default top)
3778   * @cfg {String} align left  | right - for navbars
3779
3780  * 
3781  * @constructor
3782  * Create a new Form
3783  * @param {Object} config The config object
3784  */
3785
3786
3787 Roo.bootstrap.Form = function(config){
3788     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3789     this.addEvents({
3790         /**
3791          * @event clientvalidation
3792          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3793          * @param {Form} this
3794          * @param {Boolean} valid true if the form has passed client-side validation
3795          */
3796         clientvalidation: true,
3797         /**
3798          * @event beforeaction
3799          * Fires before any action is performed. Return false to cancel the action.
3800          * @param {Form} this
3801          * @param {Action} action The action to be performed
3802          */
3803         beforeaction: true,
3804         /**
3805          * @event actionfailed
3806          * Fires when an action fails.
3807          * @param {Form} this
3808          * @param {Action} action The action that failed
3809          */
3810         actionfailed : true,
3811         /**
3812          * @event actioncomplete
3813          * Fires when an action is completed.
3814          * @param {Form} this
3815          * @param {Action} action The action that completed
3816          */
3817         actioncomplete : true
3818     });
3819     
3820 };
3821
3822 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3823       
3824      /**
3825      * @cfg {String} method
3826      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3827      */
3828     method : 'POST',
3829     /**
3830      * @cfg {String} url
3831      * The URL to use for form actions if one isn't supplied in the action options.
3832      */
3833     /**
3834      * @cfg {Boolean} fileUpload
3835      * Set to true if this form is a file upload.
3836      */
3837      
3838     /**
3839      * @cfg {Object} baseParams
3840      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3841      */
3842       
3843     /**
3844      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3845      */
3846     timeout: 30,
3847     /**
3848      * @cfg {Sting} align (left|right) for navbar forms
3849      */
3850     align : 'left',
3851
3852     // private
3853     activeAction : null,
3854  
3855     /**
3856      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3857      * element by passing it or its id or mask the form itself by passing in true.
3858      * @type Mixed
3859      */
3860     waitMsgTarget : false,
3861     
3862      
3863     
3864     /**
3865      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3866      * element by passing it or its id or mask the form itself by passing in true.
3867      * @type Mixed
3868      */
3869     
3870     getAutoCreate : function(){
3871         
3872         var cfg = {
3873             tag: 'form',
3874             method : this.method || 'POST',
3875             id : this.id || Roo.id(),
3876             cls : ''
3877         }
3878         if (this.parent().xtype.match(/^Nav/)) {
3879             cfg.cls = 'navbar-form navbar-' + this.align;
3880             
3881         }
3882         
3883         if (this.labelAlign == 'left' ) {
3884             cfg.cls += ' form-horizontal';
3885         }
3886         
3887         
3888         return cfg;
3889     },
3890     initEvents : function()
3891     {
3892         this.el.on('submit', this.onSubmit, this);
3893         
3894         
3895     },
3896     // private
3897     onSubmit : function(e){
3898         e.stopEvent();
3899     },
3900     
3901      /**
3902      * Returns true if client-side validation on the form is successful.
3903      * @return Boolean
3904      */
3905     isValid : function(){
3906         var items = this.getItems();
3907         var valid = true;
3908         items.each(function(f){
3909            if(!f.validate()){
3910                valid = false;
3911                
3912            }
3913         });
3914         return valid;
3915     },
3916     /**
3917      * Returns true if any fields in this form have changed since their original load.
3918      * @return Boolean
3919      */
3920     isDirty : function(){
3921         var dirty = false;
3922         var items = this.getItems();
3923         items.each(function(f){
3924            if(f.isDirty()){
3925                dirty = true;
3926                return false;
3927            }
3928            return true;
3929         });
3930         return dirty;
3931     },
3932      /**
3933      * Performs a predefined action (submit or load) or custom actions you define on this form.
3934      * @param {String} actionName The name of the action type
3935      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3936      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3937      * accept other config options):
3938      * <pre>
3939 Property          Type             Description
3940 ----------------  ---------------  ----------------------------------------------------------------------------------
3941 url               String           The url for the action (defaults to the form's url)
3942 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3943 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3944 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3945                                    validate the form on the client (defaults to false)
3946      * </pre>
3947      * @return {BasicForm} this
3948      */
3949     doAction : function(action, options){
3950         if(typeof action == 'string'){
3951             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3952         }
3953         if(this.fireEvent('beforeaction', this, action) !== false){
3954             this.beforeAction(action);
3955             action.run.defer(100, action);
3956         }
3957         return this;
3958     },
3959     
3960     // private
3961     beforeAction : function(action){
3962         var o = action.options;
3963         
3964         // not really supported yet.. ??
3965         
3966         //if(this.waitMsgTarget === true){
3967             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3968         //}else if(this.waitMsgTarget){
3969         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3970         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3971         //}else {
3972         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3973        // }
3974          
3975     },
3976
3977     // private
3978     afterAction : function(action, success){
3979         this.activeAction = null;
3980         var o = action.options;
3981         
3982         //if(this.waitMsgTarget === true){
3983             this.el.unmask();
3984         //}else if(this.waitMsgTarget){
3985         //    this.waitMsgTarget.unmask();
3986         //}else{
3987         //    Roo.MessageBox.updateProgress(1);
3988         //    Roo.MessageBox.hide();
3989        // }
3990         // 
3991         if(success){
3992             if(o.reset){
3993                 this.reset();
3994             }
3995             Roo.callback(o.success, o.scope, [this, action]);
3996             this.fireEvent('actioncomplete', this, action);
3997             
3998         }else{
3999             
4000             // failure condition..
4001             // we have a scenario where updates need confirming.
4002             // eg. if a locking scenario exists..
4003             // we look for { errors : { needs_confirm : true }} in the response.
4004             if (
4005                 (typeof(action.result) != 'undefined')  &&
4006                 (typeof(action.result.errors) != 'undefined')  &&
4007                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4008            ){
4009                 var _t = this;
4010                 Roo.log("not supported yet");
4011                  /*
4012                 
4013                 Roo.MessageBox.confirm(
4014                     "Change requires confirmation",
4015                     action.result.errorMsg,
4016                     function(r) {
4017                         if (r != 'yes') {
4018                             return;
4019                         }
4020                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4021                     }
4022                     
4023                 );
4024                 */
4025                 
4026                 
4027                 return;
4028             }
4029             
4030             Roo.callback(o.failure, o.scope, [this, action]);
4031             // show an error message if no failed handler is set..
4032             if (!this.hasListener('actionfailed')) {
4033                 Roo.log("need to add dialog support");
4034                 /*
4035                 Roo.MessageBox.alert("Error",
4036                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4037                         action.result.errorMsg :
4038                         "Saving Failed, please check your entries or try again"
4039                 );
4040                 */
4041             }
4042             
4043             this.fireEvent('actionfailed', this, action);
4044         }
4045         
4046     },
4047     /**
4048      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4049      * @param {String} id The value to search for
4050      * @return Field
4051      */
4052     findField : function(id){
4053         var items = this.getItems();
4054         var field = items.get(id);
4055         if(!field){
4056              items.each(function(f){
4057                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4058                     field = f;
4059                     return false;
4060                 }
4061                 return true;
4062             });
4063         }
4064         return field || null;
4065     },
4066      /**
4067      * Mark fields in this form invalid in bulk.
4068      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4069      * @return {BasicForm} this
4070      */
4071     markInvalid : function(errors){
4072         if(errors instanceof Array){
4073             for(var i = 0, len = errors.length; i < len; i++){
4074                 var fieldError = errors[i];
4075                 var f = this.findField(fieldError.id);
4076                 if(f){
4077                     f.markInvalid(fieldError.msg);
4078                 }
4079             }
4080         }else{
4081             var field, id;
4082             for(id in errors){
4083                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4084                     field.markInvalid(errors[id]);
4085                 }
4086             }
4087         }
4088         //Roo.each(this.childForms || [], function (f) {
4089         //    f.markInvalid(errors);
4090         //});
4091         
4092         return this;
4093     },
4094
4095     /**
4096      * Set values for fields in this form in bulk.
4097      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4098      * @return {BasicForm} this
4099      */
4100     setValues : function(values){
4101         if(values instanceof Array){ // array of objects
4102             for(var i = 0, len = values.length; i < len; i++){
4103                 var v = values[i];
4104                 var f = this.findField(v.id);
4105                 if(f){
4106                     f.setValue(v.value);
4107                     if(this.trackResetOnLoad){
4108                         f.originalValue = f.getValue();
4109                     }
4110                 }
4111             }
4112         }else{ // object hash
4113             var field, id;
4114             for(id in values){
4115                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4116                     
4117                     if (field.setFromData && 
4118                         field.valueField && 
4119                         field.displayField &&
4120                         // combos' with local stores can 
4121                         // be queried via setValue()
4122                         // to set their value..
4123                         (field.store && !field.store.isLocal)
4124                         ) {
4125                         // it's a combo
4126                         var sd = { };
4127                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4128                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4129                         field.setFromData(sd);
4130                         
4131                     } else {
4132                         field.setValue(values[id]);
4133                     }
4134                     
4135                     
4136                     if(this.trackResetOnLoad){
4137                         field.originalValue = field.getValue();
4138                     }
4139                 }
4140             }
4141         }
4142          
4143         //Roo.each(this.childForms || [], function (f) {
4144         //    f.setValues(values);
4145         //});
4146                 
4147         return this;
4148     },
4149
4150     /**
4151      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4152      * they are returned as an array.
4153      * @param {Boolean} asString
4154      * @return {Object}
4155      */
4156     getValues : function(asString){
4157         //if (this.childForms) {
4158             // copy values from the child forms
4159         //    Roo.each(this.childForms, function (f) {
4160         //        this.setValues(f.getValues());
4161         //    }, this);
4162         //}
4163         
4164         
4165         
4166         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4167         if(asString === true){
4168             return fs;
4169         }
4170         return Roo.urlDecode(fs);
4171     },
4172     
4173     /**
4174      * Returns the fields in this form as an object with key/value pairs. 
4175      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4176      * @return {Object}
4177      */
4178     getFieldValues : function(with_hidden)
4179     {
4180         var items = this.getItems();
4181         var ret = {};
4182         items.each(function(f){
4183             if (!f.getName()) {
4184                 return;
4185             }
4186             var v = f.getValue();
4187             if (f.inputType =='radio') {
4188                 if (typeof(ret[f.getName()]) == 'undefined') {
4189                     ret[f.getName()] = ''; // empty..
4190                 }
4191                 
4192                 if (!f.el.dom.checked) {
4193                     return;
4194                     
4195                 }
4196                 v = f.el.dom.value;
4197                 
4198             }
4199             
4200             // not sure if this supported any more..
4201             if ((typeof(v) == 'object') && f.getRawValue) {
4202                 v = f.getRawValue() ; // dates..
4203             }
4204             // combo boxes where name != hiddenName...
4205             if (f.name != f.getName()) {
4206                 ret[f.name] = f.getRawValue();
4207             }
4208             ret[f.getName()] = v;
4209         });
4210         
4211         return ret;
4212     },
4213
4214     /**
4215      * Clears all invalid messages in this form.
4216      * @return {BasicForm} this
4217      */
4218     clearInvalid : function(){
4219         var items = this.getItems();
4220         
4221         items.each(function(f){
4222            f.clearInvalid();
4223         });
4224         
4225         
4226         
4227         return this;
4228     },
4229
4230     /**
4231      * Resets this form.
4232      * @return {BasicForm} this
4233      */
4234     reset : function(){
4235         var items = this.getItems();
4236         items.each(function(f){
4237             f.reset();
4238         });
4239         
4240         Roo.each(this.childForms || [], function (f) {
4241             f.reset();
4242         });
4243        
4244         
4245         return this;
4246     },
4247     getItems : function()
4248     {
4249         var r=new Roo.util.MixedCollection(false, function(o){
4250             return o.id || (o.id = Roo.id());
4251         });
4252         var iter = function(el) {
4253             if (el.inputEl) {
4254                 r.add(el);
4255             }
4256             if (!el.items) {
4257                 return;
4258             }
4259             Roo.each(el.items,function(e) {
4260                 iter(e);
4261             });
4262             
4263             
4264         };
4265         iter(this);
4266         return r;
4267         
4268         
4269         
4270         
4271     }
4272     
4273 });
4274
4275  
4276 /*
4277  * Based on:
4278  * Ext JS Library 1.1.1
4279  * Copyright(c) 2006-2007, Ext JS, LLC.
4280  *
4281  * Originally Released Under LGPL - original licence link has changed is not relivant.
4282  *
4283  * Fork - LGPL
4284  * <script type="text/javascript">
4285  */
4286 /**
4287  * @class Roo.form.VTypes
4288  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4289  * @singleton
4290  */
4291 Roo.form.VTypes = function(){
4292     // closure these in so they are only created once.
4293     var alpha = /^[a-zA-Z_]+$/;
4294     var alphanum = /^[a-zA-Z0-9_]+$/;
4295     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4296     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4297
4298     // All these messages and functions are configurable
4299     return {
4300         /**
4301          * The function used to validate email addresses
4302          * @param {String} value The email address
4303          */
4304         'email' : function(v){
4305             return email.test(v);
4306         },
4307         /**
4308          * The error text to display when the email validation function returns false
4309          * @type String
4310          */
4311         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4312         /**
4313          * The keystroke filter mask to be applied on email input
4314          * @type RegExp
4315          */
4316         'emailMask' : /[a-z0-9_\.\-@]/i,
4317
4318         /**
4319          * The function used to validate URLs
4320          * @param {String} value The URL
4321          */
4322         'url' : function(v){
4323             return url.test(v);
4324         },
4325         /**
4326          * The error text to display when the url validation function returns false
4327          * @type String
4328          */
4329         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4330         
4331         /**
4332          * The function used to validate alpha values
4333          * @param {String} value The value
4334          */
4335         'alpha' : function(v){
4336             return alpha.test(v);
4337         },
4338         /**
4339          * The error text to display when the alpha validation function returns false
4340          * @type String
4341          */
4342         'alphaText' : 'This field should only contain letters and _',
4343         /**
4344          * The keystroke filter mask to be applied on alpha input
4345          * @type RegExp
4346          */
4347         'alphaMask' : /[a-z_]/i,
4348
4349         /**
4350          * The function used to validate alphanumeric values
4351          * @param {String} value The value
4352          */
4353         'alphanum' : function(v){
4354             return alphanum.test(v);
4355         },
4356         /**
4357          * The error text to display when the alphanumeric validation function returns false
4358          * @type String
4359          */
4360         'alphanumText' : 'This field should only contain letters, numbers and _',
4361         /**
4362          * The keystroke filter mask to be applied on alphanumeric input
4363          * @type RegExp
4364          */
4365         'alphanumMask' : /[a-z0-9_]/i
4366     };
4367 }();/*
4368  * - LGPL
4369  *
4370  * Input
4371  * 
4372  */
4373
4374 /**
4375  * @class Roo.bootstrap.Input
4376  * @extends Roo.bootstrap.Component
4377  * Bootstrap Input class
4378  * @cfg {Boolean} disabled is it disabled
4379  * @cfg {String} fieldLabel - the label associated
4380  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4381  * @cfg {String} name name of the input
4382  * @cfg {string} fieldLabel - the label associated
4383  * @cfg {string}  inputType - input / file submit ...
4384  * @cfg {string} placeholder - placeholder to put in text.
4385  * @cfg {string}  before - input group add on before
4386  * @cfg {string} after - input group add on after
4387  * @cfg {string} size - (lg|sm) or leave empty..
4388  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4389  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4390  * @cfg {Number} md colspan out of 12 for computer-sized screens
4391  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4392  * @cfg {string} value default value of the input
4393  * @cfg {Number} labelWidth set the width of label (0-12)
4394  * @cfg {String} labelAlign (top|left)
4395  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4396  * 
4397  * 
4398  * @constructor
4399  * Create a new Input
4400  * @param {Object} config The config object
4401  */
4402
4403 Roo.bootstrap.Input = function(config){
4404     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4405    
4406         this.addEvents({
4407             /**
4408              * @event focus
4409              * Fires when this field receives input focus.
4410              * @param {Roo.form.Field} this
4411              */
4412             focus : true,
4413             /**
4414              * @event blur
4415              * Fires when this field loses input focus.
4416              * @param {Roo.form.Field} this
4417              */
4418             blur : true,
4419             /**
4420              * @event specialkey
4421              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4422              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4423              * @param {Roo.form.Field} this
4424              * @param {Roo.EventObject} e The event object
4425              */
4426             specialkey : true,
4427             /**
4428              * @event change
4429              * Fires just before the field blurs if the field value has changed.
4430              * @param {Roo.form.Field} this
4431              * @param {Mixed} newValue The new value
4432              * @param {Mixed} oldValue The original value
4433              */
4434             change : true,
4435             /**
4436              * @event invalid
4437              * Fires after the field has been marked as invalid.
4438              * @param {Roo.form.Field} this
4439              * @param {String} msg The validation message
4440              */
4441             invalid : true,
4442             /**
4443              * @event valid
4444              * Fires after the field has been validated with no errors.
4445              * @param {Roo.form.Field} this
4446              */
4447             valid : true,
4448              /**
4449              * @event keyup
4450              * Fires after the key up
4451              * @param {Roo.form.Field} this
4452              * @param {Roo.EventObject}  e The event Object
4453              */
4454             keyup : true
4455         });
4456 };
4457
4458 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4459      /**
4460      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4461       automatic validation (defaults to "keyup").
4462      */
4463     validationEvent : "keyup",
4464      /**
4465      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4466      */
4467     validateOnBlur : true,
4468     /**
4469      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4470      */
4471     validationDelay : 250,
4472      /**
4473      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4474      */
4475     focusClass : "x-form-focus",  // not needed???
4476     
4477        
4478     /**
4479      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4480      */
4481     invalidClass : "has-error",
4482     
4483     /**
4484      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4485      */
4486     selectOnFocus : false,
4487     
4488      /**
4489      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4490      */
4491     maskRe : null,
4492        /**
4493      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4494      */
4495     vtype : null,
4496     
4497       /**
4498      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4499      */
4500     disableKeyFilter : false,
4501     
4502        /**
4503      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4504      */
4505     disabled : false,
4506      /**
4507      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4508      */
4509     allowBlank : true,
4510     /**
4511      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4512      */
4513     blankText : "This field is required",
4514     
4515      /**
4516      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4517      */
4518     minLength : 0,
4519     /**
4520      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4521      */
4522     maxLength : Number.MAX_VALUE,
4523     /**
4524      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4525      */
4526     minLengthText : "The minimum length for this field is {0}",
4527     /**
4528      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4529      */
4530     maxLengthText : "The maximum length for this field is {0}",
4531   
4532     
4533     /**
4534      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4535      * If available, this function will be called only after the basic validators all return true, and will be passed the
4536      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4537      */
4538     validator : null,
4539     /**
4540      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4541      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4542      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4543      */
4544     regex : null,
4545     /**
4546      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4547      */
4548     regexText : "",
4549     
4550     
4551     
4552     fieldLabel : '',
4553     inputType : 'text',
4554     
4555     name : false,
4556     placeholder: false,
4557     before : false,
4558     after : false,
4559     size : false,
4560     // private
4561     hasFocus : false,
4562     preventMark: false,
4563     isFormField : true,
4564     value : '',
4565     labelWidth : 2,
4566     labelAlign : false,
4567     readOnly : false,
4568     
4569     parentLabelAlign : function()
4570     {
4571         var parent = this;
4572         while (parent.parent()) {
4573             parent = parent.parent();
4574             if (typeof(parent.labelAlign) !='undefined') {
4575                 return parent.labelAlign;
4576             }
4577         }
4578         return 'left';
4579         
4580     },
4581     
4582     getAutoCreate : function(){
4583         
4584         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4585         
4586         var id = Roo.id();
4587         
4588         var cfg = {};
4589         
4590         if(this.inputType != 'hidden'){
4591             cfg.cls = 'form-group' //input-group
4592         }
4593         
4594         var input =  {
4595             tag: 'input',
4596             id : id,
4597             type : this.inputType,
4598             value : this.value,
4599             cls : 'form-control',
4600             placeholder : this.placeholder || ''
4601             
4602         };
4603         
4604         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4605             input.maxLength = this.maxLength;
4606         }
4607         
4608         if (this.disabled) {
4609             input.disabled=true;
4610         }
4611         
4612         if (this.readOnly) {
4613             input.readonly=true;
4614         }
4615         
4616         if (this.name) {
4617             input.name = this.name;
4618         }
4619         if (this.size) {
4620             input.cls += ' input-' + this.size;
4621         }
4622         var settings=this;
4623         ['xs','sm','md','lg'].map(function(size){
4624             if (settings[size]) {
4625                 cfg.cls += ' col-' + size + '-' + settings[size];
4626             }
4627         });
4628         
4629         var inputblock = input;
4630         
4631         if (this.before || this.after) {
4632             
4633             inputblock = {
4634                 cls : 'input-group',
4635                 cn :  [] 
4636             };
4637             if (this.before) {
4638                 inputblock.cn.push({
4639                     tag :'span',
4640                     cls : 'input-group-addon',
4641                     html : this.before
4642                 });
4643             }
4644             inputblock.cn.push(input);
4645             if (this.after) {
4646                 inputblock.cn.push({
4647                     tag :'span',
4648                     cls : 'input-group-addon',
4649                     html : this.after
4650                 });
4651             }
4652             
4653         };
4654         
4655         if (align ==='left' && this.fieldLabel.length) {
4656                 Roo.log("left and has label");
4657                 cfg.cn = [
4658                     
4659                     {
4660                         tag: 'label',
4661                         'for' :  id,
4662                         cls : 'control-label col-sm-' + this.labelWidth,
4663                         html : this.fieldLabel
4664                         
4665                     },
4666                     {
4667                         cls : "col-sm-" + (12 - this.labelWidth), 
4668                         cn: [
4669                             inputblock
4670                         ]
4671                     }
4672                     
4673                 ];
4674         } else if ( this.fieldLabel.length) {
4675                 Roo.log(" label");
4676                  cfg.cn = [
4677                    
4678                     {
4679                         tag: 'label',
4680                         //cls : 'input-group-addon',
4681                         html : this.fieldLabel
4682                         
4683                     },
4684                     
4685                     inputblock
4686                     
4687                 ];
4688
4689         } else {
4690             
4691                 Roo.log(" no label && no align");
4692                 cfg.cn = [
4693                     
4694                         inputblock
4695                     
4696                 ];
4697                 
4698                 
4699         };
4700         Roo.log('input-parentType: ' + this.parentType);
4701         
4702         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4703            cfg.cls += ' navbar-form';
4704            Roo.log(cfg);
4705         }
4706         
4707         return cfg;
4708         
4709     },
4710     /**
4711      * return the real input element.
4712      */
4713     inputEl: function ()
4714     {
4715         return this.el.select('input.form-control',true).first();
4716     },
4717     setDisabled : function(v)
4718     {
4719         var i  = this.inputEl().dom;
4720         if (!v) {
4721             i.removeAttribute('disabled');
4722             return;
4723             
4724         }
4725         i.setAttribute('disabled','true');
4726     },
4727     initEvents : function()
4728     {
4729         
4730         this.inputEl().on("keydown" , this.fireKey,  this);
4731         this.inputEl().on("focus", this.onFocus,  this);
4732         this.inputEl().on("blur", this.onBlur,  this);
4733         
4734         this.inputEl().relayEvent('keyup', this);
4735
4736         // reference to original value for reset
4737         this.originalValue = this.getValue();
4738         //Roo.form.TextField.superclass.initEvents.call(this);
4739         if(this.validationEvent == 'keyup'){
4740             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4741             this.inputEl().on('keyup', this.filterValidation, this);
4742         }
4743         else if(this.validationEvent !== false){
4744             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4745         }
4746         
4747         if(this.selectOnFocus){
4748             this.on("focus", this.preFocus, this);
4749             
4750         }
4751         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4752             this.inputEl().on("keypress", this.filterKeys, this);
4753         }
4754        /* if(this.grow){
4755             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4756             this.el.on("click", this.autoSize,  this);
4757         }
4758         */
4759         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4760             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4761         }
4762         
4763     },
4764     filterValidation : function(e){
4765         if(!e.isNavKeyPress()){
4766             this.validationTask.delay(this.validationDelay);
4767         }
4768     },
4769      /**
4770      * Validates the field value
4771      * @return {Boolean} True if the value is valid, else false
4772      */
4773     validate : function(){
4774         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4775         if(this.disabled || this.validateValue(this.getRawValue())){
4776             this.clearInvalid();
4777             return true;
4778         }
4779         return false;
4780     },
4781     
4782     
4783     /**
4784      * Validates a value according to the field's validation rules and marks the field as invalid
4785      * if the validation fails
4786      * @param {Mixed} value The value to validate
4787      * @return {Boolean} True if the value is valid, else false
4788      */
4789     validateValue : function(value){
4790         if(value.length < 1)  { // if it's blank
4791              if(this.allowBlank){
4792                 this.clearInvalid();
4793                 return true;
4794              }else{
4795                 this.markInvalid(this.blankText);
4796                 return false;
4797              }
4798         }
4799         if(value.length < this.minLength){
4800             this.markInvalid(String.format(this.minLengthText, this.minLength));
4801             return false;
4802         }
4803         if(value.length > this.maxLength){
4804             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4805             return false;
4806         }
4807         if(this.vtype){
4808             var vt = Roo.form.VTypes;
4809             if(!vt[this.vtype](value, this)){
4810                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4811                 return false;
4812             }
4813         }
4814         if(typeof this.validator == "function"){
4815             var msg = this.validator(value);
4816             if(msg !== true){
4817                 this.markInvalid(msg);
4818                 return false;
4819             }
4820         }
4821         if(this.regex && !this.regex.test(value)){
4822             this.markInvalid(this.regexText);
4823             return false;
4824         }
4825         return true;
4826     },
4827
4828     
4829     
4830      // private
4831     fireKey : function(e){
4832         //Roo.log('field ' + e.getKey());
4833         if(e.isNavKeyPress()){
4834             this.fireEvent("specialkey", this, e);
4835         }
4836     },
4837     focus : function (selectText){
4838         if(this.rendered){
4839             this.inputEl().focus();
4840             if(selectText === true){
4841                 this.inputEl().dom.select();
4842             }
4843         }
4844         return this;
4845     } ,
4846     
4847     onFocus : function(){
4848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4849            // this.el.addClass(this.focusClass);
4850         }
4851         if(!this.hasFocus){
4852             this.hasFocus = true;
4853             this.startValue = this.getValue();
4854             this.fireEvent("focus", this);
4855         }
4856     },
4857     
4858     beforeBlur : Roo.emptyFn,
4859
4860     
4861     // private
4862     onBlur : function(){
4863         this.beforeBlur();
4864         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4865             //this.el.removeClass(this.focusClass);
4866         }
4867         this.hasFocus = false;
4868         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4869             this.validate();
4870         }
4871         var v = this.getValue();
4872         if(String(v) !== String(this.startValue)){
4873             this.fireEvent('change', this, v, this.startValue);
4874         }
4875         this.fireEvent("blur", this);
4876     },
4877     
4878     /**
4879      * Resets the current field value to the originally loaded value and clears any validation messages
4880      */
4881     reset : function(){
4882         this.setValue(this.originalValue);
4883         this.clearInvalid();
4884     },
4885      /**
4886      * Returns the name of the field
4887      * @return {Mixed} name The name field
4888      */
4889     getName: function(){
4890         return this.name;
4891     },
4892      /**
4893      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4894      * @return {Mixed} value The field value
4895      */
4896     getValue : function(){
4897         return this.inputEl().getValue();
4898     },
4899     /**
4900      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4901      * @return {Mixed} value The field value
4902      */
4903     getRawValue : function(){
4904         var v = this.inputEl().getValue();
4905         
4906         return v;
4907     },
4908     
4909     /**
4910      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4911      * @param {Mixed} value The value to set
4912      */
4913     setRawValue : function(v){
4914         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4915     },
4916     
4917     selectText : function(start, end){
4918         var v = this.getRawValue();
4919         if(v.length > 0){
4920             start = start === undefined ? 0 : start;
4921             end = end === undefined ? v.length : end;
4922             var d = this.inputEl().dom;
4923             if(d.setSelectionRange){
4924                 d.setSelectionRange(start, end);
4925             }else if(d.createTextRange){
4926                 var range = d.createTextRange();
4927                 range.moveStart("character", start);
4928                 range.moveEnd("character", v.length-end);
4929                 range.select();
4930             }
4931         }
4932     },
4933     
4934     /**
4935      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4936      * @param {Mixed} value The value to set
4937      */
4938     setValue : function(v){
4939         this.value = v;
4940         if(this.rendered){
4941             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4942             this.validate();
4943         }
4944     },
4945     
4946     /*
4947     processValue : function(value){
4948         if(this.stripCharsRe){
4949             var newValue = value.replace(this.stripCharsRe, '');
4950             if(newValue !== value){
4951                 this.setRawValue(newValue);
4952                 return newValue;
4953             }
4954         }
4955         return value;
4956     },
4957   */
4958     preFocus : function(){
4959         
4960         if(this.selectOnFocus){
4961             this.inputEl().dom.select();
4962         }
4963     },
4964     filterKeys : function(e){
4965         var k = e.getKey();
4966         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4967             return;
4968         }
4969         var c = e.getCharCode(), cc = String.fromCharCode(c);
4970         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4971             return;
4972         }
4973         if(!this.maskRe.test(cc)){
4974             e.stopEvent();
4975         }
4976     },
4977      /**
4978      * Clear any invalid styles/messages for this field
4979      */
4980     clearInvalid : function(){
4981         
4982         if(!this.el || this.preventMark){ // not rendered
4983             return;
4984         }
4985         this.el.removeClass(this.invalidClass);
4986         /*
4987         switch(this.msgTarget){
4988             case 'qtip':
4989                 this.el.dom.qtip = '';
4990                 break;
4991             case 'title':
4992                 this.el.dom.title = '';
4993                 break;
4994             case 'under':
4995                 if(this.errorEl){
4996                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4997                 }
4998                 break;
4999             case 'side':
5000                 if(this.errorIcon){
5001                     this.errorIcon.dom.qtip = '';
5002                     this.errorIcon.hide();
5003                     this.un('resize', this.alignErrorIcon, this);
5004                 }
5005                 break;
5006             default:
5007                 var t = Roo.getDom(this.msgTarget);
5008                 t.innerHTML = '';
5009                 t.style.display = 'none';
5010                 break;
5011         }
5012         */
5013         this.fireEvent('valid', this);
5014     },
5015      /**
5016      * Mark this field as invalid
5017      * @param {String} msg The validation message
5018      */
5019     markInvalid : function(msg){
5020         if(!this.el  || this.preventMark){ // not rendered
5021             return;
5022         }
5023         this.el.addClass(this.invalidClass);
5024         /*
5025         msg = msg || this.invalidText;
5026         switch(this.msgTarget){
5027             case 'qtip':
5028                 this.el.dom.qtip = msg;
5029                 this.el.dom.qclass = 'x-form-invalid-tip';
5030                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5031                     Roo.QuickTips.enable();
5032                 }
5033                 break;
5034             case 'title':
5035                 this.el.dom.title = msg;
5036                 break;
5037             case 'under':
5038                 if(!this.errorEl){
5039                     var elp = this.el.findParent('.x-form-element', 5, true);
5040                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5041                     this.errorEl.setWidth(elp.getWidth(true)-20);
5042                 }
5043                 this.errorEl.update(msg);
5044                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5045                 break;
5046             case 'side':
5047                 if(!this.errorIcon){
5048                     var elp = this.el.findParent('.x-form-element', 5, true);
5049                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5050                 }
5051                 this.alignErrorIcon();
5052                 this.errorIcon.dom.qtip = msg;
5053                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5054                 this.errorIcon.show();
5055                 this.on('resize', this.alignErrorIcon, this);
5056                 break;
5057             default:
5058                 var t = Roo.getDom(this.msgTarget);
5059                 t.innerHTML = msg;
5060                 t.style.display = this.msgDisplay;
5061                 break;
5062         }
5063         */
5064         this.fireEvent('invalid', this, msg);
5065     },
5066     // private
5067     SafariOnKeyDown : function(event)
5068     {
5069         // this is a workaround for a password hang bug on chrome/ webkit.
5070         
5071         var isSelectAll = false;
5072         
5073         if(this.inputEl().dom.selectionEnd > 0){
5074             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5075         }
5076         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5077             event.preventDefault();
5078             this.setValue('');
5079             return;
5080         }
5081         
5082         if(isSelectAll){ // backspace and delete key
5083             
5084             event.preventDefault();
5085             // this is very hacky as keydown always get's upper case.
5086             //
5087             var cc = String.fromCharCode(event.getCharCode());
5088             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5089             
5090         }
5091     },
5092     adjustWidth : function(tag, w){
5093         tag = tag.toLowerCase();
5094         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5095             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5096                 if(tag == 'input'){
5097                     return w + 2;
5098                 }
5099                 if(tag == 'textarea'){
5100                     return w-2;
5101                 }
5102             }else if(Roo.isOpera){
5103                 if(tag == 'input'){
5104                     return w + 2;
5105                 }
5106                 if(tag == 'textarea'){
5107                     return w-2;
5108                 }
5109             }
5110         }
5111         return w;
5112     }
5113     
5114 });
5115
5116  
5117 /*
5118  * - LGPL
5119  *
5120  * Input
5121  * 
5122  */
5123
5124 /**
5125  * @class Roo.bootstrap.TextArea
5126  * @extends Roo.bootstrap.Input
5127  * Bootstrap TextArea class
5128  * @cfg {Number} cols Specifies the visible width of a text area
5129  * @cfg {Number} rows Specifies the visible number of lines in a text area
5130  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5131  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5132  * @cfg {string} html text
5133  * 
5134  * @constructor
5135  * Create a new TextArea
5136  * @param {Object} config The config object
5137  */
5138
5139 Roo.bootstrap.TextArea = function(config){
5140     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5141    
5142 };
5143
5144 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5145      
5146     cols : false,
5147     rows : 5,
5148     readOnly : false,
5149     warp : 'soft',
5150     resize : false,
5151     value: false,
5152     html: false,
5153     
5154     getAutoCreate : function(){
5155         
5156         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5157         
5158         var id = Roo.id();
5159         
5160         var cfg = {};
5161         
5162         var input =  {
5163             tag: 'textarea',
5164             id : id,
5165             warp : this.warp,
5166             rows : this.rows,
5167             value : this.value || '',
5168             html: this.html || '',
5169             cls : 'form-control',
5170             placeholder : this.placeholder || '' 
5171             
5172         };
5173         
5174         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5175             input.maxLength = this.maxLength;
5176         }
5177         
5178         if(this.resize){
5179             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5180         }
5181         
5182         if(this.cols){
5183             input.cols = this.cols;
5184         }
5185         
5186         if (this.readOnly) {
5187             input.readonly = true;
5188         }
5189         
5190         if (this.name) {
5191             input.name = this.name;
5192         }
5193         
5194         if (this.size) {
5195             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5196         }
5197         
5198         var settings=this;
5199         ['xs','sm','md','lg'].map(function(size){
5200             if (settings[size]) {
5201                 cfg.cls += ' col-' + size + '-' + settings[size];
5202             }
5203         });
5204         
5205         var inputblock = input;
5206         
5207         if (this.before || this.after) {
5208             
5209             inputblock = {
5210                 cls : 'input-group',
5211                 cn :  [] 
5212             };
5213             if (this.before) {
5214                 inputblock.cn.push({
5215                     tag :'span',
5216                     cls : 'input-group-addon',
5217                     html : this.before
5218                 });
5219             }
5220             inputblock.cn.push(input);
5221             if (this.after) {
5222                 inputblock.cn.push({
5223                     tag :'span',
5224                     cls : 'input-group-addon',
5225                     html : this.after
5226                 });
5227             }
5228             
5229         }
5230         
5231         if (align ==='left' && this.fieldLabel.length) {
5232                 Roo.log("left and has label");
5233                 cfg.cn = [
5234                     
5235                     {
5236                         tag: 'label',
5237                         'for' :  id,
5238                         cls : 'control-label col-sm-' + this.labelWidth,
5239                         html : this.fieldLabel
5240                         
5241                     },
5242                     {
5243                         cls : "col-sm-" + (12 - this.labelWidth), 
5244                         cn: [
5245                             inputblock
5246                         ]
5247                     }
5248                     
5249                 ];
5250         } else if ( this.fieldLabel.length) {
5251                 Roo.log(" label");
5252                  cfg.cn = [
5253                    
5254                     {
5255                         tag: 'label',
5256                         //cls : 'input-group-addon',
5257                         html : this.fieldLabel
5258                         
5259                     },
5260                     
5261                     inputblock
5262                     
5263                 ];
5264
5265         } else {
5266             
5267                    Roo.log(" no label && no align");
5268                 cfg.cn = [
5269                     
5270                         inputblock
5271                     
5272                 ];
5273                 
5274                 
5275         }
5276         
5277         if (this.disabled) {
5278             input.disabled=true;
5279         }
5280         
5281         return cfg;
5282         
5283     },
5284     /**
5285      * return the real textarea element.
5286      */
5287     inputEl: function ()
5288     {
5289         return this.el.select('textarea.form-control',true).first();
5290     }
5291 });
5292
5293  
5294 /*
5295  * - LGPL
5296  *
5297  * trigger field - base class for combo..
5298  * 
5299  */
5300  
5301 /**
5302  * @class Roo.bootstrap.TriggerField
5303  * @extends Roo.bootstrap.Input
5304  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5305  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5306  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5307  * for which you can provide a custom implementation.  For example:
5308  * <pre><code>
5309 var trigger = new Roo.bootstrap.TriggerField();
5310 trigger.onTriggerClick = myTriggerFn;
5311 trigger.applyTo('my-field');
5312 </code></pre>
5313  *
5314  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5315  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5316  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5317  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5318  * @constructor
5319  * Create a new TriggerField.
5320  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5321  * to the base TextField)
5322  */
5323 Roo.bootstrap.TriggerField = function(config){
5324     this.mimicing = false;
5325     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5326 };
5327
5328 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5329     /**
5330      * @cfg {String} triggerClass A CSS class to apply to the trigger
5331      */
5332      /**
5333      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5334      */
5335     hideTrigger:false,
5336
5337     /** @cfg {Boolean} grow @hide */
5338     /** @cfg {Number} growMin @hide */
5339     /** @cfg {Number} growMax @hide */
5340
5341     /**
5342      * @hide 
5343      * @method
5344      */
5345     autoSize: Roo.emptyFn,
5346     // private
5347     monitorTab : true,
5348     // private
5349     deferHeight : true,
5350
5351     
5352     actionMode : 'wrap',
5353     
5354     
5355     
5356     getAutoCreate : function(){
5357        
5358         var parent = this.parent();
5359         
5360         var align = this.parentLabelAlign();
5361         
5362         var id = Roo.id();
5363         
5364         var cfg = {
5365             cls: 'form-group' //input-group
5366         };
5367         
5368         
5369         var input =  {
5370             tag: 'input',
5371             id : id,
5372             type : this.inputType,
5373             cls : 'form-control',
5374             autocomplete: 'off',
5375             placeholder : this.placeholder || '' 
5376             
5377         };
5378         if (this.name) {
5379             input.name = this.name;
5380         }
5381         if (this.size) {
5382             input.cls += ' input-' + this.size;
5383         }
5384         
5385         if (this.disabled) {
5386             input.disabled=true;
5387         }
5388         
5389         var inputblock = input;
5390         
5391         if (this.before || this.after) {
5392             
5393             inputblock = {
5394                 cls : 'input-group',
5395                 cn :  [] 
5396             };
5397             if (this.before) {
5398                 inputblock.cn.push({
5399                     tag :'span',
5400                     cls : 'input-group-addon',
5401                     html : this.before
5402                 });
5403             }
5404             inputblock.cn.push(input);
5405             if (this.after) {
5406                 inputblock.cn.push({
5407                     tag :'span',
5408                     cls : 'input-group-addon',
5409                     html : this.after
5410                 });
5411             }
5412             
5413         };
5414         
5415         var box = {
5416             tag: 'div',
5417             cn: [
5418                 {
5419                     tag: 'input',
5420                     type : 'hidden',
5421                     cls: 'form-hidden-field'
5422                 },
5423                 inputblock
5424             ]
5425             
5426         };
5427         
5428         if(this.multiple){
5429             Roo.log('multiple');
5430             
5431             box = {
5432                 tag: 'div',
5433                 cn: [
5434                     {
5435                         tag: 'input',
5436                         type : 'hidden',
5437                         cls: 'form-hidden-field'
5438                     },
5439                     {
5440                         tag: 'ul',
5441                         cls: 'select2-choices',
5442                         cn:[
5443                             {
5444                                 tag: 'li',
5445                                 cls: 'select2-search-field',
5446                                 cn: [
5447
5448                                     inputblock
5449                                 ]
5450                             }
5451                         ]
5452                     }
5453                 ]
5454             }
5455         };
5456         
5457         var combobox = {
5458             cls: 'select2-container input-group',
5459             cn: [
5460                 box,
5461                 {
5462                     tag: 'ul',
5463                     cls: 'typeahead typeahead-long dropdown-menu',
5464                     style: 'display:none'
5465                 }
5466             ]
5467         };
5468         
5469         if(!this.multiple){
5470             combobox.cn.push({
5471                 tag :'span',
5472                 cls : 'input-group-addon btn dropdown-toggle',
5473                 cn : [
5474                     {
5475                         tag: 'span',
5476                         cls: 'caret'
5477                     },
5478                     {
5479                         tag: 'span',
5480                         cls: 'combobox-clear',
5481                         cn  : [
5482                             {
5483                                 tag : 'i',
5484                                 cls: 'icon-remove'
5485                             }
5486                         ]
5487                     }
5488                 ]
5489
5490             })
5491         }
5492         
5493         if(this.multiple){
5494             combobox.cls += ' select2-container-multi';
5495         }
5496         
5497         if (align ==='left' && this.fieldLabel.length) {
5498             
5499                 Roo.log("left and has label");
5500                 cfg.cn = [
5501                     
5502                     {
5503                         tag: 'label',
5504                         'for' :  id,
5505                         cls : 'control-label col-sm-' + this.labelWidth,
5506                         html : this.fieldLabel
5507                         
5508                     },
5509                     {
5510                         cls : "col-sm-" + (12 - this.labelWidth), 
5511                         cn: [
5512                             combobox
5513                         ]
5514                     }
5515                     
5516                 ];
5517         } else if ( this.fieldLabel.length) {
5518                 Roo.log(" label");
5519                  cfg.cn = [
5520                    
5521                     {
5522                         tag: 'label',
5523                         //cls : 'input-group-addon',
5524                         html : this.fieldLabel
5525                         
5526                     },
5527                     
5528                     combobox
5529                     
5530                 ];
5531
5532         } else {
5533             
5534                 Roo.log(" no label && no align");
5535                 cfg = combobox
5536                      
5537                 
5538         }
5539          
5540         var settings=this;
5541         ['xs','sm','md','lg'].map(function(size){
5542             if (settings[size]) {
5543                 cfg.cls += ' col-' + size + '-' + settings[size];
5544             }
5545         });
5546         
5547         return cfg;
5548         
5549     },
5550     
5551     
5552     
5553     // private
5554     onResize : function(w, h){
5555 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5556 //        if(typeof w == 'number'){
5557 //            var x = w - this.trigger.getWidth();
5558 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5559 //            this.trigger.setStyle('left', x+'px');
5560 //        }
5561     },
5562
5563     // private
5564     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5565
5566     // private
5567     getResizeEl : function(){
5568         return this.inputEl();
5569     },
5570
5571     // private
5572     getPositionEl : function(){
5573         return this.inputEl();
5574     },
5575
5576     // private
5577     alignErrorIcon : function(){
5578         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5579     },
5580
5581     // private
5582     initEvents : function(){
5583         
5584         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5585         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5586         if(!this.multiple){
5587             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5588             if(this.hideTrigger){
5589                 this.trigger.setDisplayed(false);
5590             }
5591             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5592         }
5593         
5594         if(this.multiple){
5595             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5596         }
5597         
5598         //this.trigger.addClassOnOver('x-form-trigger-over');
5599         //this.trigger.addClassOnClick('x-form-trigger-click');
5600         
5601         //if(!this.width){
5602         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5603         //}
5604     },
5605
5606     // private
5607     initTrigger : function(){
5608        
5609     },
5610
5611     // private
5612     onDestroy : function(){
5613         if(this.trigger){
5614             this.trigger.removeAllListeners();
5615           //  this.trigger.remove();
5616         }
5617         //if(this.wrap){
5618         //    this.wrap.remove();
5619         //}
5620         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5621     },
5622
5623     // private
5624     onFocus : function(){
5625         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5626         /*
5627         if(!this.mimicing){
5628             this.wrap.addClass('x-trigger-wrap-focus');
5629             this.mimicing = true;
5630             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5631             if(this.monitorTab){
5632                 this.el.on("keydown", this.checkTab, this);
5633             }
5634         }
5635         */
5636     },
5637
5638     // private
5639     checkTab : function(e){
5640         if(e.getKey() == e.TAB){
5641             this.triggerBlur();
5642         }
5643     },
5644
5645     // private
5646     onBlur : function(){
5647         // do nothing
5648     },
5649
5650     // private
5651     mimicBlur : function(e, t){
5652         /*
5653         if(!this.wrap.contains(t) && this.validateBlur()){
5654             this.triggerBlur();
5655         }
5656         */
5657     },
5658
5659     // private
5660     triggerBlur : function(){
5661         this.mimicing = false;
5662         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5663         if(this.monitorTab){
5664             this.el.un("keydown", this.checkTab, this);
5665         }
5666         //this.wrap.removeClass('x-trigger-wrap-focus');
5667         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5668     },
5669
5670     // private
5671     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5672     validateBlur : function(e, t){
5673         return true;
5674     },
5675
5676     // private
5677     onDisable : function(){
5678         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5679         //if(this.wrap){
5680         //    this.wrap.addClass('x-item-disabled');
5681         //}
5682     },
5683
5684     // private
5685     onEnable : function(){
5686         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5687         //if(this.wrap){
5688         //    this.el.removeClass('x-item-disabled');
5689         //}
5690     },
5691
5692     // private
5693     onShow : function(){
5694         var ae = this.getActionEl();
5695         
5696         if(ae){
5697             ae.dom.style.display = '';
5698             ae.dom.style.visibility = 'visible';
5699         }
5700     },
5701
5702     // private
5703     
5704     onHide : function(){
5705         var ae = this.getActionEl();
5706         ae.dom.style.display = 'none';
5707     },
5708
5709     /**
5710      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5711      * by an implementing function.
5712      * @method
5713      * @param {EventObject} e
5714      */
5715     onTriggerClick : Roo.emptyFn
5716 });
5717  /*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727
5728
5729 /**
5730  * @class Roo.data.SortTypes
5731  * @singleton
5732  * Defines the default sorting (casting?) comparison functions used when sorting data.
5733  */
5734 Roo.data.SortTypes = {
5735     /**
5736      * Default sort that does nothing
5737      * @param {Mixed} s The value being converted
5738      * @return {Mixed} The comparison value
5739      */
5740     none : function(s){
5741         return s;
5742     },
5743     
5744     /**
5745      * The regular expression used to strip tags
5746      * @type {RegExp}
5747      * @property
5748      */
5749     stripTagsRE : /<\/?[^>]+>/gi,
5750     
5751     /**
5752      * Strips all HTML tags to sort on text only
5753      * @param {Mixed} s The value being converted
5754      * @return {String} The comparison value
5755      */
5756     asText : function(s){
5757         return String(s).replace(this.stripTagsRE, "");
5758     },
5759     
5760     /**
5761      * Strips all HTML tags to sort on text only - Case insensitive
5762      * @param {Mixed} s The value being converted
5763      * @return {String} The comparison value
5764      */
5765     asUCText : function(s){
5766         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5767     },
5768     
5769     /**
5770      * Case insensitive string
5771      * @param {Mixed} s The value being converted
5772      * @return {String} The comparison value
5773      */
5774     asUCString : function(s) {
5775         return String(s).toUpperCase();
5776     },
5777     
5778     /**
5779      * Date sorting
5780      * @param {Mixed} s The value being converted
5781      * @return {Number} The comparison value
5782      */
5783     asDate : function(s) {
5784         if(!s){
5785             return 0;
5786         }
5787         if(s instanceof Date){
5788             return s.getTime();
5789         }
5790         return Date.parse(String(s));
5791     },
5792     
5793     /**
5794      * Float sorting
5795      * @param {Mixed} s The value being converted
5796      * @return {Float} The comparison value
5797      */
5798     asFloat : function(s) {
5799         var val = parseFloat(String(s).replace(/,/g, ""));
5800         if(isNaN(val)) val = 0;
5801         return val;
5802     },
5803     
5804     /**
5805      * Integer sorting
5806      * @param {Mixed} s The value being converted
5807      * @return {Number} The comparison value
5808      */
5809     asInt : function(s) {
5810         var val = parseInt(String(s).replace(/,/g, ""));
5811         if(isNaN(val)) val = 0;
5812         return val;
5813     }
5814 };/*
5815  * Based on:
5816  * Ext JS Library 1.1.1
5817  * Copyright(c) 2006-2007, Ext JS, LLC.
5818  *
5819  * Originally Released Under LGPL - original licence link has changed is not relivant.
5820  *
5821  * Fork - LGPL
5822  * <script type="text/javascript">
5823  */
5824
5825 /**
5826 * @class Roo.data.Record
5827  * Instances of this class encapsulate both record <em>definition</em> information, and record
5828  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5829  * to access Records cached in an {@link Roo.data.Store} object.<br>
5830  * <p>
5831  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5832  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5833  * objects.<br>
5834  * <p>
5835  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5836  * @constructor
5837  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5838  * {@link #create}. The parameters are the same.
5839  * @param {Array} data An associative Array of data values keyed by the field name.
5840  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5841  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5842  * not specified an integer id is generated.
5843  */
5844 Roo.data.Record = function(data, id){
5845     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5846     this.data = data;
5847 };
5848
5849 /**
5850  * Generate a constructor for a specific record layout.
5851  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5852  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5853  * Each field definition object may contain the following properties: <ul>
5854  * <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,
5855  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5856  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5857  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5858  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5859  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5860  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5861  * this may be omitted.</p></li>
5862  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5863  * <ul><li>auto (Default, implies no conversion)</li>
5864  * <li>string</li>
5865  * <li>int</li>
5866  * <li>float</li>
5867  * <li>boolean</li>
5868  * <li>date</li></ul></p></li>
5869  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5870  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5871  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5872  * by the Reader into an object that will be stored in the Record. It is passed the
5873  * following parameters:<ul>
5874  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5875  * </ul></p></li>
5876  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5877  * </ul>
5878  * <br>usage:<br><pre><code>
5879 var TopicRecord = Roo.data.Record.create(
5880     {name: 'title', mapping: 'topic_title'},
5881     {name: 'author', mapping: 'username'},
5882     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5883     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5884     {name: 'lastPoster', mapping: 'user2'},
5885     {name: 'excerpt', mapping: 'post_text'}
5886 );
5887
5888 var myNewRecord = new TopicRecord({
5889     title: 'Do my job please',
5890     author: 'noobie',
5891     totalPosts: 1,
5892     lastPost: new Date(),
5893     lastPoster: 'Animal',
5894     excerpt: 'No way dude!'
5895 });
5896 myStore.add(myNewRecord);
5897 </code></pre>
5898  * @method create
5899  * @static
5900  */
5901 Roo.data.Record.create = function(o){
5902     var f = function(){
5903         f.superclass.constructor.apply(this, arguments);
5904     };
5905     Roo.extend(f, Roo.data.Record);
5906     var p = f.prototype;
5907     p.fields = new Roo.util.MixedCollection(false, function(field){
5908         return field.name;
5909     });
5910     for(var i = 0, len = o.length; i < len; i++){
5911         p.fields.add(new Roo.data.Field(o[i]));
5912     }
5913     f.getField = function(name){
5914         return p.fields.get(name);  
5915     };
5916     return f;
5917 };
5918
5919 Roo.data.Record.AUTO_ID = 1000;
5920 Roo.data.Record.EDIT = 'edit';
5921 Roo.data.Record.REJECT = 'reject';
5922 Roo.data.Record.COMMIT = 'commit';
5923
5924 Roo.data.Record.prototype = {
5925     /**
5926      * Readonly flag - true if this record has been modified.
5927      * @type Boolean
5928      */
5929     dirty : false,
5930     editing : false,
5931     error: null,
5932     modified: null,
5933
5934     // private
5935     join : function(store){
5936         this.store = store;
5937     },
5938
5939     /**
5940      * Set the named field to the specified value.
5941      * @param {String} name The name of the field to set.
5942      * @param {Object} value The value to set the field to.
5943      */
5944     set : function(name, value){
5945         if(this.data[name] == value){
5946             return;
5947         }
5948         this.dirty = true;
5949         if(!this.modified){
5950             this.modified = {};
5951         }
5952         if(typeof this.modified[name] == 'undefined'){
5953             this.modified[name] = this.data[name];
5954         }
5955         this.data[name] = value;
5956         if(!this.editing && this.store){
5957             this.store.afterEdit(this);
5958         }       
5959     },
5960
5961     /**
5962      * Get the value of the named field.
5963      * @param {String} name The name of the field to get the value of.
5964      * @return {Object} The value of the field.
5965      */
5966     get : function(name){
5967         return this.data[name]; 
5968     },
5969
5970     // private
5971     beginEdit : function(){
5972         this.editing = true;
5973         this.modified = {}; 
5974     },
5975
5976     // private
5977     cancelEdit : function(){
5978         this.editing = false;
5979         delete this.modified;
5980     },
5981
5982     // private
5983     endEdit : function(){
5984         this.editing = false;
5985         if(this.dirty && this.store){
5986             this.store.afterEdit(this);
5987         }
5988     },
5989
5990     /**
5991      * Usually called by the {@link Roo.data.Store} which owns the Record.
5992      * Rejects all changes made to the Record since either creation, or the last commit operation.
5993      * Modified fields are reverted to their original values.
5994      * <p>
5995      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5996      * of reject operations.
5997      */
5998     reject : function(){
5999         var m = this.modified;
6000         for(var n in m){
6001             if(typeof m[n] != "function"){
6002                 this.data[n] = m[n];
6003             }
6004         }
6005         this.dirty = false;
6006         delete this.modified;
6007         this.editing = false;
6008         if(this.store){
6009             this.store.afterReject(this);
6010         }
6011     },
6012
6013     /**
6014      * Usually called by the {@link Roo.data.Store} which owns the Record.
6015      * Commits all changes made to the Record since either creation, or the last commit operation.
6016      * <p>
6017      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6018      * of commit operations.
6019      */
6020     commit : function(){
6021         this.dirty = false;
6022         delete this.modified;
6023         this.editing = false;
6024         if(this.store){
6025             this.store.afterCommit(this);
6026         }
6027     },
6028
6029     // private
6030     hasError : function(){
6031         return this.error != null;
6032     },
6033
6034     // private
6035     clearError : function(){
6036         this.error = null;
6037     },
6038
6039     /**
6040      * Creates a copy of this record.
6041      * @param {String} id (optional) A new record id if you don't want to use this record's id
6042      * @return {Record}
6043      */
6044     copy : function(newId) {
6045         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6046     }
6047 };/*
6048  * Based on:
6049  * Ext JS Library 1.1.1
6050  * Copyright(c) 2006-2007, Ext JS, LLC.
6051  *
6052  * Originally Released Under LGPL - original licence link has changed is not relivant.
6053  *
6054  * Fork - LGPL
6055  * <script type="text/javascript">
6056  */
6057
6058
6059
6060 /**
6061  * @class Roo.data.Store
6062  * @extends Roo.util.Observable
6063  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6064  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6065  * <p>
6066  * 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
6067  * has no knowledge of the format of the data returned by the Proxy.<br>
6068  * <p>
6069  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6070  * instances from the data object. These records are cached and made available through accessor functions.
6071  * @constructor
6072  * Creates a new Store.
6073  * @param {Object} config A config object containing the objects needed for the Store to access data,
6074  * and read the data into Records.
6075  */
6076 Roo.data.Store = function(config){
6077     this.data = new Roo.util.MixedCollection(false);
6078     this.data.getKey = function(o){
6079         return o.id;
6080     };
6081     this.baseParams = {};
6082     // private
6083     this.paramNames = {
6084         "start" : "start",
6085         "limit" : "limit",
6086         "sort" : "sort",
6087         "dir" : "dir",
6088         "multisort" : "_multisort"
6089     };
6090
6091     if(config && config.data){
6092         this.inlineData = config.data;
6093         delete config.data;
6094     }
6095
6096     Roo.apply(this, config);
6097     
6098     if(this.reader){ // reader passed
6099         this.reader = Roo.factory(this.reader, Roo.data);
6100         this.reader.xmodule = this.xmodule || false;
6101         if(!this.recordType){
6102             this.recordType = this.reader.recordType;
6103         }
6104         if(this.reader.onMetaChange){
6105             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6106         }
6107     }
6108
6109     if(this.recordType){
6110         this.fields = this.recordType.prototype.fields;
6111     }
6112     this.modified = [];
6113
6114     this.addEvents({
6115         /**
6116          * @event datachanged
6117          * Fires when the data cache has changed, and a widget which is using this Store
6118          * as a Record cache should refresh its view.
6119          * @param {Store} this
6120          */
6121         datachanged : true,
6122         /**
6123          * @event metachange
6124          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6125          * @param {Store} this
6126          * @param {Object} meta The JSON metadata
6127          */
6128         metachange : true,
6129         /**
6130          * @event add
6131          * Fires when Records have been added to the Store
6132          * @param {Store} this
6133          * @param {Roo.data.Record[]} records The array of Records added
6134          * @param {Number} index The index at which the record(s) were added
6135          */
6136         add : true,
6137         /**
6138          * @event remove
6139          * Fires when a Record has been removed from the Store
6140          * @param {Store} this
6141          * @param {Roo.data.Record} record The Record that was removed
6142          * @param {Number} index The index at which the record was removed
6143          */
6144         remove : true,
6145         /**
6146          * @event update
6147          * Fires when a Record has been updated
6148          * @param {Store} this
6149          * @param {Roo.data.Record} record The Record that was updated
6150          * @param {String} operation The update operation being performed.  Value may be one of:
6151          * <pre><code>
6152  Roo.data.Record.EDIT
6153  Roo.data.Record.REJECT
6154  Roo.data.Record.COMMIT
6155          * </code></pre>
6156          */
6157         update : true,
6158         /**
6159          * @event clear
6160          * Fires when the data cache has been cleared.
6161          * @param {Store} this
6162          */
6163         clear : true,
6164         /**
6165          * @event beforeload
6166          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6167          * the load action will be canceled.
6168          * @param {Store} this
6169          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6170          */
6171         beforeload : true,
6172         /**
6173          * @event beforeloadadd
6174          * Fires after a new set of Records has been loaded.
6175          * @param {Store} this
6176          * @param {Roo.data.Record[]} records The Records that were loaded
6177          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6178          */
6179         beforeloadadd : true,
6180         /**
6181          * @event load
6182          * Fires after a new set of Records has been loaded, before they are added to the store.
6183          * @param {Store} this
6184          * @param {Roo.data.Record[]} records The Records that were loaded
6185          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6186          * @params {Object} return from reader
6187          */
6188         load : true,
6189         /**
6190          * @event loadexception
6191          * Fires if an exception occurs in the Proxy during loading.
6192          * Called with the signature of the Proxy's "loadexception" event.
6193          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6194          * 
6195          * @param {Proxy} 
6196          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6197          * @param {Object} load options 
6198          * @param {Object} jsonData from your request (normally this contains the Exception)
6199          */
6200         loadexception : true
6201     });
6202     
6203     if(this.proxy){
6204         this.proxy = Roo.factory(this.proxy, Roo.data);
6205         this.proxy.xmodule = this.xmodule || false;
6206         this.relayEvents(this.proxy,  ["loadexception"]);
6207     }
6208     this.sortToggle = {};
6209     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6210
6211     Roo.data.Store.superclass.constructor.call(this);
6212
6213     if(this.inlineData){
6214         this.loadData(this.inlineData);
6215         delete this.inlineData;
6216     }
6217 };
6218
6219 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6220      /**
6221     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6222     * without a remote query - used by combo/forms at present.
6223     */
6224     
6225     /**
6226     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6227     */
6228     /**
6229     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6230     */
6231     /**
6232     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6233     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6234     */
6235     /**
6236     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6237     * on any HTTP request
6238     */
6239     /**
6240     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6241     */
6242     /**
6243     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6244     */
6245     multiSort: false,
6246     /**
6247     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6248     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6249     */
6250     remoteSort : false,
6251
6252     /**
6253     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6254      * loaded or when a record is removed. (defaults to false).
6255     */
6256     pruneModifiedRecords : false,
6257
6258     // private
6259     lastOptions : null,
6260
6261     /**
6262      * Add Records to the Store and fires the add event.
6263      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6264      */
6265     add : function(records){
6266         records = [].concat(records);
6267         for(var i = 0, len = records.length; i < len; i++){
6268             records[i].join(this);
6269         }
6270         var index = this.data.length;
6271         this.data.addAll(records);
6272         this.fireEvent("add", this, records, index);
6273     },
6274
6275     /**
6276      * Remove a Record from the Store and fires the remove event.
6277      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6278      */
6279     remove : function(record){
6280         var index = this.data.indexOf(record);
6281         this.data.removeAt(index);
6282         if(this.pruneModifiedRecords){
6283             this.modified.remove(record);
6284         }
6285         this.fireEvent("remove", this, record, index);
6286     },
6287
6288     /**
6289      * Remove all Records from the Store and fires the clear event.
6290      */
6291     removeAll : function(){
6292         this.data.clear();
6293         if(this.pruneModifiedRecords){
6294             this.modified = [];
6295         }
6296         this.fireEvent("clear", this);
6297     },
6298
6299     /**
6300      * Inserts Records to the Store at the given index and fires the add event.
6301      * @param {Number} index The start index at which to insert the passed Records.
6302      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6303      */
6304     insert : function(index, records){
6305         records = [].concat(records);
6306         for(var i = 0, len = records.length; i < len; i++){
6307             this.data.insert(index, records[i]);
6308             records[i].join(this);
6309         }
6310         this.fireEvent("add", this, records, index);
6311     },
6312
6313     /**
6314      * Get the index within the cache of the passed Record.
6315      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6316      * @return {Number} The index of the passed Record. Returns -1 if not found.
6317      */
6318     indexOf : function(record){
6319         return this.data.indexOf(record);
6320     },
6321
6322     /**
6323      * Get the index within the cache of the Record with the passed id.
6324      * @param {String} id The id of the Record to find.
6325      * @return {Number} The index of the Record. Returns -1 if not found.
6326      */
6327     indexOfId : function(id){
6328         return this.data.indexOfKey(id);
6329     },
6330
6331     /**
6332      * Get the Record with the specified id.
6333      * @param {String} id The id of the Record to find.
6334      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6335      */
6336     getById : function(id){
6337         return this.data.key(id);
6338     },
6339
6340     /**
6341      * Get the Record at the specified index.
6342      * @param {Number} index The index of the Record to find.
6343      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6344      */
6345     getAt : function(index){
6346         return this.data.itemAt(index);
6347     },
6348
6349     /**
6350      * Returns a range of Records between specified indices.
6351      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6352      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6353      * @return {Roo.data.Record[]} An array of Records
6354      */
6355     getRange : function(start, end){
6356         return this.data.getRange(start, end);
6357     },
6358
6359     // private
6360     storeOptions : function(o){
6361         o = Roo.apply({}, o);
6362         delete o.callback;
6363         delete o.scope;
6364         this.lastOptions = o;
6365     },
6366
6367     /**
6368      * Loads the Record cache from the configured Proxy using the configured Reader.
6369      * <p>
6370      * If using remote paging, then the first load call must specify the <em>start</em>
6371      * and <em>limit</em> properties in the options.params property to establish the initial
6372      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6373      * <p>
6374      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6375      * and this call will return before the new data has been loaded. Perform any post-processing
6376      * in a callback function, or in a "load" event handler.</strong>
6377      * <p>
6378      * @param {Object} options An object containing properties which control loading options:<ul>
6379      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6380      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6381      * passed the following arguments:<ul>
6382      * <li>r : Roo.data.Record[]</li>
6383      * <li>options: Options object from the load call</li>
6384      * <li>success: Boolean success indicator</li></ul></li>
6385      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6386      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6387      * </ul>
6388      */
6389     load : function(options){
6390         options = options || {};
6391         if(this.fireEvent("beforeload", this, options) !== false){
6392             this.storeOptions(options);
6393             var p = Roo.apply(options.params || {}, this.baseParams);
6394             // if meta was not loaded from remote source.. try requesting it.
6395             if (!this.reader.metaFromRemote) {
6396                 p._requestMeta = 1;
6397             }
6398             if(this.sortInfo && this.remoteSort){
6399                 var pn = this.paramNames;
6400                 p[pn["sort"]] = this.sortInfo.field;
6401                 p[pn["dir"]] = this.sortInfo.direction;
6402             }
6403             if (this.multiSort) {
6404                 var pn = this.paramNames;
6405                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6406             }
6407             
6408             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6409         }
6410     },
6411
6412     /**
6413      * Reloads the Record cache from the configured Proxy using the configured Reader and
6414      * the options from the last load operation performed.
6415      * @param {Object} options (optional) An object containing properties which may override the options
6416      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6417      * the most recently used options are reused).
6418      */
6419     reload : function(options){
6420         this.load(Roo.applyIf(options||{}, this.lastOptions));
6421     },
6422
6423     // private
6424     // Called as a callback by the Reader during a load operation.
6425     loadRecords : function(o, options, success){
6426         if(!o || success === false){
6427             if(success !== false){
6428                 this.fireEvent("load", this, [], options, o);
6429             }
6430             if(options.callback){
6431                 options.callback.call(options.scope || this, [], options, false);
6432             }
6433             return;
6434         }
6435         // if data returned failure - throw an exception.
6436         if (o.success === false) {
6437             // show a message if no listener is registered.
6438             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6439                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6440             }
6441             // loadmask wil be hooked into this..
6442             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6443             return;
6444         }
6445         var r = o.records, t = o.totalRecords || r.length;
6446         
6447         this.fireEvent("beforeloadadd", this, r, options, o);
6448         
6449         if(!options || options.add !== true){
6450             if(this.pruneModifiedRecords){
6451                 this.modified = [];
6452             }
6453             for(var i = 0, len = r.length; i < len; i++){
6454                 r[i].join(this);
6455             }
6456             if(this.snapshot){
6457                 this.data = this.snapshot;
6458                 delete this.snapshot;
6459             }
6460             this.data.clear();
6461             this.data.addAll(r);
6462             this.totalLength = t;
6463             this.applySort();
6464             this.fireEvent("datachanged", this);
6465         }else{
6466             this.totalLength = Math.max(t, this.data.length+r.length);
6467             this.add(r);
6468         }
6469         this.fireEvent("load", this, r, options, o);
6470         if(options.callback){
6471             options.callback.call(options.scope || this, r, options, true);
6472         }
6473     },
6474
6475
6476     /**
6477      * Loads data from a passed data block. A Reader which understands the format of the data
6478      * must have been configured in the constructor.
6479      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6480      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6481      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6482      */
6483     loadData : function(o, append){
6484         var r = this.reader.readRecords(o);
6485         this.loadRecords(r, {add: append}, true);
6486     },
6487
6488     /**
6489      * Gets the number of cached records.
6490      * <p>
6491      * <em>If using paging, this may not be the total size of the dataset. If the data object
6492      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6493      * the data set size</em>
6494      */
6495     getCount : function(){
6496         return this.data.length || 0;
6497     },
6498
6499     /**
6500      * Gets the total number of records in the dataset as returned by the server.
6501      * <p>
6502      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6503      * the dataset size</em>
6504      */
6505     getTotalCount : function(){
6506         return this.totalLength || 0;
6507     },
6508
6509     /**
6510      * Returns the sort state of the Store as an object with two properties:
6511      * <pre><code>
6512  field {String} The name of the field by which the Records are sorted
6513  direction {String} The sort order, "ASC" or "DESC"
6514      * </code></pre>
6515      */
6516     getSortState : function(){
6517         return this.sortInfo;
6518     },
6519
6520     // private
6521     applySort : function(){
6522         if(this.sortInfo && !this.remoteSort){
6523             var s = this.sortInfo, f = s.field;
6524             var st = this.fields.get(f).sortType;
6525             var fn = function(r1, r2){
6526                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6527                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6528             };
6529             this.data.sort(s.direction, fn);
6530             if(this.snapshot && this.snapshot != this.data){
6531                 this.snapshot.sort(s.direction, fn);
6532             }
6533         }
6534     },
6535
6536     /**
6537      * Sets the default sort column and order to be used by the next load operation.
6538      * @param {String} fieldName The name of the field to sort by.
6539      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6540      */
6541     setDefaultSort : function(field, dir){
6542         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6543     },
6544
6545     /**
6546      * Sort the Records.
6547      * If remote sorting is used, the sort is performed on the server, and the cache is
6548      * reloaded. If local sorting is used, the cache is sorted internally.
6549      * @param {String} fieldName The name of the field to sort by.
6550      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6551      */
6552     sort : function(fieldName, dir){
6553         var f = this.fields.get(fieldName);
6554         if(!dir){
6555             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6556             
6557             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6558                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6559             }else{
6560                 dir = f.sortDir;
6561             }
6562         }
6563         this.sortToggle[f.name] = dir;
6564         this.sortInfo = {field: f.name, direction: dir};
6565         if(!this.remoteSort){
6566             this.applySort();
6567             this.fireEvent("datachanged", this);
6568         }else{
6569             this.load(this.lastOptions);
6570         }
6571     },
6572
6573     /**
6574      * Calls the specified function for each of the Records in the cache.
6575      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6576      * Returning <em>false</em> aborts and exits the iteration.
6577      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6578      */
6579     each : function(fn, scope){
6580         this.data.each(fn, scope);
6581     },
6582
6583     /**
6584      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6585      * (e.g., during paging).
6586      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6587      */
6588     getModifiedRecords : function(){
6589         return this.modified;
6590     },
6591
6592     // private
6593     createFilterFn : function(property, value, anyMatch){
6594         if(!value.exec){ // not a regex
6595             value = String(value);
6596             if(value.length == 0){
6597                 return false;
6598             }
6599             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6600         }
6601         return function(r){
6602             return value.test(r.data[property]);
6603         };
6604     },
6605
6606     /**
6607      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6608      * @param {String} property A field on your records
6609      * @param {Number} start The record index to start at (defaults to 0)
6610      * @param {Number} end The last record index to include (defaults to length - 1)
6611      * @return {Number} The sum
6612      */
6613     sum : function(property, start, end){
6614         var rs = this.data.items, v = 0;
6615         start = start || 0;
6616         end = (end || end === 0) ? end : rs.length-1;
6617
6618         for(var i = start; i <= end; i++){
6619             v += (rs[i].data[property] || 0);
6620         }
6621         return v;
6622     },
6623
6624     /**
6625      * Filter 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      */
6631     filter : function(property, value, anyMatch){
6632         var fn = this.createFilterFn(property, value, anyMatch);
6633         return fn ? this.filterBy(fn) : this.clearFilter();
6634     },
6635
6636     /**
6637      * Filter by a function. The specified function will be called with each
6638      * record in this data source. If the function returns true the record is included,
6639      * otherwise it is filtered.
6640      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6641      * @param {Object} scope (optional) The scope of the function (defaults to this)
6642      */
6643     filterBy : function(fn, scope){
6644         this.snapshot = this.snapshot || this.data;
6645         this.data = this.queryBy(fn, scope||this);
6646         this.fireEvent("datachanged", this);
6647     },
6648
6649     /**
6650      * Query the records by a specified property.
6651      * @param {String} field A field on your records
6652      * @param {String/RegExp} value Either a string that the field
6653      * should start with or a RegExp to test against the field
6654      * @param {Boolean} anyMatch True to match any part not just the beginning
6655      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6656      */
6657     query : function(property, value, anyMatch){
6658         var fn = this.createFilterFn(property, value, anyMatch);
6659         return fn ? this.queryBy(fn) : this.data.clone();
6660     },
6661
6662     /**
6663      * Query by a function. The specified function will be called with each
6664      * record in this data source. If the function returns true the record is included
6665      * in the results.
6666      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6667      * @param {Object} scope (optional) The scope of the function (defaults to this)
6668       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6669      **/
6670     queryBy : function(fn, scope){
6671         var data = this.snapshot || this.data;
6672         return data.filterBy(fn, scope||this);
6673     },
6674
6675     /**
6676      * Collects unique values for a particular dataIndex from this store.
6677      * @param {String} dataIndex The property to collect
6678      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6679      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6680      * @return {Array} An array of the unique values
6681      **/
6682     collect : function(dataIndex, allowNull, bypassFilter){
6683         var d = (bypassFilter === true && this.snapshot) ?
6684                 this.snapshot.items : this.data.items;
6685         var v, sv, r = [], l = {};
6686         for(var i = 0, len = d.length; i < len; i++){
6687             v = d[i].data[dataIndex];
6688             sv = String(v);
6689             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6690                 l[sv] = true;
6691                 r[r.length] = v;
6692             }
6693         }
6694         return r;
6695     },
6696
6697     /**
6698      * Revert to a view of the Record cache with no filtering applied.
6699      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6700      */
6701     clearFilter : function(suppressEvent){
6702         if(this.snapshot && this.snapshot != this.data){
6703             this.data = this.snapshot;
6704             delete this.snapshot;
6705             if(suppressEvent !== true){
6706                 this.fireEvent("datachanged", this);
6707             }
6708         }
6709     },
6710
6711     // private
6712     afterEdit : function(record){
6713         if(this.modified.indexOf(record) == -1){
6714             this.modified.push(record);
6715         }
6716         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6717     },
6718     
6719     // private
6720     afterReject : function(record){
6721         this.modified.remove(record);
6722         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6723     },
6724
6725     // private
6726     afterCommit : function(record){
6727         this.modified.remove(record);
6728         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6729     },
6730
6731     /**
6732      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6733      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6734      */
6735     commitChanges : function(){
6736         var m = this.modified.slice(0);
6737         this.modified = [];
6738         for(var i = 0, len = m.length; i < len; i++){
6739             m[i].commit();
6740         }
6741     },
6742
6743     /**
6744      * Cancel outstanding changes on all changed records.
6745      */
6746     rejectChanges : function(){
6747         var m = this.modified.slice(0);
6748         this.modified = [];
6749         for(var i = 0, len = m.length; i < len; i++){
6750             m[i].reject();
6751         }
6752     },
6753
6754     onMetaChange : function(meta, rtype, o){
6755         this.recordType = rtype;
6756         this.fields = rtype.prototype.fields;
6757         delete this.snapshot;
6758         this.sortInfo = meta.sortInfo || this.sortInfo;
6759         this.modified = [];
6760         this.fireEvent('metachange', this, this.reader.meta);
6761     },
6762     
6763     moveIndex : function(data, type)
6764     {
6765         var index = this.indexOf(data);
6766         
6767         var newIndex = index + type;
6768         
6769         this.remove(data);
6770         
6771         this.insert(newIndex, data);
6772         
6773     }
6774 });/*
6775  * Based on:
6776  * Ext JS Library 1.1.1
6777  * Copyright(c) 2006-2007, Ext JS, LLC.
6778  *
6779  * Originally Released Under LGPL - original licence link has changed is not relivant.
6780  *
6781  * Fork - LGPL
6782  * <script type="text/javascript">
6783  */
6784
6785 /**
6786  * @class Roo.data.SimpleStore
6787  * @extends Roo.data.Store
6788  * Small helper class to make creating Stores from Array data easier.
6789  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6790  * @cfg {Array} fields An array of field definition objects, or field name strings.
6791  * @cfg {Array} data The multi-dimensional array of data
6792  * @constructor
6793  * @param {Object} config
6794  */
6795 Roo.data.SimpleStore = function(config){
6796     Roo.data.SimpleStore.superclass.constructor.call(this, {
6797         isLocal : true,
6798         reader: new Roo.data.ArrayReader({
6799                 id: config.id
6800             },
6801             Roo.data.Record.create(config.fields)
6802         ),
6803         proxy : new Roo.data.MemoryProxy(config.data)
6804     });
6805     this.load();
6806 };
6807 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6808  * Based on:
6809  * Ext JS Library 1.1.1
6810  * Copyright(c) 2006-2007, Ext JS, LLC.
6811  *
6812  * Originally Released Under LGPL - original licence link has changed is not relivant.
6813  *
6814  * Fork - LGPL
6815  * <script type="text/javascript">
6816  */
6817
6818 /**
6819 /**
6820  * @extends Roo.data.Store
6821  * @class Roo.data.JsonStore
6822  * Small helper class to make creating Stores for JSON data easier. <br/>
6823 <pre><code>
6824 var store = new Roo.data.JsonStore({
6825     url: 'get-images.php',
6826     root: 'images',
6827     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6828 });
6829 </code></pre>
6830  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6831  * JsonReader and HttpProxy (unless inline data is provided).</b>
6832  * @cfg {Array} fields An array of field definition objects, or field name strings.
6833  * @constructor
6834  * @param {Object} config
6835  */
6836 Roo.data.JsonStore = function(c){
6837     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6838         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6839         reader: new Roo.data.JsonReader(c, c.fields)
6840     }));
6841 };
6842 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6843  * Based on:
6844  * Ext JS Library 1.1.1
6845  * Copyright(c) 2006-2007, Ext JS, LLC.
6846  *
6847  * Originally Released Under LGPL - original licence link has changed is not relivant.
6848  *
6849  * Fork - LGPL
6850  * <script type="text/javascript">
6851  */
6852
6853  
6854 Roo.data.Field = function(config){
6855     if(typeof config == "string"){
6856         config = {name: config};
6857     }
6858     Roo.apply(this, config);
6859     
6860     if(!this.type){
6861         this.type = "auto";
6862     }
6863     
6864     var st = Roo.data.SortTypes;
6865     // named sortTypes are supported, here we look them up
6866     if(typeof this.sortType == "string"){
6867         this.sortType = st[this.sortType];
6868     }
6869     
6870     // set default sortType for strings and dates
6871     if(!this.sortType){
6872         switch(this.type){
6873             case "string":
6874                 this.sortType = st.asUCString;
6875                 break;
6876             case "date":
6877                 this.sortType = st.asDate;
6878                 break;
6879             default:
6880                 this.sortType = st.none;
6881         }
6882     }
6883
6884     // define once
6885     var stripRe = /[\$,%]/g;
6886
6887     // prebuilt conversion function for this field, instead of
6888     // switching every time we're reading a value
6889     if(!this.convert){
6890         var cv, dateFormat = this.dateFormat;
6891         switch(this.type){
6892             case "":
6893             case "auto":
6894             case undefined:
6895                 cv = function(v){ return v; };
6896                 break;
6897             case "string":
6898                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6899                 break;
6900             case "int":
6901                 cv = function(v){
6902                     return v !== undefined && v !== null && v !== '' ?
6903                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6904                     };
6905                 break;
6906             case "float":
6907                 cv = function(v){
6908                     return v !== undefined && v !== null && v !== '' ?
6909                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6910                     };
6911                 break;
6912             case "bool":
6913             case "boolean":
6914                 cv = function(v){ return v === true || v === "true" || v == 1; };
6915                 break;
6916             case "date":
6917                 cv = function(v){
6918                     if(!v){
6919                         return '';
6920                     }
6921                     if(v instanceof Date){
6922                         return v;
6923                     }
6924                     if(dateFormat){
6925                         if(dateFormat == "timestamp"){
6926                             return new Date(v*1000);
6927                         }
6928                         return Date.parseDate(v, dateFormat);
6929                     }
6930                     var parsed = Date.parse(v);
6931                     return parsed ? new Date(parsed) : null;
6932                 };
6933              break;
6934             
6935         }
6936         this.convert = cv;
6937     }
6938 };
6939
6940 Roo.data.Field.prototype = {
6941     dateFormat: null,
6942     defaultValue: "",
6943     mapping: null,
6944     sortType : null,
6945     sortDir : "ASC"
6946 };/*
6947  * Based on:
6948  * Ext JS Library 1.1.1
6949  * Copyright(c) 2006-2007, Ext JS, LLC.
6950  *
6951  * Originally Released Under LGPL - original licence link has changed is not relivant.
6952  *
6953  * Fork - LGPL
6954  * <script type="text/javascript">
6955  */
6956  
6957 // Base class for reading structured data from a data source.  This class is intended to be
6958 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6959
6960 /**
6961  * @class Roo.data.DataReader
6962  * Base class for reading structured data from a data source.  This class is intended to be
6963  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6964  */
6965
6966 Roo.data.DataReader = function(meta, recordType){
6967     
6968     this.meta = meta;
6969     
6970     this.recordType = recordType instanceof Array ? 
6971         Roo.data.Record.create(recordType) : recordType;
6972 };
6973
6974 Roo.data.DataReader.prototype = {
6975      /**
6976      * Create an empty record
6977      * @param {Object} data (optional) - overlay some values
6978      * @return {Roo.data.Record} record created.
6979      */
6980     newRow :  function(d) {
6981         var da =  {};
6982         this.recordType.prototype.fields.each(function(c) {
6983             switch( c.type) {
6984                 case 'int' : da[c.name] = 0; break;
6985                 case 'date' : da[c.name] = new Date(); break;
6986                 case 'float' : da[c.name] = 0.0; break;
6987                 case 'boolean' : da[c.name] = false; break;
6988                 default : da[c.name] = ""; break;
6989             }
6990             
6991         });
6992         return new this.recordType(Roo.apply(da, d));
6993     }
6994     
6995 };/*
6996  * Based on:
6997  * Ext JS Library 1.1.1
6998  * Copyright(c) 2006-2007, Ext JS, LLC.
6999  *
7000  * Originally Released Under LGPL - original licence link has changed is not relivant.
7001  *
7002  * Fork - LGPL
7003  * <script type="text/javascript">
7004  */
7005
7006 /**
7007  * @class Roo.data.DataProxy
7008  * @extends Roo.data.Observable
7009  * This class is an abstract base class for implementations which provide retrieval of
7010  * unformatted data objects.<br>
7011  * <p>
7012  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7013  * (of the appropriate type which knows how to parse the data object) to provide a block of
7014  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7015  * <p>
7016  * Custom implementations must implement the load method as described in
7017  * {@link Roo.data.HttpProxy#load}.
7018  */
7019 Roo.data.DataProxy = function(){
7020     this.addEvents({
7021         /**
7022          * @event beforeload
7023          * Fires before a network request is made to retrieve a data object.
7024          * @param {Object} This DataProxy object.
7025          * @param {Object} params The params parameter to the load function.
7026          */
7027         beforeload : true,
7028         /**
7029          * @event load
7030          * Fires before the load method's callback is called.
7031          * @param {Object} This DataProxy object.
7032          * @param {Object} o The data object.
7033          * @param {Object} arg The callback argument object passed to the load function.
7034          */
7035         load : true,
7036         /**
7037          * @event loadexception
7038          * Fires if an Exception occurs during data retrieval.
7039          * @param {Object} This DataProxy object.
7040          * @param {Object} o The data object.
7041          * @param {Object} arg The callback argument object passed to the load function.
7042          * @param {Object} e The Exception.
7043          */
7044         loadexception : true
7045     });
7046     Roo.data.DataProxy.superclass.constructor.call(this);
7047 };
7048
7049 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7050
7051     /**
7052      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7053      */
7054 /*
7055  * Based on:
7056  * Ext JS Library 1.1.1
7057  * Copyright(c) 2006-2007, Ext JS, LLC.
7058  *
7059  * Originally Released Under LGPL - original licence link has changed is not relivant.
7060  *
7061  * Fork - LGPL
7062  * <script type="text/javascript">
7063  */
7064 /**
7065  * @class Roo.data.MemoryProxy
7066  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7067  * to the Reader when its load method is called.
7068  * @constructor
7069  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7070  */
7071 Roo.data.MemoryProxy = function(data){
7072     if (data.data) {
7073         data = data.data;
7074     }
7075     Roo.data.MemoryProxy.superclass.constructor.call(this);
7076     this.data = data;
7077 };
7078
7079 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7080     /**
7081      * Load data from the requested source (in this case an in-memory
7082      * data object passed to the constructor), read the data object into
7083      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7084      * process that block using the passed callback.
7085      * @param {Object} params This parameter is not used by the MemoryProxy class.
7086      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7087      * object into a block of Roo.data.Records.
7088      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7089      * The function must be passed <ul>
7090      * <li>The Record block object</li>
7091      * <li>The "arg" argument from the load function</li>
7092      * <li>A boolean success indicator</li>
7093      * </ul>
7094      * @param {Object} scope The scope in which to call the callback
7095      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7096      */
7097     load : function(params, reader, callback, scope, arg){
7098         params = params || {};
7099         var result;
7100         try {
7101             result = reader.readRecords(this.data);
7102         }catch(e){
7103             this.fireEvent("loadexception", this, arg, null, e);
7104             callback.call(scope, null, arg, false);
7105             return;
7106         }
7107         callback.call(scope, result, arg, true);
7108     },
7109     
7110     // private
7111     update : function(params, records){
7112         
7113     }
7114 });/*
7115  * Based on:
7116  * Ext JS Library 1.1.1
7117  * Copyright(c) 2006-2007, Ext JS, LLC.
7118  *
7119  * Originally Released Under LGPL - original licence link has changed is not relivant.
7120  *
7121  * Fork - LGPL
7122  * <script type="text/javascript">
7123  */
7124 /**
7125  * @class Roo.data.HttpProxy
7126  * @extends Roo.data.DataProxy
7127  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7128  * configured to reference a certain URL.<br><br>
7129  * <p>
7130  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7131  * from which the running page was served.<br><br>
7132  * <p>
7133  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7134  * <p>
7135  * Be aware that to enable the browser to parse an XML document, the server must set
7136  * the Content-Type header in the HTTP response to "text/xml".
7137  * @constructor
7138  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7139  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7140  * will be used to make the request.
7141  */
7142 Roo.data.HttpProxy = function(conn){
7143     Roo.data.HttpProxy.superclass.constructor.call(this);
7144     // is conn a conn config or a real conn?
7145     this.conn = conn;
7146     this.useAjax = !conn || !conn.events;
7147   
7148 };
7149
7150 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7151     // thse are take from connection...
7152     
7153     /**
7154      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7155      */
7156     /**
7157      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7158      * extra parameters to each request made by this object. (defaults to undefined)
7159      */
7160     /**
7161      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7162      *  to each request made by this object. (defaults to undefined)
7163      */
7164     /**
7165      * @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)
7166      */
7167     /**
7168      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7169      */
7170      /**
7171      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7172      * @type Boolean
7173      */
7174   
7175
7176     /**
7177      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7178      * @type Boolean
7179      */
7180     /**
7181      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7182      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7183      * a finer-grained basis than the DataProxy events.
7184      */
7185     getConnection : function(){
7186         return this.useAjax ? Roo.Ajax : this.conn;
7187     },
7188
7189     /**
7190      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7191      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7192      * process that block using the passed callback.
7193      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7194      * for the request to the remote server.
7195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7196      * object into a block of Roo.data.Records.
7197      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7198      * The function must be passed <ul>
7199      * <li>The Record block object</li>
7200      * <li>The "arg" argument from the load function</li>
7201      * <li>A boolean success indicator</li>
7202      * </ul>
7203      * @param {Object} scope The scope in which to call the callback
7204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7205      */
7206     load : function(params, reader, callback, scope, arg){
7207         if(this.fireEvent("beforeload", this, params) !== false){
7208             var  o = {
7209                 params : params || {},
7210                 request: {
7211                     callback : callback,
7212                     scope : scope,
7213                     arg : arg
7214                 },
7215                 reader: reader,
7216                 callback : this.loadResponse,
7217                 scope: this
7218             };
7219             if(this.useAjax){
7220                 Roo.applyIf(o, this.conn);
7221                 if(this.activeRequest){
7222                     Roo.Ajax.abort(this.activeRequest);
7223                 }
7224                 this.activeRequest = Roo.Ajax.request(o);
7225             }else{
7226                 this.conn.request(o);
7227             }
7228         }else{
7229             callback.call(scope||this, null, arg, false);
7230         }
7231     },
7232
7233     // private
7234     loadResponse : function(o, success, response){
7235         delete this.activeRequest;
7236         if(!success){
7237             this.fireEvent("loadexception", this, o, response);
7238             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7239             return;
7240         }
7241         var result;
7242         try {
7243             result = o.reader.read(response);
7244         }catch(e){
7245             this.fireEvent("loadexception", this, o, response, e);
7246             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7247             return;
7248         }
7249         
7250         this.fireEvent("load", this, o, o.request.arg);
7251         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7252     },
7253
7254     // private
7255     update : function(dataSet){
7256
7257     },
7258
7259     // private
7260     updateResponse : function(dataSet){
7261
7262     }
7263 });/*
7264  * Based on:
7265  * Ext JS Library 1.1.1
7266  * Copyright(c) 2006-2007, Ext JS, LLC.
7267  *
7268  * Originally Released Under LGPL - original licence link has changed is not relivant.
7269  *
7270  * Fork - LGPL
7271  * <script type="text/javascript">
7272  */
7273
7274 /**
7275  * @class Roo.data.ScriptTagProxy
7276  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7277  * other than the originating domain of the running page.<br><br>
7278  * <p>
7279  * <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
7280  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7281  * <p>
7282  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7283  * source code that is used as the source inside a &lt;script> tag.<br><br>
7284  * <p>
7285  * In order for the browser to process the returned data, the server must wrap the data object
7286  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7287  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7288  * depending on whether the callback name was passed:
7289  * <p>
7290  * <pre><code>
7291 boolean scriptTag = false;
7292 String cb = request.getParameter("callback");
7293 if (cb != null) {
7294     scriptTag = true;
7295     response.setContentType("text/javascript");
7296 } else {
7297     response.setContentType("application/x-json");
7298 }
7299 Writer out = response.getWriter();
7300 if (scriptTag) {
7301     out.write(cb + "(");
7302 }
7303 out.print(dataBlock.toJsonString());
7304 if (scriptTag) {
7305     out.write(");");
7306 }
7307 </pre></code>
7308  *
7309  * @constructor
7310  * @param {Object} config A configuration object.
7311  */
7312 Roo.data.ScriptTagProxy = function(config){
7313     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7314     Roo.apply(this, config);
7315     this.head = document.getElementsByTagName("head")[0];
7316 };
7317
7318 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7319
7320 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7321     /**
7322      * @cfg {String} url The URL from which to request the data object.
7323      */
7324     /**
7325      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7326      */
7327     timeout : 30000,
7328     /**
7329      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7330      * the server the name of the callback function set up by the load call to process the returned data object.
7331      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7332      * javascript output which calls this named function passing the data object as its only parameter.
7333      */
7334     callbackParam : "callback",
7335     /**
7336      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7337      * name to the request.
7338      */
7339     nocache : true,
7340
7341     /**
7342      * Load data from the configured URL, read the data object into
7343      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7344      * process that block using the passed callback.
7345      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7346      * for the request to the remote server.
7347      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7348      * object into a block of Roo.data.Records.
7349      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7350      * The function must be passed <ul>
7351      * <li>The Record block object</li>
7352      * <li>The "arg" argument from the load function</li>
7353      * <li>A boolean success indicator</li>
7354      * </ul>
7355      * @param {Object} scope The scope in which to call the callback
7356      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7357      */
7358     load : function(params, reader, callback, scope, arg){
7359         if(this.fireEvent("beforeload", this, params) !== false){
7360
7361             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7362
7363             var url = this.url;
7364             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7365             if(this.nocache){
7366                 url += "&_dc=" + (new Date().getTime());
7367             }
7368             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7369             var trans = {
7370                 id : transId,
7371                 cb : "stcCallback"+transId,
7372                 scriptId : "stcScript"+transId,
7373                 params : params,
7374                 arg : arg,
7375                 url : url,
7376                 callback : callback,
7377                 scope : scope,
7378                 reader : reader
7379             };
7380             var conn = this;
7381
7382             window[trans.cb] = function(o){
7383                 conn.handleResponse(o, trans);
7384             };
7385
7386             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7387
7388             if(this.autoAbort !== false){
7389                 this.abort();
7390             }
7391
7392             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7393
7394             var script = document.createElement("script");
7395             script.setAttribute("src", url);
7396             script.setAttribute("type", "text/javascript");
7397             script.setAttribute("id", trans.scriptId);
7398             this.head.appendChild(script);
7399
7400             this.trans = trans;
7401         }else{
7402             callback.call(scope||this, null, arg, false);
7403         }
7404     },
7405
7406     // private
7407     isLoading : function(){
7408         return this.trans ? true : false;
7409     },
7410
7411     /**
7412      * Abort the current server request.
7413      */
7414     abort : function(){
7415         if(this.isLoading()){
7416             this.destroyTrans(this.trans);
7417         }
7418     },
7419
7420     // private
7421     destroyTrans : function(trans, isLoaded){
7422         this.head.removeChild(document.getElementById(trans.scriptId));
7423         clearTimeout(trans.timeoutId);
7424         if(isLoaded){
7425             window[trans.cb] = undefined;
7426             try{
7427                 delete window[trans.cb];
7428             }catch(e){}
7429         }else{
7430             // if hasn't been loaded, wait for load to remove it to prevent script error
7431             window[trans.cb] = function(){
7432                 window[trans.cb] = undefined;
7433                 try{
7434                     delete window[trans.cb];
7435                 }catch(e){}
7436             };
7437         }
7438     },
7439
7440     // private
7441     handleResponse : function(o, trans){
7442         this.trans = false;
7443         this.destroyTrans(trans, true);
7444         var result;
7445         try {
7446             result = trans.reader.readRecords(o);
7447         }catch(e){
7448             this.fireEvent("loadexception", this, o, trans.arg, e);
7449             trans.callback.call(trans.scope||window, null, trans.arg, false);
7450             return;
7451         }
7452         this.fireEvent("load", this, o, trans.arg);
7453         trans.callback.call(trans.scope||window, result, trans.arg, true);
7454     },
7455
7456     // private
7457     handleFailure : function(trans){
7458         this.trans = false;
7459         this.destroyTrans(trans, false);
7460         this.fireEvent("loadexception", this, null, trans.arg);
7461         trans.callback.call(trans.scope||window, null, trans.arg, false);
7462     }
7463 });/*
7464  * Based on:
7465  * Ext JS Library 1.1.1
7466  * Copyright(c) 2006-2007, Ext JS, LLC.
7467  *
7468  * Originally Released Under LGPL - original licence link has changed is not relivant.
7469  *
7470  * Fork - LGPL
7471  * <script type="text/javascript">
7472  */
7473
7474 /**
7475  * @class Roo.data.JsonReader
7476  * @extends Roo.data.DataReader
7477  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7478  * based on mappings in a provided Roo.data.Record constructor.
7479  * 
7480  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7481  * in the reply previously. 
7482  * 
7483  * <p>
7484  * Example code:
7485  * <pre><code>
7486 var RecordDef = Roo.data.Record.create([
7487     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7488     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7489 ]);
7490 var myReader = new Roo.data.JsonReader({
7491     totalProperty: "results",    // The property which contains the total dataset size (optional)
7492     root: "rows",                // The property which contains an Array of row objects
7493     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7494 }, RecordDef);
7495 </code></pre>
7496  * <p>
7497  * This would consume a JSON file like this:
7498  * <pre><code>
7499 { 'results': 2, 'rows': [
7500     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7501     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7502 }
7503 </code></pre>
7504  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7505  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7506  * paged from the remote server.
7507  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7508  * @cfg {String} root name of the property which contains the Array of row objects.
7509  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7510  * @constructor
7511  * Create a new JsonReader
7512  * @param {Object} meta Metadata configuration options
7513  * @param {Object} recordType Either an Array of field definition objects,
7514  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7515  */
7516 Roo.data.JsonReader = function(meta, recordType){
7517     
7518     meta = meta || {};
7519     // set some defaults:
7520     Roo.applyIf(meta, {
7521         totalProperty: 'total',
7522         successProperty : 'success',
7523         root : 'data',
7524         id : 'id'
7525     });
7526     
7527     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7528 };
7529 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7530     
7531     /**
7532      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7533      * Used by Store query builder to append _requestMeta to params.
7534      * 
7535      */
7536     metaFromRemote : false,
7537     /**
7538      * This method is only used by a DataProxy which has retrieved data from a remote server.
7539      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7540      * @return {Object} data A data block which is used by an Roo.data.Store object as
7541      * a cache of Roo.data.Records.
7542      */
7543     read : function(response){
7544         var json = response.responseText;
7545        
7546         var o = /* eval:var:o */ eval("("+json+")");
7547         if(!o) {
7548             throw {message: "JsonReader.read: Json object not found"};
7549         }
7550         
7551         if(o.metaData){
7552             
7553             delete this.ef;
7554             this.metaFromRemote = true;
7555             this.meta = o.metaData;
7556             this.recordType = Roo.data.Record.create(o.metaData.fields);
7557             this.onMetaChange(this.meta, this.recordType, o);
7558         }
7559         return this.readRecords(o);
7560     },
7561
7562     // private function a store will implement
7563     onMetaChange : function(meta, recordType, o){
7564
7565     },
7566
7567     /**
7568          * @ignore
7569          */
7570     simpleAccess: function(obj, subsc) {
7571         return obj[subsc];
7572     },
7573
7574         /**
7575          * @ignore
7576          */
7577     getJsonAccessor: function(){
7578         var re = /[\[\.]/;
7579         return function(expr) {
7580             try {
7581                 return(re.test(expr))
7582                     ? new Function("obj", "return obj." + expr)
7583                     : function(obj){
7584                         return obj[expr];
7585                     };
7586             } catch(e){}
7587             return Roo.emptyFn;
7588         };
7589     }(),
7590
7591     /**
7592      * Create a data block containing Roo.data.Records from an XML document.
7593      * @param {Object} o An object which contains an Array of row objects in the property specified
7594      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7595      * which contains the total size of the dataset.
7596      * @return {Object} data A data block which is used by an Roo.data.Store object as
7597      * a cache of Roo.data.Records.
7598      */
7599     readRecords : function(o){
7600         /**
7601          * After any data loads, the raw JSON data is available for further custom processing.
7602          * @type Object
7603          */
7604         this.o = o;
7605         var s = this.meta, Record = this.recordType,
7606             f = Record.prototype.fields, fi = f.items, fl = f.length;
7607
7608 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7609         if (!this.ef) {
7610             if(s.totalProperty) {
7611                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7612                 }
7613                 if(s.successProperty) {
7614                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7615                 }
7616                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7617                 if (s.id) {
7618                         var g = this.getJsonAccessor(s.id);
7619                         this.getId = function(rec) {
7620                                 var r = g(rec);
7621                                 return (r === undefined || r === "") ? null : r;
7622                         };
7623                 } else {
7624                         this.getId = function(){return null;};
7625                 }
7626             this.ef = [];
7627             for(var jj = 0; jj < fl; jj++){
7628                 f = fi[jj];
7629                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7630                 this.ef[jj] = this.getJsonAccessor(map);
7631             }
7632         }
7633
7634         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7635         if(s.totalProperty){
7636             var vt = parseInt(this.getTotal(o), 10);
7637             if(!isNaN(vt)){
7638                 totalRecords = vt;
7639             }
7640         }
7641         if(s.successProperty){
7642             var vs = this.getSuccess(o);
7643             if(vs === false || vs === 'false'){
7644                 success = false;
7645             }
7646         }
7647         var records = [];
7648             for(var i = 0; i < c; i++){
7649                     var n = root[i];
7650                 var values = {};
7651                 var id = this.getId(n);
7652                 for(var j = 0; j < fl; j++){
7653                     f = fi[j];
7654                 var v = this.ef[j](n);
7655                 if (!f.convert) {
7656                     Roo.log('missing convert for ' + f.name);
7657                     Roo.log(f);
7658                     continue;
7659                 }
7660                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7661                 }
7662                 var record = new Record(values, id);
7663                 record.json = n;
7664                 records[i] = record;
7665             }
7666             return {
7667             raw : o,
7668                 success : success,
7669                 records : records,
7670                 totalRecords : totalRecords
7671             };
7672     }
7673 });/*
7674  * Based on:
7675  * Ext JS Library 1.1.1
7676  * Copyright(c) 2006-2007, Ext JS, LLC.
7677  *
7678  * Originally Released Under LGPL - original licence link has changed is not relivant.
7679  *
7680  * Fork - LGPL
7681  * <script type="text/javascript">
7682  */
7683
7684 /**
7685  * @class Roo.data.ArrayReader
7686  * @extends Roo.data.DataReader
7687  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7688  * Each element of that Array represents a row of data fields. The
7689  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7690  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7691  * <p>
7692  * Example code:.
7693  * <pre><code>
7694 var RecordDef = Roo.data.Record.create([
7695     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7696     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7697 ]);
7698 var myReader = new Roo.data.ArrayReader({
7699     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7700 }, RecordDef);
7701 </code></pre>
7702  * <p>
7703  * This would consume an Array like this:
7704  * <pre><code>
7705 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7706   </code></pre>
7707  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7708  * @constructor
7709  * Create a new JsonReader
7710  * @param {Object} meta Metadata configuration options.
7711  * @param {Object} recordType Either an Array of field definition objects
7712  * as specified to {@link Roo.data.Record#create},
7713  * or an {@link Roo.data.Record} object
7714  * created using {@link Roo.data.Record#create}.
7715  */
7716 Roo.data.ArrayReader = function(meta, recordType){
7717     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7718 };
7719
7720 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7721     /**
7722      * Create a data block containing Roo.data.Records from an XML document.
7723      * @param {Object} o An Array of row objects which represents the dataset.
7724      * @return {Object} data A data block which is used by an Roo.data.Store object as
7725      * a cache of Roo.data.Records.
7726      */
7727     readRecords : function(o){
7728         var sid = this.meta ? this.meta.id : null;
7729         var recordType = this.recordType, fields = recordType.prototype.fields;
7730         var records = [];
7731         var root = o;
7732             for(var i = 0; i < root.length; i++){
7733                     var n = root[i];
7734                 var values = {};
7735                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7736                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7737                 var f = fields.items[j];
7738                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7739                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7740                 v = f.convert(v);
7741                 values[f.name] = v;
7742             }
7743                 var record = new recordType(values, id);
7744                 record.json = n;
7745                 records[records.length] = record;
7746             }
7747             return {
7748                 records : records,
7749                 totalRecords : records.length
7750             };
7751     }
7752 });/*
7753  * - LGPL
7754  * * 
7755  */
7756
7757 /**
7758  * @class Roo.bootstrap.ComboBox
7759  * @extends Roo.bootstrap.TriggerField
7760  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7761  * @cfg {Boolean} append (true|false) default false
7762  * @constructor
7763  * Create a new ComboBox.
7764  * @param {Object} config Configuration options
7765  */
7766 Roo.bootstrap.ComboBox = function(config){
7767     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7768     this.addEvents({
7769         /**
7770          * @event expand
7771          * Fires when the dropdown list is expanded
7772              * @param {Roo.bootstrap.ComboBox} combo This combo box
7773              */
7774         'expand' : true,
7775         /**
7776          * @event collapse
7777          * Fires when the dropdown list is collapsed
7778              * @param {Roo.bootstrap.ComboBox} combo This combo box
7779              */
7780         'collapse' : true,
7781         /**
7782          * @event beforeselect
7783          * Fires before a list item is selected. Return false to cancel the selection.
7784              * @param {Roo.bootstrap.ComboBox} combo This combo box
7785              * @param {Roo.data.Record} record The data record returned from the underlying store
7786              * @param {Number} index The index of the selected item in the dropdown list
7787              */
7788         'beforeselect' : true,
7789         /**
7790          * @event select
7791          * Fires when a list item is selected
7792              * @param {Roo.bootstrap.ComboBox} combo This combo box
7793              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7794              * @param {Number} index The index of the selected item in the dropdown list
7795              */
7796         'select' : true,
7797         /**
7798          * @event beforequery
7799          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7800          * The event object passed has these properties:
7801              * @param {Roo.bootstrap.ComboBox} combo This combo box
7802              * @param {String} query The query
7803              * @param {Boolean} forceAll true to force "all" query
7804              * @param {Boolean} cancel true to cancel the query
7805              * @param {Object} e The query event object
7806              */
7807         'beforequery': true,
7808          /**
7809          * @event add
7810          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7811              * @param {Roo.bootstrap.ComboBox} combo This combo box
7812              */
7813         'add' : true,
7814         /**
7815          * @event edit
7816          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7817              * @param {Roo.bootstrap.ComboBox} combo This combo box
7818              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7819              */
7820         'edit' : true,
7821         /**
7822          * @event remove
7823          * Fires when the remove value from the combobox array
7824              * @param {Roo.bootstrap.ComboBox} combo This combo box
7825              */
7826         'remove' : true
7827         
7828     });
7829     
7830     
7831     this.selectedIndex = -1;
7832     if(this.mode == 'local'){
7833         if(config.queryDelay === undefined){
7834             this.queryDelay = 10;
7835         }
7836         if(config.minChars === undefined){
7837             this.minChars = 0;
7838         }
7839     }
7840 };
7841
7842 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7843      
7844     /**
7845      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7846      * rendering into an Roo.Editor, defaults to false)
7847      */
7848     /**
7849      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7850      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7851      */
7852     /**
7853      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7854      */
7855     /**
7856      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7857      * the dropdown list (defaults to undefined, with no header element)
7858      */
7859
7860      /**
7861      * @cfg {String/Roo.Template} tpl The template to use to render the output
7862      */
7863      
7864      /**
7865      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7866      */
7867     listWidth: undefined,
7868     /**
7869      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7870      * mode = 'remote' or 'text' if mode = 'local')
7871      */
7872     displayField: undefined,
7873     /**
7874      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7875      * mode = 'remote' or 'value' if mode = 'local'). 
7876      * Note: use of a valueField requires the user make a selection
7877      * in order for a value to be mapped.
7878      */
7879     valueField: undefined,
7880     
7881     
7882     /**
7883      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7884      * field's data value (defaults to the underlying DOM element's name)
7885      */
7886     hiddenName: undefined,
7887     /**
7888      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7889      */
7890     listClass: '',
7891     /**
7892      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7893      */
7894     selectedClass: 'active',
7895     
7896     /**
7897      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7898      */
7899     shadow:'sides',
7900     /**
7901      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7902      * anchor positions (defaults to 'tl-bl')
7903      */
7904     listAlign: 'tl-bl?',
7905     /**
7906      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7907      */
7908     maxHeight: 300,
7909     /**
7910      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7911      * query specified by the allQuery config option (defaults to 'query')
7912      */
7913     triggerAction: 'query',
7914     /**
7915      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7916      * (defaults to 4, does not apply if editable = false)
7917      */
7918     minChars : 4,
7919     /**
7920      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7921      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7922      */
7923     typeAhead: false,
7924     /**
7925      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7926      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7927      */
7928     queryDelay: 500,
7929     /**
7930      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7931      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7932      */
7933     pageSize: 0,
7934     /**
7935      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7936      * when editable = true (defaults to false)
7937      */
7938     selectOnFocus:false,
7939     /**
7940      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7941      */
7942     queryParam: 'query',
7943     /**
7944      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7945      * when mode = 'remote' (defaults to 'Loading...')
7946      */
7947     loadingText: 'Loading...',
7948     /**
7949      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7950      */
7951     resizable: false,
7952     /**
7953      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7954      */
7955     handleHeight : 8,
7956     /**
7957      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7958      * traditional select (defaults to true)
7959      */
7960     editable: true,
7961     /**
7962      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7963      */
7964     allQuery: '',
7965     /**
7966      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7967      */
7968     mode: 'remote',
7969     /**
7970      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7971      * listWidth has a higher value)
7972      */
7973     minListWidth : 70,
7974     /**
7975      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7976      * allow the user to set arbitrary text into the field (defaults to false)
7977      */
7978     forceSelection:false,
7979     /**
7980      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7981      * if typeAhead = true (defaults to 250)
7982      */
7983     typeAheadDelay : 250,
7984     /**
7985      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7986      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7987      */
7988     valueNotFoundText : undefined,
7989     /**
7990      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7991      */
7992     blockFocus : false,
7993     
7994     /**
7995      * @cfg {Boolean} disableClear Disable showing of clear button.
7996      */
7997     disableClear : false,
7998     /**
7999      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8000      */
8001     alwaysQuery : false,
8002     
8003     /**
8004      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8005      */
8006     multiple : false,
8007     
8008     //private
8009     addicon : false,
8010     editicon: false,
8011     
8012     page: 0,
8013     hasQuery: false,
8014     append: false,
8015     loadNext: false,
8016     item: [],
8017     
8018     // element that contains real text value.. (when hidden is used..)
8019      
8020     // private
8021     initEvents: function(){
8022         
8023         if (!this.store) {
8024             throw "can not find store for combo";
8025         }
8026         this.store = Roo.factory(this.store, Roo.data);
8027         
8028         
8029         
8030         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8031         
8032         
8033         if(this.hiddenName){
8034             
8035             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8036             
8037             this.hiddenField.dom.value =
8038                 this.hiddenValue !== undefined ? this.hiddenValue :
8039                 this.value !== undefined ? this.value : '';
8040
8041             // prevent input submission
8042             this.el.dom.removeAttribute('name');
8043             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8044              
8045              
8046         }
8047         //if(Roo.isGecko){
8048         //    this.el.dom.setAttribute('autocomplete', 'off');
8049         //}
8050
8051         var cls = 'x-combo-list';
8052         this.list = this.el.select('ul.dropdown-menu',true).first();
8053
8054         //this.list = new Roo.Layer({
8055         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8056         //});
8057         
8058         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8059         this.list.setWidth(lw);
8060         
8061         this.list.on('mouseover', this.onViewOver, this);
8062         this.list.on('mousemove', this.onViewMove, this);
8063         
8064         this.list.on('scroll', this.onViewScroll, this);
8065         
8066         /*
8067         this.list.swallowEvent('mousewheel');
8068         this.assetHeight = 0;
8069
8070         if(this.title){
8071             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8072             this.assetHeight += this.header.getHeight();
8073         }
8074
8075         this.innerList = this.list.createChild({cls:cls+'-inner'});
8076         this.innerList.on('mouseover', this.onViewOver, this);
8077         this.innerList.on('mousemove', this.onViewMove, this);
8078         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8079         
8080         if(this.allowBlank && !this.pageSize && !this.disableClear){
8081             this.footer = this.list.createChild({cls:cls+'-ft'});
8082             this.pageTb = new Roo.Toolbar(this.footer);
8083            
8084         }
8085         if(this.pageSize){
8086             this.footer = this.list.createChild({cls:cls+'-ft'});
8087             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8088                     {pageSize: this.pageSize});
8089             
8090         }
8091         
8092         if (this.pageTb && this.allowBlank && !this.disableClear) {
8093             var _this = this;
8094             this.pageTb.add(new Roo.Toolbar.Fill(), {
8095                 cls: 'x-btn-icon x-btn-clear',
8096                 text: '&#160;',
8097                 handler: function()
8098                 {
8099                     _this.collapse();
8100                     _this.clearValue();
8101                     _this.onSelect(false, -1);
8102                 }
8103             });
8104         }
8105         if (this.footer) {
8106             this.assetHeight += this.footer.getHeight();
8107         }
8108         */
8109             
8110         if(!this.tpl){
8111             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8112         }
8113
8114         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8115             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8116         });
8117         //this.view.wrapEl.setDisplayed(false);
8118         this.view.on('click', this.onViewClick, this);
8119         
8120         
8121         
8122         this.store.on('beforeload', this.onBeforeLoad, this);
8123         this.store.on('load', this.onLoad, this);
8124         this.store.on('loadexception', this.onLoadException, this);
8125         /*
8126         if(this.resizable){
8127             this.resizer = new Roo.Resizable(this.list,  {
8128                pinned:true, handles:'se'
8129             });
8130             this.resizer.on('resize', function(r, w, h){
8131                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8132                 this.listWidth = w;
8133                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8134                 this.restrictHeight();
8135             }, this);
8136             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8137         }
8138         */
8139         if(!this.editable){
8140             this.editable = true;
8141             this.setEditable(false);
8142         }
8143         
8144         /*
8145         
8146         if (typeof(this.events.add.listeners) != 'undefined') {
8147             
8148             this.addicon = this.wrap.createChild(
8149                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8150        
8151             this.addicon.on('click', function(e) {
8152                 this.fireEvent('add', this);
8153             }, this);
8154         }
8155         if (typeof(this.events.edit.listeners) != 'undefined') {
8156             
8157             this.editicon = this.wrap.createChild(
8158                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8159             if (this.addicon) {
8160                 this.editicon.setStyle('margin-left', '40px');
8161             }
8162             this.editicon.on('click', function(e) {
8163                 
8164                 // we fire even  if inothing is selected..
8165                 this.fireEvent('edit', this, this.lastData );
8166                 
8167             }, this);
8168         }
8169         */
8170         
8171         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8172             "up" : function(e){
8173                 this.inKeyMode = true;
8174                 this.selectPrev();
8175             },
8176
8177             "down" : function(e){
8178                 if(!this.isExpanded()){
8179                     this.onTriggerClick();
8180                 }else{
8181                     this.inKeyMode = true;
8182                     this.selectNext();
8183                 }
8184             },
8185
8186             "enter" : function(e){
8187                 this.onViewClick();
8188                 //return true;
8189             },
8190
8191             "esc" : function(e){
8192                 this.collapse();
8193             },
8194
8195             "tab" : function(e){
8196                 this.collapse();
8197                 
8198                 if(this.fireEvent("specialkey", this, e)){
8199                     this.onViewClick(false);
8200                 }
8201                 
8202                 return true;
8203             },
8204
8205             scope : this,
8206
8207             doRelay : function(foo, bar, hname){
8208                 if(hname == 'down' || this.scope.isExpanded()){
8209                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8210                 }
8211                 return true;
8212             },
8213
8214             forceKeyDown: true
8215         });
8216         
8217         
8218         this.queryDelay = Math.max(this.queryDelay || 10,
8219                 this.mode == 'local' ? 10 : 250);
8220         
8221         
8222         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8223         
8224         if(this.typeAhead){
8225             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8226         }
8227         if(this.editable !== false){
8228             this.inputEl().on("keyup", this.onKeyUp, this);
8229         }
8230         if(this.forceSelection){
8231             this.on('blur', this.doForce, this);
8232         }
8233         
8234         if(this.multiple){
8235             this.choices = this.el.select('ul.select2-choices', true).first();
8236             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8237         }
8238     },
8239
8240     onDestroy : function(){
8241         if(this.view){
8242             this.view.setStore(null);
8243             this.view.el.removeAllListeners();
8244             this.view.el.remove();
8245             this.view.purgeListeners();
8246         }
8247         if(this.list){
8248             this.list.dom.innerHTML  = '';
8249         }
8250         if(this.store){
8251             this.store.un('beforeload', this.onBeforeLoad, this);
8252             this.store.un('load', this.onLoad, this);
8253             this.store.un('loadexception', this.onLoadException, this);
8254         }
8255         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8256     },
8257
8258     // private
8259     fireKey : function(e){
8260         if(e.isNavKeyPress() && !this.list.isVisible()){
8261             this.fireEvent("specialkey", this, e);
8262         }
8263     },
8264
8265     // private
8266     onResize: function(w, h){
8267 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8268 //        
8269 //        if(typeof w != 'number'){
8270 //            // we do not handle it!?!?
8271 //            return;
8272 //        }
8273 //        var tw = this.trigger.getWidth();
8274 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8275 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8276 //        var x = w - tw;
8277 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8278 //            
8279 //        //this.trigger.setStyle('left', x+'px');
8280 //        
8281 //        if(this.list && this.listWidth === undefined){
8282 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8283 //            this.list.setWidth(lw);
8284 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8285 //        }
8286         
8287     
8288         
8289     },
8290
8291     /**
8292      * Allow or prevent the user from directly editing the field text.  If false is passed,
8293      * the user will only be able to select from the items defined in the dropdown list.  This method
8294      * is the runtime equivalent of setting the 'editable' config option at config time.
8295      * @param {Boolean} value True to allow the user to directly edit the field text
8296      */
8297     setEditable : function(value){
8298         if(value == this.editable){
8299             return;
8300         }
8301         this.editable = value;
8302         if(!value){
8303             this.inputEl().dom.setAttribute('readOnly', true);
8304             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8305             this.inputEl().addClass('x-combo-noedit');
8306         }else{
8307             this.inputEl().dom.setAttribute('readOnly', false);
8308             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8309             this.inputEl().removeClass('x-combo-noedit');
8310         }
8311     },
8312
8313     // private
8314     
8315     onBeforeLoad : function(combo,opts){
8316         if(!this.hasFocus){
8317             return;
8318         }
8319          if (!opts.add) {
8320             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8321          }
8322         this.restrictHeight();
8323         this.selectedIndex = -1;
8324     },
8325
8326     // private
8327     onLoad : function(){
8328         
8329         this.hasQuery = false;
8330         
8331         if(!this.hasFocus){
8332             return;
8333         }
8334         
8335         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8336             this.loading.hide();
8337         }
8338         
8339         if(this.store.getCount() > 0){
8340             this.expand();
8341             this.restrictHeight();
8342             if(this.lastQuery == this.allQuery){
8343                 if(this.editable){
8344                     this.inputEl().dom.select();
8345                 }
8346                 if(!this.selectByValue(this.value, true)){
8347                     this.select(0, true);
8348                 }
8349             }else{
8350                 this.selectNext();
8351                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8352                     this.taTask.delay(this.typeAheadDelay);
8353                 }
8354             }
8355         }else{
8356             this.onEmptyResults();
8357         }
8358         
8359         //this.el.focus();
8360     },
8361     // private
8362     onLoadException : function()
8363     {
8364         this.hasQuery = false;
8365         
8366         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8367             this.loading.hide();
8368         }
8369         
8370         this.collapse();
8371         Roo.log(this.store.reader.jsonData);
8372         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8373             // fixme
8374             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8375         }
8376         
8377         
8378     },
8379     // private
8380     onTypeAhead : function(){
8381         if(this.store.getCount() > 0){
8382             var r = this.store.getAt(0);
8383             var newValue = r.data[this.displayField];
8384             var len = newValue.length;
8385             var selStart = this.getRawValue().length;
8386             
8387             if(selStart != len){
8388                 this.setRawValue(newValue);
8389                 this.selectText(selStart, newValue.length);
8390             }
8391         }
8392     },
8393
8394     // private
8395     onSelect : function(record, index){
8396         
8397         if(this.fireEvent('beforeselect', this, record, index) !== false){
8398         
8399             this.setFromData(index > -1 ? record.data : false);
8400             
8401             this.collapse();
8402             this.fireEvent('select', this, record, index);
8403         }
8404     },
8405
8406     /**
8407      * Returns the currently selected field value or empty string if no value is set.
8408      * @return {String} value The selected value
8409      */
8410     getValue : function(){
8411         
8412         if(this.multiple){
8413             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8414         }
8415         
8416         if(this.valueField){
8417             return typeof this.value != 'undefined' ? this.value : '';
8418         }else{
8419             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8420         }
8421     },
8422
8423     /**
8424      * Clears any text/value currently set in the field
8425      */
8426     clearValue : function(){
8427         if(this.hiddenField){
8428             this.hiddenField.dom.value = '';
8429         }
8430         this.value = '';
8431         this.setRawValue('');
8432         this.lastSelectionText = '';
8433         
8434     },
8435
8436     /**
8437      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8438      * will be displayed in the field.  If the value does not match the data value of an existing item,
8439      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8440      * Otherwise the field will be blank (although the value will still be set).
8441      * @param {String} value The value to match
8442      */
8443     setValue : function(v){
8444         if(this.multiple){
8445             this.syncValue();
8446             return;
8447         }
8448         
8449         var text = v;
8450         if(this.valueField){
8451             var r = this.findRecord(this.valueField, v);
8452             if(r){
8453                 text = r.data[this.displayField];
8454             }else if(this.valueNotFoundText !== undefined){
8455                 text = this.valueNotFoundText;
8456             }
8457         }
8458         this.lastSelectionText = text;
8459         if(this.hiddenField){
8460             this.hiddenField.dom.value = v;
8461         }
8462         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8463         this.value = v;
8464     },
8465     /**
8466      * @property {Object} the last set data for the element
8467      */
8468     
8469     lastData : false,
8470     /**
8471      * Sets the value of the field based on a object which is related to the record format for the store.
8472      * @param {Object} value the value to set as. or false on reset?
8473      */
8474     setFromData : function(o){
8475         
8476         if(this.multiple){
8477             this.addItem(o);
8478             return;
8479         }
8480             
8481         var dv = ''; // display value
8482         var vv = ''; // value value..
8483         this.lastData = o;
8484         if (this.displayField) {
8485             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8486         } else {
8487             // this is an error condition!!!
8488             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8489         }
8490         
8491         if(this.valueField){
8492             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8493         }
8494         
8495         if(this.hiddenField){
8496             this.hiddenField.dom.value = vv;
8497             
8498             this.lastSelectionText = dv;
8499             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8500             this.value = vv;
8501             return;
8502         }
8503         // no hidden field.. - we store the value in 'value', but still display
8504         // display field!!!!
8505         this.lastSelectionText = dv;
8506         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8507         this.value = vv;
8508         
8509         
8510     },
8511     // private
8512     reset : function(){
8513         // overridden so that last data is reset..
8514         this.setValue(this.originalValue);
8515         this.clearInvalid();
8516         this.lastData = false;
8517         if (this.view) {
8518             this.view.clearSelections();
8519         }
8520     },
8521     // private
8522     findRecord : function(prop, value){
8523         var record;
8524         if(this.store.getCount() > 0){
8525             this.store.each(function(r){
8526                 if(r.data[prop] == value){
8527                     record = r;
8528                     return false;
8529                 }
8530                 return true;
8531             });
8532         }
8533         return record;
8534     },
8535     
8536     getName: function()
8537     {
8538         // returns hidden if it's set..
8539         if (!this.rendered) {return ''};
8540         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8541         
8542     },
8543     // private
8544     onViewMove : function(e, t){
8545         this.inKeyMode = false;
8546     },
8547
8548     // private
8549     onViewOver : function(e, t){
8550         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8551             return;
8552         }
8553         var item = this.view.findItemFromChild(t);
8554         if(item){
8555             var index = this.view.indexOf(item);
8556             this.select(index, false);
8557         }
8558     },
8559
8560     // private
8561     onViewClick : function(doFocus)
8562     {
8563         var index = this.view.getSelectedIndexes()[0];
8564         var r = this.store.getAt(index);
8565         if(r){
8566             this.onSelect(r, index);
8567         }
8568         if(doFocus !== false && !this.blockFocus){
8569             this.inputEl().focus();
8570         }
8571     },
8572
8573     // private
8574     restrictHeight : function(){
8575         //this.innerList.dom.style.height = '';
8576         //var inner = this.innerList.dom;
8577         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8578         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8579         //this.list.beginUpdate();
8580         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8581         this.list.alignTo(this.inputEl(), this.listAlign);
8582         //this.list.endUpdate();
8583     },
8584
8585     // private
8586     onEmptyResults : function(){
8587         this.collapse();
8588     },
8589
8590     /**
8591      * Returns true if the dropdown list is expanded, else false.
8592      */
8593     isExpanded : function(){
8594         return this.list.isVisible();
8595     },
8596
8597     /**
8598      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8599      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8600      * @param {String} value The data value of the item to select
8601      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8602      * selected item if it is not currently in view (defaults to true)
8603      * @return {Boolean} True if the value matched an item in the list, else false
8604      */
8605     selectByValue : function(v, scrollIntoView){
8606         if(v !== undefined && v !== null){
8607             var r = this.findRecord(this.valueField || this.displayField, v);
8608             if(r){
8609                 this.select(this.store.indexOf(r), scrollIntoView);
8610                 return true;
8611             }
8612         }
8613         return false;
8614     },
8615
8616     /**
8617      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8618      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8619      * @param {Number} index The zero-based index of the list item to select
8620      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8621      * selected item if it is not currently in view (defaults to true)
8622      */
8623     select : function(index, scrollIntoView){
8624         this.selectedIndex = index;
8625         this.view.select(index);
8626         if(scrollIntoView !== false){
8627             var el = this.view.getNode(index);
8628             if(el){
8629                 //this.innerList.scrollChildIntoView(el, false);
8630                 
8631             }
8632         }
8633     },
8634
8635     // private
8636     selectNext : function(){
8637         var ct = this.store.getCount();
8638         if(ct > 0){
8639             if(this.selectedIndex == -1){
8640                 this.select(0);
8641             }else if(this.selectedIndex < ct-1){
8642                 this.select(this.selectedIndex+1);
8643             }
8644         }
8645     },
8646
8647     // private
8648     selectPrev : function(){
8649         var ct = this.store.getCount();
8650         if(ct > 0){
8651             if(this.selectedIndex == -1){
8652                 this.select(0);
8653             }else if(this.selectedIndex != 0){
8654                 this.select(this.selectedIndex-1);
8655             }
8656         }
8657     },
8658
8659     // private
8660     onKeyUp : function(e){
8661         if(this.editable !== false && !e.isSpecialKey()){
8662             this.lastKey = e.getKey();
8663             this.dqTask.delay(this.queryDelay);
8664         }
8665     },
8666
8667     // private
8668     validateBlur : function(){
8669         return !this.list || !this.list.isVisible();   
8670     },
8671
8672     // private
8673     initQuery : function(){
8674         this.doQuery(this.getRawValue());
8675     },
8676
8677     // private
8678     doForce : function(){
8679         if(this.el.dom.value.length > 0){
8680             this.el.dom.value =
8681                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8682              
8683         }
8684     },
8685
8686     /**
8687      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8688      * query allowing the query action to be canceled if needed.
8689      * @param {String} query The SQL query to execute
8690      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8691      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8692      * saved in the current store (defaults to false)
8693      */
8694     doQuery : function(q, forceAll){
8695         
8696         if(q === undefined || q === null){
8697             q = '';
8698         }
8699         var qe = {
8700             query: q,
8701             forceAll: forceAll,
8702             combo: this,
8703             cancel:false
8704         };
8705         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8706             return false;
8707         }
8708         q = qe.query;
8709         
8710         forceAll = qe.forceAll;
8711         if(forceAll === true || (q.length >= this.minChars)){
8712             
8713             this.hasQuery = true;
8714             
8715             if(this.lastQuery != q || this.alwaysQuery){
8716                 this.lastQuery = q;
8717                 if(this.mode == 'local'){
8718                     this.selectedIndex = -1;
8719                     if(forceAll){
8720                         this.store.clearFilter();
8721                     }else{
8722                         this.store.filter(this.displayField, q);
8723                     }
8724                     this.onLoad();
8725                 }else{
8726                     this.store.baseParams[this.queryParam] = q;
8727                     
8728                     var options = {params : this.getParams(q)};
8729                     
8730                     if(this.loadNext){
8731                         options.add = true;
8732                         options.params.start = this.page * this.pageSize;
8733                     }
8734                     
8735                     this.store.load(options);
8736                     this.expand();
8737                 }
8738             }else{
8739                 this.selectedIndex = -1;
8740                 this.onLoad();   
8741             }
8742         }
8743         
8744         this.loadNext = false;
8745     },
8746
8747     // private
8748     getParams : function(q){
8749         var p = {};
8750         //p[this.queryParam] = q;
8751         
8752         if(this.pageSize){
8753             p.start = 0;
8754             p.limit = this.pageSize;
8755         }
8756         return p;
8757     },
8758
8759     /**
8760      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8761      */
8762     collapse : function(){
8763         if(!this.isExpanded()){
8764             return;
8765         }
8766         
8767         this.list.hide();
8768         Roo.get(document).un('mousedown', this.collapseIf, this);
8769         Roo.get(document).un('mousewheel', this.collapseIf, this);
8770         if (!this.editable) {
8771             Roo.get(document).un('keydown', this.listKeyPress, this);
8772         }
8773         this.fireEvent('collapse', this);
8774     },
8775
8776     // private
8777     collapseIf : function(e){
8778         var in_combo  = e.within(this.el);
8779         var in_list =  e.within(this.list);
8780         
8781         if (in_combo || in_list) {
8782             //e.stopPropagation();
8783             return;
8784         }
8785
8786         this.collapse();
8787         
8788     },
8789
8790     /**
8791      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8792      */
8793     expand : function(){
8794        
8795         if(this.isExpanded() || !this.hasFocus){
8796             return;
8797         }
8798          Roo.log('expand');
8799         this.list.alignTo(this.inputEl(), this.listAlign);
8800         this.list.show();
8801         Roo.get(document).on('mousedown', this.collapseIf, this);
8802         Roo.get(document).on('mousewheel', this.collapseIf, this);
8803         if (!this.editable) {
8804             Roo.get(document).on('keydown', this.listKeyPress, this);
8805         }
8806         
8807         this.fireEvent('expand', this);
8808     },
8809
8810     // private
8811     // Implements the default empty TriggerField.onTriggerClick function
8812     onTriggerClick : function()
8813     {
8814         Roo.log('trigger click');
8815         
8816         if(this.disabled){
8817             return;
8818         }
8819         
8820         this.page = 0;
8821         this.loadNext = false;
8822         
8823         if(this.isExpanded()){
8824             this.collapse();
8825             if (!this.blockFocus) {
8826                 this.inputEl().focus();
8827             }
8828             
8829         }else {
8830             this.hasFocus = true;
8831             if(this.triggerAction == 'all') {
8832                 this.doQuery(this.allQuery, true);
8833             } else {
8834                 this.doQuery(this.getRawValue());
8835             }
8836             if (!this.blockFocus) {
8837                 this.inputEl().focus();
8838             }
8839         }
8840     },
8841     listKeyPress : function(e)
8842     {
8843         //Roo.log('listkeypress');
8844         // scroll to first matching element based on key pres..
8845         if (e.isSpecialKey()) {
8846             return false;
8847         }
8848         var k = String.fromCharCode(e.getKey()).toUpperCase();
8849         //Roo.log(k);
8850         var match  = false;
8851         var csel = this.view.getSelectedNodes();
8852         var cselitem = false;
8853         if (csel.length) {
8854             var ix = this.view.indexOf(csel[0]);
8855             cselitem  = this.store.getAt(ix);
8856             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8857                 cselitem = false;
8858             }
8859             
8860         }
8861         
8862         this.store.each(function(v) { 
8863             if (cselitem) {
8864                 // start at existing selection.
8865                 if (cselitem.id == v.id) {
8866                     cselitem = false;
8867                 }
8868                 return true;
8869             }
8870                 
8871             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8872                 match = this.store.indexOf(v);
8873                 return false;
8874             }
8875             return true;
8876         }, this);
8877         
8878         if (match === false) {
8879             return true; // no more action?
8880         }
8881         // scroll to?
8882         this.view.select(match);
8883         var sn = Roo.get(this.view.getSelectedNodes()[0])
8884         //sn.scrollIntoView(sn.dom.parentNode, false);
8885     },
8886     
8887     onViewScroll : function(e, t){
8888         
8889         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8890             return;
8891         }
8892         
8893         this.hasQuery = true;
8894         
8895         this.loading = this.list.select('.loading', true).first();
8896         
8897         if(this.loading === null){
8898             this.list.createChild({
8899                 tag: 'div',
8900                 cls: 'loading select2-more-results select2-active',
8901                 html: 'Loading more results...'
8902             })
8903             
8904             this.loading = this.list.select('.loading', true).first();
8905             
8906             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8907             
8908             this.loading.hide();
8909         }
8910         
8911         this.loading.show();
8912         
8913         var _combo = this;
8914         
8915         this.page++;
8916         this.loadNext = true;
8917         
8918         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8919         
8920         return;
8921     },
8922     
8923     addItem : function(o)
8924     {   
8925         var dv = ''; // display value
8926         
8927         if (this.displayField) {
8928             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8929         } else {
8930             // this is an error condition!!!
8931             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8932         }
8933         
8934         if(!dv.length){
8935             return;
8936         }
8937         
8938         var choice = this.choices.createChild({
8939             tag: 'li',
8940             cls: 'select2-search-choice',
8941             cn: [
8942                 {
8943                     tag: 'div',
8944                     html: dv
8945                 },
8946                 {
8947                     tag: 'a',
8948                     href: '#',
8949                     cls: 'select2-search-choice-close',
8950                     tabindex: '-1'
8951                 }
8952             ]
8953             
8954         }, this.searchField);
8955         
8956         var close = choice.select('a.select2-search-choice-close', true).first()
8957         
8958         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8959         
8960         this.item.push(o);
8961         this.lastData = o;
8962         
8963         this.syncValue();
8964         
8965         this.inputEl().dom.value = '';
8966         
8967     },
8968     
8969     onRemoveItem : function(e, _self, o)
8970     {
8971         Roo.log('remove item');
8972         var index = this.item.indexOf(o.data) * 1;
8973         
8974         if( index < 0){
8975             Roo.log('not this item?!');
8976             return;
8977         }
8978         
8979         this.item.splice(index, 1);
8980         o.item.remove();
8981         
8982         this.syncValue();
8983         
8984         this.fireEvent('remove', this);
8985         
8986     },
8987     
8988     syncValue : function()
8989     {
8990         if(!this.item.length){
8991             this.clearValue();
8992             return;
8993         }
8994             
8995         var value = [];
8996         var _this = this;
8997         Roo.each(this.item, function(i){
8998             if(_this.valueField){
8999                 value.push(i[_this.valueField]);
9000                 return;
9001             }
9002
9003             value.push(i);
9004         });
9005
9006         this.value = value.join(',');
9007
9008         if(this.hiddenField){
9009             this.hiddenField.dom.value = this.value;
9010         }
9011     },
9012     
9013     clearItem : function()
9014     {
9015         if(!this.multiple){
9016             return;
9017         }
9018         
9019         this.item = [];
9020         
9021         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9022            c.remove();
9023         });
9024         
9025         this.syncValue();
9026     }
9027     
9028     
9029
9030     /** 
9031     * @cfg {Boolean} grow 
9032     * @hide 
9033     */
9034     /** 
9035     * @cfg {Number} growMin 
9036     * @hide 
9037     */
9038     /** 
9039     * @cfg {Number} growMax 
9040     * @hide 
9041     */
9042     /**
9043      * @hide
9044      * @method autoSize
9045      */
9046 });
9047 /*
9048  * Based on:
9049  * Ext JS Library 1.1.1
9050  * Copyright(c) 2006-2007, Ext JS, LLC.
9051  *
9052  * Originally Released Under LGPL - original licence link has changed is not relivant.
9053  *
9054  * Fork - LGPL
9055  * <script type="text/javascript">
9056  */
9057
9058 /**
9059  * @class Roo.View
9060  * @extends Roo.util.Observable
9061  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9062  * This class also supports single and multi selection modes. <br>
9063  * Create a data model bound view:
9064  <pre><code>
9065  var store = new Roo.data.Store(...);
9066
9067  var view = new Roo.View({
9068     el : "my-element",
9069     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9070  
9071     singleSelect: true,
9072     selectedClass: "ydataview-selected",
9073     store: store
9074  });
9075
9076  // listen for node click?
9077  view.on("click", function(vw, index, node, e){
9078  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9079  });
9080
9081  // load XML data
9082  dataModel.load("foobar.xml");
9083  </code></pre>
9084  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9085  * <br><br>
9086  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9087  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9088  * 
9089  * Note: old style constructor is still suported (container, template, config)
9090  * 
9091  * @constructor
9092  * Create a new View
9093  * @param {Object} config The config object
9094  * 
9095  */
9096 Roo.View = function(config, depreciated_tpl, depreciated_config){
9097     
9098     if (typeof(depreciated_tpl) == 'undefined') {
9099         // new way.. - universal constructor.
9100         Roo.apply(this, config);
9101         this.el  = Roo.get(this.el);
9102     } else {
9103         // old format..
9104         this.el  = Roo.get(config);
9105         this.tpl = depreciated_tpl;
9106         Roo.apply(this, depreciated_config);
9107     }
9108     this.wrapEl  = this.el.wrap().wrap();
9109     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9110     
9111     
9112     if(typeof(this.tpl) == "string"){
9113         this.tpl = new Roo.Template(this.tpl);
9114     } else {
9115         // support xtype ctors..
9116         this.tpl = new Roo.factory(this.tpl, Roo);
9117     }
9118     
9119     
9120     this.tpl.compile();
9121    
9122   
9123     
9124      
9125     /** @private */
9126     this.addEvents({
9127         /**
9128          * @event beforeclick
9129          * Fires before a click is processed. Returns false to cancel the default action.
9130          * @param {Roo.View} this
9131          * @param {Number} index The index of the target node
9132          * @param {HTMLElement} node The target node
9133          * @param {Roo.EventObject} e The raw event object
9134          */
9135             "beforeclick" : true,
9136         /**
9137          * @event click
9138          * Fires when a template node is clicked.
9139          * @param {Roo.View} this
9140          * @param {Number} index The index of the target node
9141          * @param {HTMLElement} node The target node
9142          * @param {Roo.EventObject} e The raw event object
9143          */
9144             "click" : true,
9145         /**
9146          * @event dblclick
9147          * Fires when a template node is double clicked.
9148          * @param {Roo.View} this
9149          * @param {Number} index The index of the target node
9150          * @param {HTMLElement} node The target node
9151          * @param {Roo.EventObject} e The raw event object
9152          */
9153             "dblclick" : true,
9154         /**
9155          * @event contextmenu
9156          * Fires when a template node is right clicked.
9157          * @param {Roo.View} this
9158          * @param {Number} index The index of the target node
9159          * @param {HTMLElement} node The target node
9160          * @param {Roo.EventObject} e The raw event object
9161          */
9162             "contextmenu" : true,
9163         /**
9164          * @event selectionchange
9165          * Fires when the selected nodes change.
9166          * @param {Roo.View} this
9167          * @param {Array} selections Array of the selected nodes
9168          */
9169             "selectionchange" : true,
9170     
9171         /**
9172          * @event beforeselect
9173          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9174          * @param {Roo.View} this
9175          * @param {HTMLElement} node The node to be selected
9176          * @param {Array} selections Array of currently selected nodes
9177          */
9178             "beforeselect" : true,
9179         /**
9180          * @event preparedata
9181          * Fires on every row to render, to allow you to change the data.
9182          * @param {Roo.View} this
9183          * @param {Object} data to be rendered (change this)
9184          */
9185           "preparedata" : true
9186           
9187           
9188         });
9189
9190
9191
9192     this.el.on({
9193         "click": this.onClick,
9194         "dblclick": this.onDblClick,
9195         "contextmenu": this.onContextMenu,
9196         scope:this
9197     });
9198
9199     this.selections = [];
9200     this.nodes = [];
9201     this.cmp = new Roo.CompositeElementLite([]);
9202     if(this.store){
9203         this.store = Roo.factory(this.store, Roo.data);
9204         this.setStore(this.store, true);
9205     }
9206     
9207     if ( this.footer && this.footer.xtype) {
9208            
9209          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9210         
9211         this.footer.dataSource = this.store
9212         this.footer.container = fctr;
9213         this.footer = Roo.factory(this.footer, Roo);
9214         fctr.insertFirst(this.el);
9215         
9216         // this is a bit insane - as the paging toolbar seems to detach the el..
9217 //        dom.parentNode.parentNode.parentNode
9218          // they get detached?
9219     }
9220     
9221     
9222     Roo.View.superclass.constructor.call(this);
9223     
9224     
9225 };
9226
9227 Roo.extend(Roo.View, Roo.util.Observable, {
9228     
9229      /**
9230      * @cfg {Roo.data.Store} store Data store to load data from.
9231      */
9232     store : false,
9233     
9234     /**
9235      * @cfg {String|Roo.Element} el The container element.
9236      */
9237     el : '',
9238     
9239     /**
9240      * @cfg {String|Roo.Template} tpl The template used by this View 
9241      */
9242     tpl : false,
9243     /**
9244      * @cfg {String} dataName the named area of the template to use as the data area
9245      *                          Works with domtemplates roo-name="name"
9246      */
9247     dataName: false,
9248     /**
9249      * @cfg {String} selectedClass The css class to add to selected nodes
9250      */
9251     selectedClass : "x-view-selected",
9252      /**
9253      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9254      */
9255     emptyText : "",
9256     
9257     /**
9258      * @cfg {String} text to display on mask (default Loading)
9259      */
9260     mask : false,
9261     /**
9262      * @cfg {Boolean} multiSelect Allow multiple selection
9263      */
9264     multiSelect : false,
9265     /**
9266      * @cfg {Boolean} singleSelect Allow single selection
9267      */
9268     singleSelect:  false,
9269     
9270     /**
9271      * @cfg {Boolean} toggleSelect - selecting 
9272      */
9273     toggleSelect : false,
9274     
9275     /**
9276      * Returns the element this view is bound to.
9277      * @return {Roo.Element}
9278      */
9279     getEl : function(){
9280         return this.wrapEl;
9281     },
9282     
9283     
9284
9285     /**
9286      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9287      */
9288     refresh : function(){
9289         Roo.log('refresh');
9290         var t = this.tpl;
9291         
9292         // if we are using something like 'domtemplate', then
9293         // the what gets used is:
9294         // t.applySubtemplate(NAME, data, wrapping data..)
9295         // the outer template then get' applied with
9296         //     the store 'extra data'
9297         // and the body get's added to the
9298         //      roo-name="data" node?
9299         //      <span class='roo-tpl-{name}'></span> ?????
9300         
9301         
9302         
9303         this.clearSelections();
9304         this.el.update("");
9305         var html = [];
9306         var records = this.store.getRange();
9307         if(records.length < 1) {
9308             
9309             // is this valid??  = should it render a template??
9310             
9311             this.el.update(this.emptyText);
9312             return;
9313         }
9314         var el = this.el;
9315         if (this.dataName) {
9316             this.el.update(t.apply(this.store.meta)); //????
9317             el = this.el.child('.roo-tpl-' + this.dataName);
9318         }
9319         
9320         for(var i = 0, len = records.length; i < len; i++){
9321             var data = this.prepareData(records[i].data, i, records[i]);
9322             this.fireEvent("preparedata", this, data, i, records[i]);
9323             html[html.length] = Roo.util.Format.trim(
9324                 this.dataName ?
9325                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9326                     t.apply(data)
9327             );
9328         }
9329         
9330         
9331         
9332         el.update(html.join(""));
9333         this.nodes = el.dom.childNodes;
9334         this.updateIndexes(0);
9335     },
9336     
9337
9338     /**
9339      * Function to override to reformat the data that is sent to
9340      * the template for each node.
9341      * DEPRICATED - use the preparedata event handler.
9342      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9343      * a JSON object for an UpdateManager bound view).
9344      */
9345     prepareData : function(data, index, record)
9346     {
9347         this.fireEvent("preparedata", this, data, index, record);
9348         return data;
9349     },
9350
9351     onUpdate : function(ds, record){
9352          Roo.log('on update');   
9353         this.clearSelections();
9354         var index = this.store.indexOf(record);
9355         var n = this.nodes[index];
9356         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9357         n.parentNode.removeChild(n);
9358         this.updateIndexes(index, index);
9359     },
9360
9361     
9362     
9363 // --------- FIXME     
9364     onAdd : function(ds, records, index)
9365     {
9366         Roo.log(['on Add', ds, records, index] );        
9367         this.clearSelections();
9368         if(this.nodes.length == 0){
9369             this.refresh();
9370             return;
9371         }
9372         var n = this.nodes[index];
9373         for(var i = 0, len = records.length; i < len; i++){
9374             var d = this.prepareData(records[i].data, i, records[i]);
9375             if(n){
9376                 this.tpl.insertBefore(n, d);
9377             }else{
9378                 
9379                 this.tpl.append(this.el, d);
9380             }
9381         }
9382         this.updateIndexes(index);
9383     },
9384
9385     onRemove : function(ds, record, index){
9386         Roo.log('onRemove');
9387         this.clearSelections();
9388         var el = this.dataName  ?
9389             this.el.child('.roo-tpl-' + this.dataName) :
9390             this.el; 
9391         
9392         el.dom.removeChild(this.nodes[index]);
9393         this.updateIndexes(index);
9394     },
9395
9396     /**
9397      * Refresh an individual node.
9398      * @param {Number} index
9399      */
9400     refreshNode : function(index){
9401         this.onUpdate(this.store, this.store.getAt(index));
9402     },
9403
9404     updateIndexes : function(startIndex, endIndex){
9405         var ns = this.nodes;
9406         startIndex = startIndex || 0;
9407         endIndex = endIndex || ns.length - 1;
9408         for(var i = startIndex; i <= endIndex; i++){
9409             ns[i].nodeIndex = i;
9410         }
9411     },
9412
9413     /**
9414      * Changes the data store this view uses and refresh the view.
9415      * @param {Store} store
9416      */
9417     setStore : function(store, initial){
9418         if(!initial && this.store){
9419             this.store.un("datachanged", this.refresh);
9420             this.store.un("add", this.onAdd);
9421             this.store.un("remove", this.onRemove);
9422             this.store.un("update", this.onUpdate);
9423             this.store.un("clear", this.refresh);
9424             this.store.un("beforeload", this.onBeforeLoad);
9425             this.store.un("load", this.onLoad);
9426             this.store.un("loadexception", this.onLoad);
9427         }
9428         if(store){
9429           
9430             store.on("datachanged", this.refresh, this);
9431             store.on("add", this.onAdd, this);
9432             store.on("remove", this.onRemove, this);
9433             store.on("update", this.onUpdate, this);
9434             store.on("clear", this.refresh, this);
9435             store.on("beforeload", this.onBeforeLoad, this);
9436             store.on("load", this.onLoad, this);
9437             store.on("loadexception", this.onLoad, this);
9438         }
9439         
9440         if(store){
9441             this.refresh();
9442         }
9443     },
9444     /**
9445      * onbeforeLoad - masks the loading area.
9446      *
9447      */
9448     onBeforeLoad : function(store,opts)
9449     {
9450          Roo.log('onBeforeLoad');   
9451         if (!opts.add) {
9452             this.el.update("");
9453         }
9454         this.el.mask(this.mask ? this.mask : "Loading" ); 
9455     },
9456     onLoad : function ()
9457     {
9458         this.el.unmask();
9459     },
9460     
9461
9462     /**
9463      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9464      * @param {HTMLElement} node
9465      * @return {HTMLElement} The template node
9466      */
9467     findItemFromChild : function(node){
9468         var el = this.dataName  ?
9469             this.el.child('.roo-tpl-' + this.dataName,true) :
9470             this.el.dom; 
9471         
9472         if(!node || node.parentNode == el){
9473                     return node;
9474             }
9475             var p = node.parentNode;
9476             while(p && p != el){
9477             if(p.parentNode == el){
9478                 return p;
9479             }
9480             p = p.parentNode;
9481         }
9482             return null;
9483     },
9484
9485     /** @ignore */
9486     onClick : function(e){
9487         var item = this.findItemFromChild(e.getTarget());
9488         if(item){
9489             var index = this.indexOf(item);
9490             if(this.onItemClick(item, index, e) !== false){
9491                 this.fireEvent("click", this, index, item, e);
9492             }
9493         }else{
9494             this.clearSelections();
9495         }
9496     },
9497
9498     /** @ignore */
9499     onContextMenu : function(e){
9500         var item = this.findItemFromChild(e.getTarget());
9501         if(item){
9502             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9503         }
9504     },
9505
9506     /** @ignore */
9507     onDblClick : function(e){
9508         var item = this.findItemFromChild(e.getTarget());
9509         if(item){
9510             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9511         }
9512     },
9513
9514     onItemClick : function(item, index, e)
9515     {
9516         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9517             return false;
9518         }
9519         if (this.toggleSelect) {
9520             var m = this.isSelected(item) ? 'unselect' : 'select';
9521             Roo.log(m);
9522             var _t = this;
9523             _t[m](item, true, false);
9524             return true;
9525         }
9526         if(this.multiSelect || this.singleSelect){
9527             if(this.multiSelect && e.shiftKey && this.lastSelection){
9528                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9529             }else{
9530                 this.select(item, this.multiSelect && e.ctrlKey);
9531                 this.lastSelection = item;
9532             }
9533             e.preventDefault();
9534         }
9535         return true;
9536     },
9537
9538     /**
9539      * Get the number of selected nodes.
9540      * @return {Number}
9541      */
9542     getSelectionCount : function(){
9543         return this.selections.length;
9544     },
9545
9546     /**
9547      * Get the currently selected nodes.
9548      * @return {Array} An array of HTMLElements
9549      */
9550     getSelectedNodes : function(){
9551         return this.selections;
9552     },
9553
9554     /**
9555      * Get the indexes of the selected nodes.
9556      * @return {Array}
9557      */
9558     getSelectedIndexes : function(){
9559         var indexes = [], s = this.selections;
9560         for(var i = 0, len = s.length; i < len; i++){
9561             indexes.push(s[i].nodeIndex);
9562         }
9563         return indexes;
9564     },
9565
9566     /**
9567      * Clear all selections
9568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9569      */
9570     clearSelections : function(suppressEvent){
9571         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9572             this.cmp.elements = this.selections;
9573             this.cmp.removeClass(this.selectedClass);
9574             this.selections = [];
9575             if(!suppressEvent){
9576                 this.fireEvent("selectionchange", this, this.selections);
9577             }
9578         }
9579     },
9580
9581     /**
9582      * Returns true if the passed node is selected
9583      * @param {HTMLElement/Number} node The node or node index
9584      * @return {Boolean}
9585      */
9586     isSelected : function(node){
9587         var s = this.selections;
9588         if(s.length < 1){
9589             return false;
9590         }
9591         node = this.getNode(node);
9592         return s.indexOf(node) !== -1;
9593     },
9594
9595     /**
9596      * Selects nodes.
9597      * @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
9598      * @param {Boolean} keepExisting (optional) true to keep existing selections
9599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9600      */
9601     select : function(nodeInfo, keepExisting, suppressEvent){
9602         if(nodeInfo instanceof Array){
9603             if(!keepExisting){
9604                 this.clearSelections(true);
9605             }
9606             for(var i = 0, len = nodeInfo.length; i < len; i++){
9607                 this.select(nodeInfo[i], true, true);
9608             }
9609             return;
9610         } 
9611         var node = this.getNode(nodeInfo);
9612         if(!node || this.isSelected(node)){
9613             return; // already selected.
9614         }
9615         if(!keepExisting){
9616             this.clearSelections(true);
9617         }
9618         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9619             Roo.fly(node).addClass(this.selectedClass);
9620             this.selections.push(node);
9621             if(!suppressEvent){
9622                 this.fireEvent("selectionchange", this, this.selections);
9623             }
9624         }
9625         
9626         
9627     },
9628       /**
9629      * Unselects nodes.
9630      * @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
9631      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9632      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9633      */
9634     unselect : function(nodeInfo, keepExisting, suppressEvent)
9635     {
9636         if(nodeInfo instanceof Array){
9637             Roo.each(this.selections, function(s) {
9638                 this.unselect(s, nodeInfo);
9639             }, this);
9640             return;
9641         }
9642         var node = this.getNode(nodeInfo);
9643         if(!node || !this.isSelected(node)){
9644             Roo.log("not selected");
9645             return; // not selected.
9646         }
9647         // fireevent???
9648         var ns = [];
9649         Roo.each(this.selections, function(s) {
9650             if (s == node ) {
9651                 Roo.fly(node).removeClass(this.selectedClass);
9652
9653                 return;
9654             }
9655             ns.push(s);
9656         },this);
9657         
9658         this.selections= ns;
9659         this.fireEvent("selectionchange", this, this.selections);
9660     },
9661
9662     /**
9663      * Gets a template node.
9664      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9665      * @return {HTMLElement} The node or null if it wasn't found
9666      */
9667     getNode : function(nodeInfo){
9668         if(typeof nodeInfo == "string"){
9669             return document.getElementById(nodeInfo);
9670         }else if(typeof nodeInfo == "number"){
9671             return this.nodes[nodeInfo];
9672         }
9673         return nodeInfo;
9674     },
9675
9676     /**
9677      * Gets a range template nodes.
9678      * @param {Number} startIndex
9679      * @param {Number} endIndex
9680      * @return {Array} An array of nodes
9681      */
9682     getNodes : function(start, end){
9683         var ns = this.nodes;
9684         start = start || 0;
9685         end = typeof end == "undefined" ? ns.length - 1 : end;
9686         var nodes = [];
9687         if(start <= end){
9688             for(var i = start; i <= end; i++){
9689                 nodes.push(ns[i]);
9690             }
9691         } else{
9692             for(var i = start; i >= end; i--){
9693                 nodes.push(ns[i]);
9694             }
9695         }
9696         return nodes;
9697     },
9698
9699     /**
9700      * Finds the index of the passed node
9701      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9702      * @return {Number} The index of the node or -1
9703      */
9704     indexOf : function(node){
9705         node = this.getNode(node);
9706         if(typeof node.nodeIndex == "number"){
9707             return node.nodeIndex;
9708         }
9709         var ns = this.nodes;
9710         for(var i = 0, len = ns.length; i < len; i++){
9711             if(ns[i] == node){
9712                 return i;
9713             }
9714         }
9715         return -1;
9716     }
9717 });
9718 /*
9719  * - LGPL
9720  *
9721  * based on jquery fullcalendar
9722  * 
9723  */
9724
9725 Roo.bootstrap = Roo.bootstrap || {};
9726 /**
9727  * @class Roo.bootstrap.Calendar
9728  * @extends Roo.bootstrap.Component
9729  * Bootstrap Calendar class
9730  * @cfg {Boolean} loadMask (true|false) default false
9731     
9732  * @constructor
9733  * Create a new Container
9734  * @param {Object} config The config object
9735  */
9736
9737
9738
9739 Roo.bootstrap.Calendar = function(config){
9740     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9741      this.addEvents({
9742         /**
9743              * @event select
9744              * Fires when a date is selected
9745              * @param {DatePicker} this
9746              * @param {Date} date The selected date
9747              */
9748         'select': true,
9749         /**
9750              * @event monthchange
9751              * Fires when the displayed month changes 
9752              * @param {DatePicker} this
9753              * @param {Date} date The selected month
9754              */
9755         'monthchange': true,
9756         /**
9757              * @event evententer
9758              * Fires when mouse over an event
9759              * @param {Calendar} this
9760              * @param {event} Event
9761              */
9762         'evententer': true,
9763         /**
9764              * @event eventleave
9765              * Fires when the mouse leaves an
9766              * @param {Calendar} this
9767              * @param {event}
9768              */
9769         'eventleave': true,
9770         /**
9771              * @event eventclick
9772              * Fires when the mouse click an
9773              * @param {Calendar} this
9774              * @param {event}
9775              */
9776         'eventclick': true
9777         
9778     });
9779
9780 };
9781
9782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9783     
9784      /**
9785      * @cfg {Number} startDay
9786      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9787      */
9788     startDay : 0,
9789     
9790     loadMask : false,
9791       
9792     getAutoCreate : function(){
9793         
9794         
9795         var fc_button = function(name, corner, style, content ) {
9796             return Roo.apply({},{
9797                 tag : 'span',
9798                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9799                          (corner.length ?
9800                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9801                             ''
9802                         ),
9803                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9804                 unselectable: 'on'
9805             });
9806         };
9807         
9808         var header = {
9809             tag : 'table',
9810             cls : 'fc-header',
9811             style : 'width:100%',
9812             cn : [
9813                 {
9814                     tag: 'tr',
9815                     cn : [
9816                         {
9817                             tag : 'td',
9818                             cls : 'fc-header-left',
9819                             cn : [
9820                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9821                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9822                                 { tag: 'span', cls: 'fc-header-space' },
9823                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9824                                 
9825                                 
9826                             ]
9827                         },
9828                         
9829                         {
9830                             tag : 'td',
9831                             cls : 'fc-header-center',
9832                             cn : [
9833                                 {
9834                                     tag: 'span',
9835                                     cls: 'fc-header-title',
9836                                     cn : {
9837                                         tag: 'H2',
9838                                         html : 'month / year'
9839                                     }
9840                                 }
9841                                 
9842                             ]
9843                         },
9844                         {
9845                             tag : 'td',
9846                             cls : 'fc-header-right',
9847                             cn : [
9848                           /*      fc_button('month', 'left', '', 'month' ),
9849                                 fc_button('week', '', '', 'week' ),
9850                                 fc_button('day', 'right', '', 'day' )
9851                             */    
9852                                 
9853                             ]
9854                         }
9855                         
9856                     ]
9857                 }
9858             ]
9859         };
9860         
9861        
9862         var cal_heads = function() {
9863             var ret = [];
9864             // fixme - handle this.
9865             
9866             for (var i =0; i < Date.dayNames.length; i++) {
9867                 var d = Date.dayNames[i];
9868                 ret.push({
9869                     tag: 'th',
9870                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9871                     html : d.substring(0,3)
9872                 });
9873                 
9874             }
9875             ret[0].cls += ' fc-first';
9876             ret[6].cls += ' fc-last';
9877             return ret;
9878         };
9879         var cal_cell = function(n) {
9880             return  {
9881                 tag: 'td',
9882                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9883                 cn : [
9884                     {
9885                         cn : [
9886                             {
9887                                 cls: 'fc-day-number',
9888                                 html: 'D'
9889                             },
9890                             {
9891                                 cls: 'fc-day-content',
9892                              
9893                                 cn : [
9894                                      {
9895                                         style: 'position: relative;' // height: 17px;
9896                                     }
9897                                 ]
9898                             }
9899                             
9900                             
9901                         ]
9902                     }
9903                 ]
9904                 
9905             }
9906         };
9907         var cal_rows = function() {
9908             
9909             var ret = []
9910             for (var r = 0; r < 6; r++) {
9911                 var row= {
9912                     tag : 'tr',
9913                     cls : 'fc-week',
9914                     cn : []
9915                 };
9916                 
9917                 for (var i =0; i < Date.dayNames.length; i++) {
9918                     var d = Date.dayNames[i];
9919                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9920
9921                 }
9922                 row.cn[0].cls+=' fc-first';
9923                 row.cn[0].cn[0].style = 'min-height:90px';
9924                 row.cn[6].cls+=' fc-last';
9925                 ret.push(row);
9926                 
9927             }
9928             ret[0].cls += ' fc-first';
9929             ret[4].cls += ' fc-prev-last';
9930             ret[5].cls += ' fc-last';
9931             return ret;
9932             
9933         };
9934         
9935         var cal_table = {
9936             tag: 'table',
9937             cls: 'fc-border-separate',
9938             style : 'width:100%',
9939             cellspacing  : 0,
9940             cn : [
9941                 { 
9942                     tag: 'thead',
9943                     cn : [
9944                         { 
9945                             tag: 'tr',
9946                             cls : 'fc-first fc-last',
9947                             cn : cal_heads()
9948                         }
9949                     ]
9950                 },
9951                 { 
9952                     tag: 'tbody',
9953                     cn : cal_rows()
9954                 }
9955                   
9956             ]
9957         };
9958          
9959          var cfg = {
9960             cls : 'fc fc-ltr',
9961             cn : [
9962                 header,
9963                 {
9964                     cls : 'fc-content',
9965                     style : "position: relative;",
9966                     cn : [
9967                         {
9968                             cls : 'fc-view fc-view-month fc-grid',
9969                             style : 'position: relative',
9970                             unselectable : 'on',
9971                             cn : [
9972                                 {
9973                                     cls : 'fc-event-container',
9974                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9975                                 },
9976                                 cal_table
9977                             ]
9978                         }
9979                     ]
9980     
9981                 }
9982            ] 
9983             
9984         };
9985         
9986          
9987         
9988         return cfg;
9989     },
9990     
9991     
9992     initEvents : function()
9993     {
9994         if(!this.store){
9995             throw "can not find store for calendar";
9996         }
9997         
9998         var mark = {
9999             tag: "div",
10000             cls:"x-dlg-mask",
10001             style: "text-align:center",
10002             cn: [
10003                 {
10004                     tag: "div",
10005                     style: "background-color:white;width:50%;margin:250 auto",
10006                     cn: [
10007                         {
10008                             tag: "img",
10009                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10010                         },
10011                         {
10012                             tag: "span",
10013                             html: "Loading"
10014                         }
10015                         
10016                     ]
10017                 }
10018             ]
10019         }
10020         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10021         
10022         var size = this.el.select('.fc-content', true).first().getSize();
10023         this.maskEl.setSize(size.width, size.height);
10024         this.maskEl.enableDisplayMode("block");
10025         if(!this.loadMask){
10026             this.maskEl.hide();
10027         }
10028         
10029         this.store = Roo.factory(this.store, Roo.data);
10030         this.store.on('load', this.onLoad, this);
10031         this.store.on('beforeload', this.onBeforeLoad, this);
10032         
10033         this.resize();
10034         
10035         this.cells = this.el.select('.fc-day',true);
10036         //Roo.log(this.cells);
10037         this.textNodes = this.el.query('.fc-day-number');
10038         this.cells.addClassOnOver('fc-state-hover');
10039         
10040         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10041         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10042         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10043         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10044         
10045         this.on('monthchange', this.onMonthChange, this);
10046         
10047         this.update(new Date().clearTime());
10048     },
10049     
10050     resize : function() {
10051         var sz  = this.el.getSize();
10052         
10053         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10054         this.el.select('.fc-day-content div',true).setHeight(34);
10055     },
10056     
10057     
10058     // private
10059     showPrevMonth : function(e){
10060         this.update(this.activeDate.add("mo", -1));
10061     },
10062     showToday : function(e){
10063         this.update(new Date().clearTime());
10064     },
10065     // private
10066     showNextMonth : function(e){
10067         this.update(this.activeDate.add("mo", 1));
10068     },
10069
10070     // private
10071     showPrevYear : function(){
10072         this.update(this.activeDate.add("y", -1));
10073     },
10074
10075     // private
10076     showNextYear : function(){
10077         this.update(this.activeDate.add("y", 1));
10078     },
10079
10080     
10081    // private
10082     update : function(date)
10083     {
10084         var vd = this.activeDate;
10085         this.activeDate = date;
10086 //        if(vd && this.el){
10087 //            var t = date.getTime();
10088 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10089 //                Roo.log('using add remove');
10090 //                
10091 //                this.fireEvent('monthchange', this, date);
10092 //                
10093 //                this.cells.removeClass("fc-state-highlight");
10094 //                this.cells.each(function(c){
10095 //                   if(c.dateValue == t){
10096 //                       c.addClass("fc-state-highlight");
10097 //                       setTimeout(function(){
10098 //                            try{c.dom.firstChild.focus();}catch(e){}
10099 //                       }, 50);
10100 //                       return false;
10101 //                   }
10102 //                   return true;
10103 //                });
10104 //                return;
10105 //            }
10106 //        }
10107         
10108         var days = date.getDaysInMonth();
10109         
10110         var firstOfMonth = date.getFirstDateOfMonth();
10111         var startingPos = firstOfMonth.getDay()-this.startDay;
10112         
10113         if(startingPos < this.startDay){
10114             startingPos += 7;
10115         }
10116         
10117         var pm = date.add(Date.MONTH, -1);
10118         var prevStart = pm.getDaysInMonth()-startingPos;
10119 //        
10120         this.cells = this.el.select('.fc-day',true);
10121         this.textNodes = this.el.query('.fc-day-number');
10122         this.cells.addClassOnOver('fc-state-hover');
10123         
10124         var cells = this.cells.elements;
10125         var textEls = this.textNodes;
10126         
10127         Roo.each(cells, function(cell){
10128             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10129         });
10130         
10131         days += startingPos;
10132
10133         // convert everything to numbers so it's fast
10134         var day = 86400000;
10135         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10136         //Roo.log(d);
10137         //Roo.log(pm);
10138         //Roo.log(prevStart);
10139         
10140         var today = new Date().clearTime().getTime();
10141         var sel = date.clearTime().getTime();
10142         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10143         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10144         var ddMatch = this.disabledDatesRE;
10145         var ddText = this.disabledDatesText;
10146         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10147         var ddaysText = this.disabledDaysText;
10148         var format = this.format;
10149         
10150         var setCellClass = function(cal, cell){
10151             
10152             //Roo.log('set Cell Class');
10153             cell.title = "";
10154             var t = d.getTime();
10155             
10156             //Roo.log(d);
10157             
10158             cell.dateValue = t;
10159             if(t == today){
10160                 cell.className += " fc-today";
10161                 cell.className += " fc-state-highlight";
10162                 cell.title = cal.todayText;
10163             }
10164             if(t == sel){
10165                 // disable highlight in other month..
10166                 //cell.className += " fc-state-highlight";
10167                 
10168             }
10169             // disabling
10170             if(t < min) {
10171                 cell.className = " fc-state-disabled";
10172                 cell.title = cal.minText;
10173                 return;
10174             }
10175             if(t > max) {
10176                 cell.className = " fc-state-disabled";
10177                 cell.title = cal.maxText;
10178                 return;
10179             }
10180             if(ddays){
10181                 if(ddays.indexOf(d.getDay()) != -1){
10182                     cell.title = ddaysText;
10183                     cell.className = " fc-state-disabled";
10184                 }
10185             }
10186             if(ddMatch && format){
10187                 var fvalue = d.dateFormat(format);
10188                 if(ddMatch.test(fvalue)){
10189                     cell.title = ddText.replace("%0", fvalue);
10190                     cell.className = " fc-state-disabled";
10191                 }
10192             }
10193             
10194             if (!cell.initialClassName) {
10195                 cell.initialClassName = cell.dom.className;
10196             }
10197             
10198             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10199         };
10200
10201         var i = 0;
10202         
10203         for(; i < startingPos; i++) {
10204             textEls[i].innerHTML = (++prevStart);
10205             d.setDate(d.getDate()+1);
10206             
10207             cells[i].className = "fc-past fc-other-month";
10208             setCellClass(this, cells[i]);
10209         }
10210         
10211         var intDay = 0;
10212         
10213         for(; i < days; i++){
10214             intDay = i - startingPos + 1;
10215             textEls[i].innerHTML = (intDay);
10216             d.setDate(d.getDate()+1);
10217             
10218             cells[i].className = ''; // "x-date-active";
10219             setCellClass(this, cells[i]);
10220         }
10221         var extraDays = 0;
10222         
10223         for(; i < 42; i++) {
10224             textEls[i].innerHTML = (++extraDays);
10225             d.setDate(d.getDate()+1);
10226             
10227             cells[i].className = "fc-future fc-other-month";
10228             setCellClass(this, cells[i]);
10229         }
10230         
10231         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10232         
10233         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10234         
10235         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10236         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10237         
10238         if(totalRows != 6){
10239             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10240             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10241         }
10242         
10243         this.fireEvent('monthchange', this, date);
10244         
10245         
10246         /*
10247         if(!this.internalRender){
10248             var main = this.el.dom.firstChild;
10249             var w = main.offsetWidth;
10250             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10251             Roo.fly(main).setWidth(w);
10252             this.internalRender = true;
10253             // opera does not respect the auto grow header center column
10254             // then, after it gets a width opera refuses to recalculate
10255             // without a second pass
10256             if(Roo.isOpera && !this.secondPass){
10257                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10258                 this.secondPass = true;
10259                 this.update.defer(10, this, [date]);
10260             }
10261         }
10262         */
10263         
10264     },
10265     
10266     findCell : function(dt) {
10267         dt = dt.clearTime().getTime();
10268         var ret = false;
10269         this.cells.each(function(c){
10270             //Roo.log("check " +c.dateValue + '?=' + dt);
10271             if(c.dateValue == dt){
10272                 ret = c;
10273                 return false;
10274             }
10275             return true;
10276         });
10277         
10278         return ret;
10279     },
10280     
10281     findCells : function(ev) {
10282         var s = ev.start.clone().clearTime().getTime();
10283        // Roo.log(s);
10284         var e= ev.end.clone().clearTime().getTime();
10285        // Roo.log(e);
10286         var ret = [];
10287         this.cells.each(function(c){
10288              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10289             
10290             if(c.dateValue > e){
10291                 return ;
10292             }
10293             if(c.dateValue < s){
10294                 return ;
10295             }
10296             ret.push(c);
10297         });
10298         
10299         return ret;    
10300     },
10301     
10302     findBestRow: function(cells)
10303     {
10304         var ret = 0;
10305         
10306         for (var i =0 ; i < cells.length;i++) {
10307             ret  = Math.max(cells[i].rows || 0,ret);
10308         }
10309         return ret;
10310         
10311     },
10312     
10313     
10314     addItem : function(ev)
10315     {
10316         // look for vertical location slot in
10317         var cells = this.findCells(ev);
10318         
10319         ev.row = this.findBestRow(cells);
10320         
10321         // work out the location.
10322         
10323         var crow = false;
10324         var rows = [];
10325         for(var i =0; i < cells.length; i++) {
10326             if (!crow) {
10327                 crow = {
10328                     start : cells[i],
10329                     end :  cells[i]
10330                 };
10331                 continue;
10332             }
10333             if (crow.start.getY() == cells[i].getY()) {
10334                 // on same row.
10335                 crow.end = cells[i];
10336                 continue;
10337             }
10338             // different row.
10339             rows.push(crow);
10340             crow = {
10341                 start: cells[i],
10342                 end : cells[i]
10343             };
10344             
10345         }
10346         
10347         rows.push(crow);
10348         ev.els = [];
10349         ev.rows = rows;
10350         ev.cells = cells;
10351         for (var i = 0; i < cells.length;i++) {
10352             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10353             
10354         }
10355         
10356         this.calevents.push(ev);
10357     },
10358     
10359     clearEvents: function() {
10360         
10361         if(!this.calevents){
10362             return;
10363         }
10364         
10365         Roo.each(this.cells.elements, function(c){
10366             c.rows = 0;
10367         });
10368         
10369         Roo.each(this.calevents, function(e) {
10370             Roo.each(e.els, function(el) {
10371                 el.un('mouseenter' ,this.onEventEnter, this);
10372                 el.un('mouseleave' ,this.onEventLeave, this);
10373                 el.remove();
10374             },this);
10375         },this);
10376         
10377     },
10378     
10379     renderEvents: function()
10380     {   
10381         // first make sure there is enough space..
10382         
10383         this.cells.each(function(c) {
10384         
10385             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10386         });
10387         
10388         for (var e = 0; e < this.calevents.length; e++) {
10389             var ev = this.calevents[e];
10390             var cells = ev.cells;
10391             var rows = ev.rows;
10392             
10393             for(var i =0; i < rows.length; i++) {
10394                 
10395                  
10396                 // how many rows should it span..
10397                 
10398                 var  cfg = {
10399                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10400                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10401                     
10402                     unselectable : "on",
10403                     cn : [
10404                         {
10405                             cls: 'fc-event-inner',
10406                             cn : [
10407 //                                {
10408 //                                  tag:'span',
10409 //                                  cls: 'fc-event-time',
10410 //                                  html : cells.length > 1 ? '' : ev.time
10411 //                                },
10412                                 {
10413                                   tag:'span',
10414                                   cls: 'fc-event-title',
10415                                   html : String.format('{0}', ev.title)
10416                                 }
10417                                 
10418                                 
10419                             ]
10420                         },
10421                         {
10422                             cls: 'ui-resizable-handle ui-resizable-e',
10423                             html : '&nbsp;&nbsp;&nbsp'
10424                         }
10425                         
10426                     ]
10427                 };
10428                 if (i == 0) {
10429                     cfg.cls += ' fc-event-start';
10430                 }
10431                 if ((i+1) == rows.length) {
10432                     cfg.cls += ' fc-event-end';
10433                 }
10434                 
10435                 var ctr = this.el.select('.fc-event-container',true).first();
10436                 var cg = ctr.createChild(cfg);
10437                 
10438                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10439                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10440                 cg.on('click', this.onEventClick, this, ev);
10441                 
10442                 ev.els.push(cg);
10443                 
10444                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10445                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10446                 //Roo.log(cg);
10447                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10448                 cg.setWidth(ebox.right - sbox.x -2);
10449             }
10450             
10451             
10452         }
10453         
10454     },
10455     
10456     onEventEnter: function (e, el,event,d) {
10457         this.fireEvent('evententer', this, el, event);
10458     },
10459     
10460     onEventLeave: function (e, el,event,d) {
10461         this.fireEvent('eventleave', this, el, event);
10462     },
10463     
10464     onEventClick: function (e, el,event,d) {
10465         this.fireEvent('eventclick', this, el, event);
10466     },
10467     
10468     onMonthChange: function () {
10469         this.store.load();
10470     },
10471     
10472     onLoad: function () 
10473     {   
10474         this.calevents = [];
10475         var cal = this;
10476         
10477         if(this.store.getCount() > 0){
10478             this.store.data.each(function(d){
10479                cal.addItem({
10480                     id : d.data.id,
10481                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10482                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10483                     time : d.data.start_time,
10484                     title : d.data.title,
10485                     description : d.data.description,
10486                     venue : d.data.venue
10487                 });
10488             });
10489         }
10490         
10491         this.renderEvents();
10492         
10493         if(this.loadMask){
10494             this.maskEl.hide();
10495         }
10496     },
10497     
10498     onBeforeLoad: function()
10499     {
10500         this.clearEvents();
10501         
10502         if(this.loadMask){
10503             this.maskEl.show();
10504         }
10505     }
10506 });
10507
10508  
10509  /*
10510  * - LGPL
10511  *
10512  * element
10513  * 
10514  */
10515
10516 /**
10517  * @class Roo.bootstrap.Popover
10518  * @extends Roo.bootstrap.Component
10519  * Bootstrap Popover class
10520  * @cfg {String} html contents of the popover   (or false to use children..)
10521  * @cfg {String} title of popover (or false to hide)
10522  * @cfg {String} placement how it is placed
10523  * @cfg {String} trigger click || hover (or false to trigger manually)
10524  * @cfg {String} over what (parent or false to trigger manually.)
10525  * 
10526  * @constructor
10527  * Create a new Popover
10528  * @param {Object} config The config object
10529  */
10530
10531 Roo.bootstrap.Popover = function(config){
10532     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10533 };
10534
10535 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10536     
10537     title: 'Fill in a title',
10538     html: false,
10539     
10540     placement : 'right',
10541     trigger : 'hover', // hover
10542     
10543     over: 'parent',
10544     
10545     can_build_overlaid : false,
10546     
10547     getChildContainer : function()
10548     {
10549         return this.el.select('.popover-content',true).first();
10550     },
10551     
10552     getAutoCreate : function(){
10553          Roo.log('make popover?');
10554         var cfg = {
10555            cls : 'popover roo-dynamic',
10556            style: 'display:block',
10557            cn : [
10558                 {
10559                     cls : 'arrow'
10560                 },
10561                 {
10562                     cls : 'popover-inner',
10563                     cn : [
10564                         {
10565                             tag: 'h3',
10566                             cls: 'popover-title',
10567                             html : this.title
10568                         },
10569                         {
10570                             cls : 'popover-content',
10571                             html : this.html
10572                         }
10573                     ]
10574                     
10575                 }
10576            ]
10577         };
10578         
10579         return cfg;
10580     },
10581     setTitle: function(str)
10582     {
10583         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10584     },
10585     setContent: function(str)
10586     {
10587         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10588     },
10589     // as it get's added to the bottom of the page.
10590     onRender : function(ct, position)
10591     {
10592         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10593         if(!this.el){
10594             var cfg = Roo.apply({},  this.getAutoCreate());
10595             cfg.id = Roo.id();
10596             
10597             if (this.cls) {
10598                 cfg.cls += ' ' + this.cls;
10599             }
10600             if (this.style) {
10601                 cfg.style = this.style;
10602             }
10603             Roo.log("adding to ")
10604             this.el = Roo.get(document.body).createChild(cfg, position);
10605             Roo.log(this.el);
10606         }
10607         this.initEvents();
10608     },
10609     
10610     initEvents : function()
10611     {
10612         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10613         this.el.enableDisplayMode('block');
10614         this.el.hide();
10615         if (this.over === false) {
10616             return; 
10617         }
10618         if (this.triggers === false) {
10619             return;
10620         }
10621         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10622         var triggers = this.trigger ? this.trigger.split(' ') : [];
10623         Roo.each(triggers, function(trigger) {
10624         
10625             if (trigger == 'click') {
10626                 on_el.on('click', this.toggle, this);
10627             } else if (trigger != 'manual') {
10628                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10629                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10630       
10631                 on_el.on(eventIn  ,this.enter, this);
10632                 on_el.on(eventOut, this.leave, this);
10633             }
10634         }, this);
10635         
10636     },
10637     
10638     
10639     // private
10640     timeout : null,
10641     hoverState : null,
10642     
10643     toggle : function () {
10644         this.hoverState == 'in' ? this.leave() : this.enter();
10645     },
10646     
10647     enter : function () {
10648        
10649     
10650         clearTimeout(this.timeout);
10651     
10652         this.hoverState = 'in'
10653     
10654         if (!this.delay || !this.delay.show) {
10655             this.show();
10656             return 
10657         }
10658         var _t = this;
10659         this.timeout = setTimeout(function () {
10660             if (_t.hoverState == 'in') {
10661                 _t.show();
10662             }
10663         }, this.delay.show)
10664     },
10665     leave : function() {
10666         clearTimeout(this.timeout);
10667     
10668         this.hoverState = 'out'
10669     
10670         if (!this.delay || !this.delay.hide) {
10671             this.hide();
10672             return 
10673         }
10674         var _t = this;
10675         this.timeout = setTimeout(function () {
10676             if (_t.hoverState == 'out') {
10677                 _t.hide();
10678             }
10679         }, this.delay.hide)
10680     },
10681     
10682     show : function (on_el)
10683     {
10684         if (!on_el) {
10685             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10686         }
10687         // set content.
10688         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10689         if (this.html !== false) {
10690             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10691         }
10692         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10693         if (!this.title.length) {
10694             this.el.select('.popover-title',true).hide();
10695         }
10696         
10697         var placement = typeof this.placement == 'function' ?
10698             this.placement.call(this, this.el, on_el) :
10699             this.placement;
10700             
10701         var autoToken = /\s?auto?\s?/i;
10702         var autoPlace = autoToken.test(placement);
10703         if (autoPlace) {
10704             placement = placement.replace(autoToken, '') || 'top';
10705         }
10706         
10707         //this.el.detach()
10708         //this.el.setXY([0,0]);
10709         this.el.show();
10710         this.el.dom.style.display='block';
10711         this.el.addClass(placement);
10712         
10713         //this.el.appendTo(on_el);
10714         
10715         var p = this.getPosition();
10716         var box = this.el.getBox();
10717         
10718         if (autoPlace) {
10719             // fixme..
10720         }
10721         var align = Roo.bootstrap.Popover.alignment[placement]
10722         this.el.alignTo(on_el, align[0],align[1]);
10723         //var arrow = this.el.select('.arrow',true).first();
10724         //arrow.set(align[2], 
10725         
10726         this.el.addClass('in');
10727         this.hoverState = null;
10728         
10729         if (this.el.hasClass('fade')) {
10730             // fade it?
10731         }
10732         
10733     },
10734     hide : function()
10735     {
10736         this.el.setXY([0,0]);
10737         this.el.removeClass('in');
10738         this.el.hide();
10739         
10740     }
10741     
10742 });
10743
10744 Roo.bootstrap.Popover.alignment = {
10745     'left' : ['r-l', [-10,0], 'right'],
10746     'right' : ['l-r', [10,0], 'left'],
10747     'bottom' : ['t-b', [0,10], 'top'],
10748     'top' : [ 'b-t', [0,-10], 'bottom']
10749 };
10750
10751  /*
10752  * - LGPL
10753  *
10754  * Progress
10755  * 
10756  */
10757
10758 /**
10759  * @class Roo.bootstrap.Progress
10760  * @extends Roo.bootstrap.Component
10761  * Bootstrap Progress class
10762  * @cfg {Boolean} striped striped of the progress bar
10763  * @cfg {Boolean} active animated of the progress bar
10764  * 
10765  * 
10766  * @constructor
10767  * Create a new Progress
10768  * @param {Object} config The config object
10769  */
10770
10771 Roo.bootstrap.Progress = function(config){
10772     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10773 };
10774
10775 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10776     
10777     striped : false,
10778     active: false,
10779     
10780     getAutoCreate : function(){
10781         var cfg = {
10782             tag: 'div',
10783             cls: 'progress'
10784         };
10785         
10786         
10787         if(this.striped){
10788             cfg.cls += ' progress-striped';
10789         }
10790       
10791         if(this.active){
10792             cfg.cls += ' active';
10793         }
10794         
10795         
10796         return cfg;
10797     }
10798    
10799 });
10800
10801  
10802
10803  /*
10804  * - LGPL
10805  *
10806  * ProgressBar
10807  * 
10808  */
10809
10810 /**
10811  * @class Roo.bootstrap.ProgressBar
10812  * @extends Roo.bootstrap.Component
10813  * Bootstrap ProgressBar class
10814  * @cfg {Number} aria_valuenow aria-value now
10815  * @cfg {Number} aria_valuemin aria-value min
10816  * @cfg {Number} aria_valuemax aria-value max
10817  * @cfg {String} label label for the progress bar
10818  * @cfg {String} panel (success | info | warning | danger )
10819  * @cfg {String} role role of the progress bar
10820  * @cfg {String} sr_only text
10821  * 
10822  * 
10823  * @constructor
10824  * Create a new ProgressBar
10825  * @param {Object} config The config object
10826  */
10827
10828 Roo.bootstrap.ProgressBar = function(config){
10829     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10830 };
10831
10832 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10833     
10834     aria_valuenow : 0,
10835     aria_valuemin : 0,
10836     aria_valuemax : 100,
10837     label : false,
10838     panel : false,
10839     role : false,
10840     sr_only: false,
10841     
10842     getAutoCreate : function()
10843     {
10844         
10845         var cfg = {
10846             tag: 'div',
10847             cls: 'progress-bar',
10848             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10849         };
10850         
10851         if(this.sr_only){
10852             cfg.cn = {
10853                 tag: 'span',
10854                 cls: 'sr-only',
10855                 html: this.sr_only
10856             }
10857         }
10858         
10859         if(this.role){
10860             cfg.role = this.role;
10861         }
10862         
10863         if(this.aria_valuenow){
10864             cfg['aria-valuenow'] = this.aria_valuenow;
10865         }
10866         
10867         if(this.aria_valuemin){
10868             cfg['aria-valuemin'] = this.aria_valuemin;
10869         }
10870         
10871         if(this.aria_valuemax){
10872             cfg['aria-valuemax'] = this.aria_valuemax;
10873         }
10874         
10875         if(this.label && !this.sr_only){
10876             cfg.html = this.label;
10877         }
10878         
10879         if(this.panel){
10880             cfg.cls += ' progress-bar-' + this.panel;
10881         }
10882         
10883         return cfg;
10884     },
10885     
10886     update : function(aria_valuenow)
10887     {
10888         this.aria_valuenow = aria_valuenow;
10889         
10890         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10891     }
10892    
10893 });
10894
10895  
10896
10897  /*
10898  * - LGPL
10899  *
10900  * TabPanel
10901  * 
10902  */
10903
10904 /**
10905  * @class Roo.bootstrap.TabPanel
10906  * @extends Roo.bootstrap.Component
10907  * Bootstrap TabPanel class
10908  * @cfg {Boolean} active panel active
10909  * @cfg {String} html panel content
10910  * @cfg {String} tabId tab relate id
10911  * 
10912  * 
10913  * @constructor
10914  * Create a new TabPanel
10915  * @param {Object} config The config object
10916  */
10917
10918 Roo.bootstrap.TabPanel = function(config){
10919     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10920 };
10921
10922 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10923     
10924     active: false,
10925     html: false,
10926     tabId: false,
10927     
10928     getAutoCreate : function(){
10929         var cfg = {
10930             tag: 'div',
10931             cls: 'tab-pane',
10932             html: this.html || ''
10933         };
10934         
10935         if(this.active){
10936             cfg.cls += ' active';
10937         }
10938         
10939         if(this.tabId){
10940             cfg.tabId = this.tabId;
10941         }
10942         
10943         return cfg;
10944     }
10945    
10946 });
10947
10948  
10949
10950  /*
10951  * - LGPL
10952  *
10953  * DateField
10954  * 
10955  */
10956
10957 /**
10958  * @class Roo.bootstrap.DateField
10959  * @extends Roo.bootstrap.Input
10960  * Bootstrap DateField class
10961  * @cfg {Number} weekStart default 0
10962  * @cfg {Number} weekStart default 0
10963  * @cfg {Number} viewMode default empty, (months|years)
10964  * @cfg {Number} minViewMode default empty, (months|years)
10965  * @cfg {Number} startDate default -Infinity
10966  * @cfg {Number} endDate default Infinity
10967  * @cfg {Boolean} todayHighlight default false
10968  * @cfg {Boolean} todayBtn default false
10969  * @cfg {Boolean} calendarWeeks default false
10970  * @cfg {Object} daysOfWeekDisabled default empty
10971  * 
10972  * @cfg {Boolean} keyboardNavigation default true
10973  * @cfg {String} language default en
10974  * 
10975  * @constructor
10976  * Create a new DateField
10977  * @param {Object} config The config object
10978  */
10979
10980 Roo.bootstrap.DateField = function(config){
10981     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10982      this.addEvents({
10983             /**
10984              * @event show
10985              * Fires when this field show.
10986              * @param {Roo.bootstrap.DateField} this
10987              * @param {Mixed} date The date value
10988              */
10989             show : true,
10990             /**
10991              * @event show
10992              * Fires when this field hide.
10993              * @param {Roo.bootstrap.DateField} this
10994              * @param {Mixed} date The date value
10995              */
10996             hide : true,
10997             /**
10998              * @event select
10999              * Fires when select a date.
11000              * @param {Roo.bootstrap.DateField} this
11001              * @param {Mixed} date The date value
11002              */
11003             select : true
11004         });
11005 };
11006
11007 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11008     
11009     /**
11010      * @cfg {String} format
11011      * The default date format string which can be overriden for localization support.  The format must be
11012      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11013      */
11014     format : "m/d/y",
11015     /**
11016      * @cfg {String} altFormats
11017      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11018      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11019      */
11020     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11021     
11022     weekStart : 0,
11023     
11024     viewMode : '',
11025     
11026     minViewMode : '',
11027     
11028     todayHighlight : false,
11029     
11030     todayBtn: false,
11031     
11032     language: 'en',
11033     
11034     keyboardNavigation: true,
11035     
11036     calendarWeeks: false,
11037     
11038     startDate: -Infinity,
11039     
11040     endDate: Infinity,
11041     
11042     daysOfWeekDisabled: [],
11043     
11044     _events: [],
11045     
11046     UTCDate: function()
11047     {
11048         return new Date(Date.UTC.apply(Date, arguments));
11049     },
11050     
11051     UTCToday: function()
11052     {
11053         var today = new Date();
11054         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11055     },
11056     
11057     getDate: function() {
11058             var d = this.getUTCDate();
11059             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11060     },
11061     
11062     getUTCDate: function() {
11063             return this.date;
11064     },
11065     
11066     setDate: function(d) {
11067             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11068     },
11069     
11070     setUTCDate: function(d) {
11071             this.date = d;
11072             this.setValue(this.formatDate(this.date));
11073     },
11074         
11075     onRender: function(ct, position)
11076     {
11077         
11078         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11079         
11080         this.language = this.language || 'en';
11081         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11082         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11083         
11084         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11085         this.format = this.format || 'm/d/y';
11086         this.isInline = false;
11087         this.isInput = true;
11088         this.component = this.el.select('.add-on', true).first() || false;
11089         this.component = (this.component && this.component.length === 0) ? false : this.component;
11090         this.hasInput = this.component && this.inputEL().length;
11091         
11092         if (typeof(this.minViewMode === 'string')) {
11093             switch (this.minViewMode) {
11094                 case 'months':
11095                     this.minViewMode = 1;
11096                     break;
11097                 case 'years':
11098                     this.minViewMode = 2;
11099                     break;
11100                 default:
11101                     this.minViewMode = 0;
11102                     break;
11103             }
11104         }
11105         
11106         if (typeof(this.viewMode === 'string')) {
11107             switch (this.viewMode) {
11108                 case 'months':
11109                     this.viewMode = 1;
11110                     break;
11111                 case 'years':
11112                     this.viewMode = 2;
11113                     break;
11114                 default:
11115                     this.viewMode = 0;
11116                     break;
11117             }
11118         }
11119                 
11120         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11121         
11122         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11123         
11124         this.picker().on('mousedown', this.onMousedown, this);
11125         this.picker().on('click', this.onClick, this);
11126         
11127         this.picker().addClass('datepicker-dropdown');
11128         
11129         this.startViewMode = this.viewMode;
11130         
11131         
11132         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11133             if(!this.calendarWeeks){
11134                 v.remove();
11135                 return;
11136             };
11137             
11138             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11139             v.attr('colspan', function(i, val){
11140                 return parseInt(val) + 1;
11141             });
11142         })
11143                         
11144         
11145         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11146         
11147         this.setStartDate(this.startDate);
11148         this.setEndDate(this.endDate);
11149         
11150         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11151         
11152         this.fillDow();
11153         this.fillMonths();
11154         this.update();
11155         this.showMode();
11156         
11157         if(this.isInline) {
11158             this.show();
11159         }
11160     },
11161     
11162     picker : function()
11163     {
11164         return this.el.select('.datepicker', true).first();
11165     },
11166     
11167     fillDow: function()
11168     {
11169         var dowCnt = this.weekStart;
11170         
11171         var dow = {
11172             tag: 'tr',
11173             cn: [
11174                 
11175             ]
11176         };
11177         
11178         if(this.calendarWeeks){
11179             dow.cn.push({
11180                 tag: 'th',
11181                 cls: 'cw',
11182                 html: '&nbsp;'
11183             })
11184         }
11185         
11186         while (dowCnt < this.weekStart + 7) {
11187             dow.cn.push({
11188                 tag: 'th',
11189                 cls: 'dow',
11190                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11191             });
11192         }
11193         
11194         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11195     },
11196     
11197     fillMonths: function()
11198     {    
11199         var i = 0
11200         var months = this.picker().select('>.datepicker-months td', true).first();
11201         
11202         months.dom.innerHTML = '';
11203         
11204         while (i < 12) {
11205             var month = {
11206                 tag: 'span',
11207                 cls: 'month',
11208                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11209             }
11210             
11211             months.createChild(month);
11212         }
11213         
11214     },
11215     
11216     update: function(){
11217         
11218         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11219         
11220         if (this.date < this.startDate) {
11221             this.viewDate = new Date(this.startDate);
11222         } else if (this.date > this.endDate) {
11223             this.viewDate = new Date(this.endDate);
11224         } else {
11225             this.viewDate = new Date(this.date);
11226         }
11227         
11228         this.fill();
11229     },
11230     
11231     fill: function() {
11232         var d = new Date(this.viewDate),
11233                 year = d.getUTCFullYear(),
11234                 month = d.getUTCMonth(),
11235                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11236                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11237                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11238                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11239                 currentDate = this.date && this.date.valueOf(),
11240                 today = this.UTCToday();
11241         
11242         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11243         
11244 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11245         
11246 //        this.picker.select('>tfoot th.today').
11247 //                                              .text(dates[this.language].today)
11248 //                                              .toggle(this.todayBtn !== false);
11249     
11250         this.updateNavArrows();
11251         this.fillMonths();
11252                                                 
11253         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11254         
11255         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11256          
11257         prevMonth.setUTCDate(day);
11258         
11259         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11260         
11261         var nextMonth = new Date(prevMonth);
11262         
11263         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11264         
11265         nextMonth = nextMonth.valueOf();
11266         
11267         var fillMonths = false;
11268         
11269         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11270         
11271         while(prevMonth.valueOf() < nextMonth) {
11272             var clsName = '';
11273             
11274             if (prevMonth.getUTCDay() === this.weekStart) {
11275                 if(fillMonths){
11276                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11277                 }
11278                     
11279                 fillMonths = {
11280                     tag: 'tr',
11281                     cn: []
11282                 };
11283                 
11284                 if(this.calendarWeeks){
11285                     // ISO 8601: First week contains first thursday.
11286                     // ISO also states week starts on Monday, but we can be more abstract here.
11287                     var
11288                     // Start of current week: based on weekstart/current date
11289                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11290                     // Thursday of this week
11291                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11292                     // First Thursday of year, year from thursday
11293                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11294                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11295                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11296                     
11297                     fillMonths.cn.push({
11298                         tag: 'td',
11299                         cls: 'cw',
11300                         html: calWeek
11301                     });
11302                 }
11303             }
11304             
11305             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11306                 clsName += ' old';
11307             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11308                 clsName += ' new';
11309             }
11310             if (this.todayHighlight &&
11311                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11312                 prevMonth.getUTCMonth() == today.getMonth() &&
11313                 prevMonth.getUTCDate() == today.getDate()) {
11314                 clsName += ' today';
11315             }
11316             
11317             if (currentDate && prevMonth.valueOf() === currentDate) {
11318                 clsName += ' active';
11319             }
11320             
11321             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11322                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11323                     clsName += ' disabled';
11324             }
11325             
11326             fillMonths.cn.push({
11327                 tag: 'td',
11328                 cls: 'day ' + clsName,
11329                 html: prevMonth.getDate()
11330             })
11331             
11332             prevMonth.setDate(prevMonth.getDate()+1);
11333         }
11334           
11335         var currentYear = this.date && this.date.getUTCFullYear();
11336         var currentMonth = this.date && this.date.getUTCMonth();
11337         
11338         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11339         
11340         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11341             v.removeClass('active');
11342             
11343             if(currentYear === year && k === currentMonth){
11344                 v.addClass('active');
11345             }
11346             
11347             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11348                 v.addClass('disabled');
11349             }
11350             
11351         });
11352         
11353         
11354         year = parseInt(year/10, 10) * 10;
11355         
11356         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11357         
11358         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11359         
11360         year -= 1;
11361         for (var i = -1; i < 11; i++) {
11362             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11363                 tag: 'span',
11364                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11365                 html: year
11366             })
11367             
11368             year += 1;
11369         }
11370     },
11371     
11372     showMode: function(dir) {
11373         if (dir) {
11374             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11375         }
11376         Roo.each(this.picker().select('>div',true).elements, function(v){
11377             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11378             v.hide();
11379         });
11380         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11381     },
11382     
11383     place: function()
11384     {
11385         if(this.isInline) return;
11386         
11387         this.picker().removeClass(['bottom', 'top']);
11388         
11389         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11390             /*
11391              * place to the top of element!
11392              *
11393              */
11394             
11395             this.picker().addClass('top');
11396             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11397             
11398             return;
11399         }
11400         
11401         this.picker().addClass('bottom');
11402         
11403         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11404     },
11405     
11406     parseDate : function(value){
11407         if(!value || value instanceof Date){
11408             return value;
11409         }
11410         var v = Date.parseDate(value, this.format);
11411         if (!v && this.useIso) {
11412             v = Date.parseDate(value, 'Y-m-d');
11413         }
11414         if(!v && this.altFormats){
11415             if(!this.altFormatsArray){
11416                 this.altFormatsArray = this.altFormats.split("|");
11417             }
11418             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11419                 v = Date.parseDate(value, this.altFormatsArray[i]);
11420             }
11421         }
11422         return v;
11423     },
11424     
11425     formatDate : function(date, fmt){
11426         return (!date || !(date instanceof Date)) ?
11427         date : date.dateFormat(fmt || this.format);
11428     },
11429     
11430     onFocus : function()
11431     {
11432         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11433         this.show();
11434     },
11435     
11436     onBlur : function()
11437     {
11438         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11439         this.hide();
11440     },
11441     
11442     show : function()
11443     {
11444         this.picker().show();
11445         this.update();
11446         this.place();
11447         
11448         this.fireEvent('show', this, this.date);
11449     },
11450     
11451     hide : function()
11452     {
11453         if(this.isInline) return;
11454         this.picker().hide();
11455         this.viewMode = this.startViewMode;
11456         this.showMode();
11457         
11458         this.fireEvent('hide', this, this.date);
11459         
11460     },
11461     
11462     onMousedown: function(e){
11463         e.stopPropagation();
11464         e.preventDefault();
11465     },
11466     
11467     keyup: function(e){
11468         Roo.bootstrap.DateField.superclass.keyup.call(this);
11469         this.update();
11470         
11471     },
11472
11473     setValue: function(v){
11474         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11475         
11476         this.fireEvent('select', this, this.date);
11477         
11478     },
11479     
11480     fireKey: function(e){
11481         if (!this.picker().isVisible()){
11482             if (e.keyCode == 27) // allow escape to hide and re-show picker
11483                 this.show();
11484             return;
11485         }
11486         var dateChanged = false,
11487         dir, day, month,
11488         newDate, newViewDate;
11489         switch(e.keyCode){
11490             case 27: // escape
11491                 this.hide();
11492                 e.preventDefault();
11493                 break;
11494             case 37: // left
11495             case 39: // right
11496                 if (!this.keyboardNavigation) break;
11497                 dir = e.keyCode == 37 ? -1 : 1;
11498                 
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);
11508                     newViewDate = new Date(this.viewDate);
11509                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
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 38: // up
11521             case 40: // down
11522                 if (!this.keyboardNavigation) break;
11523                 dir = e.keyCode == 38 ? -1 : 1;
11524                 if (e.ctrlKey){
11525                     newDate = this.moveYear(this.date, dir);
11526                     newViewDate = this.moveYear(this.viewDate, dir);
11527                 } else if (e.shiftKey){
11528                     newDate = this.moveMonth(this.date, dir);
11529                     newViewDate = this.moveMonth(this.viewDate, dir);
11530                 } else {
11531                     newDate = new Date(this.date);
11532                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11533                     newViewDate = new Date(this.viewDate);
11534                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11535                 }
11536                 if (this.dateWithinRange(newDate)){
11537                     this.date = newDate;
11538                     this.viewDate = newViewDate;
11539                     this.setValue(this.formatDate(this.date));
11540                     this.update();
11541                     e.preventDefault();
11542                     dateChanged = true;
11543                 }
11544                 break;
11545             case 13: // enter
11546                 this.setValue(this.formatDate(this.date));
11547                 this.hide();
11548                 e.preventDefault();
11549                 break;
11550             case 9: // tab
11551                 this.setValue(this.formatDate(this.date));
11552                 this.hide();
11553                 break;
11554         }
11555     },
11556     
11557     
11558     onClick: function(e) {
11559         e.stopPropagation();
11560         e.preventDefault();
11561         
11562         var target = e.getTarget();
11563         
11564         if(target.nodeName.toLowerCase() === 'i'){
11565             target = Roo.get(target).dom.parentNode;
11566         }
11567         
11568         var nodeName = target.nodeName;
11569         var className = target.className;
11570         var html = target.innerHTML;
11571         
11572         switch(nodeName.toLowerCase()) {
11573             case 'th':
11574                 switch(className) {
11575                     case 'switch':
11576                         this.showMode(1);
11577                         break;
11578                     case 'prev':
11579                     case 'next':
11580                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11581                         switch(this.viewMode){
11582                                 case 0:
11583                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11584                                         break;
11585                                 case 1:
11586                                 case 2:
11587                                         this.viewDate = this.moveYear(this.viewDate, dir);
11588                                         break;
11589                         }
11590                         this.fill();
11591                         break;
11592                     case 'today':
11593                         var date = new Date();
11594                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11595                         this.fill()
11596                         this.setValue(this.formatDate(this.date));
11597                         this.hide();
11598                         break;
11599                 }
11600                 break;
11601             case 'span':
11602                 if (className.indexOf('disabled') === -1) {
11603                     this.viewDate.setUTCDate(1);
11604                     if (className.indexOf('month') !== -1) {
11605                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11606                     } else {
11607                         var year = parseInt(html, 10) || 0;
11608                         this.viewDate.setUTCFullYear(year);
11609                         
11610                     }
11611                     this.showMode(-1);
11612                     this.fill();
11613                 }
11614                 break;
11615                 
11616             case 'td':
11617                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11618                     var day = parseInt(html, 10) || 1;
11619                     var year = this.viewDate.getUTCFullYear(),
11620                         month = this.viewDate.getUTCMonth();
11621
11622                     if (className.indexOf('old') !== -1) {
11623                         if(month === 0 ){
11624                             month = 11;
11625                             year -= 1;
11626                         }else{
11627                             month -= 1;
11628                         }
11629                     } else if (className.indexOf('new') !== -1) {
11630                         if (month == 11) {
11631                             month = 0;
11632                             year += 1;
11633                         } else {
11634                             month += 1;
11635                         }
11636                     }
11637                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11638                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11639                     this.fill();
11640                     this.setValue(this.formatDate(this.date));
11641                     this.hide();
11642                 }
11643                 break;
11644         }
11645     },
11646     
11647     setStartDate: function(startDate){
11648         this.startDate = startDate || -Infinity;
11649         if (this.startDate !== -Infinity) {
11650             this.startDate = this.parseDate(this.startDate);
11651         }
11652         this.update();
11653         this.updateNavArrows();
11654     },
11655
11656     setEndDate: function(endDate){
11657         this.endDate = endDate || Infinity;
11658         if (this.endDate !== Infinity) {
11659             this.endDate = this.parseDate(this.endDate);
11660         }
11661         this.update();
11662         this.updateNavArrows();
11663     },
11664     
11665     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11666         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11667         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11668             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11669         }
11670         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11671             return parseInt(d, 10);
11672         });
11673         this.update();
11674         this.updateNavArrows();
11675     },
11676     
11677     updateNavArrows: function() {
11678         var d = new Date(this.viewDate),
11679         year = d.getUTCFullYear(),
11680         month = d.getUTCMonth();
11681         
11682         Roo.each(this.picker().select('.prev', true).elements, function(v){
11683             v.show();
11684             switch (this.viewMode) {
11685                 case 0:
11686
11687                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11688                         v.hide();
11689                     }
11690                     break;
11691                 case 1:
11692                 case 2:
11693                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11694                         v.hide();
11695                     }
11696                     break;
11697             }
11698         });
11699         
11700         Roo.each(this.picker().select('.next', true).elements, function(v){
11701             v.show();
11702             switch (this.viewMode) {
11703                 case 0:
11704
11705                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11706                         v.hide();
11707                     }
11708                     break;
11709                 case 1:
11710                 case 2:
11711                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11712                         v.hide();
11713                     }
11714                     break;
11715             }
11716         })
11717     },
11718     
11719     moveMonth: function(date, dir){
11720         if (!dir) return date;
11721         var new_date = new Date(date.valueOf()),
11722         day = new_date.getUTCDate(),
11723         month = new_date.getUTCMonth(),
11724         mag = Math.abs(dir),
11725         new_month, test;
11726         dir = dir > 0 ? 1 : -1;
11727         if (mag == 1){
11728             test = dir == -1
11729             // If going back one month, make sure month is not current month
11730             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11731             ? function(){
11732                 return new_date.getUTCMonth() == month;
11733             }
11734             // If going forward one month, make sure month is as expected
11735             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11736             : function(){
11737                 return new_date.getUTCMonth() != new_month;
11738             };
11739             new_month = month + dir;
11740             new_date.setUTCMonth(new_month);
11741             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11742             if (new_month < 0 || new_month > 11)
11743                 new_month = (new_month + 12) % 12;
11744         } else {
11745             // For magnitudes >1, move one month at a time...
11746             for (var i=0; i<mag; i++)
11747                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11748                 new_date = this.moveMonth(new_date, dir);
11749             // ...then reset the day, keeping it in the new month
11750             new_month = new_date.getUTCMonth();
11751             new_date.setUTCDate(day);
11752             test = function(){
11753                 return new_month != new_date.getUTCMonth();
11754             };
11755         }
11756         // Common date-resetting loop -- if date is beyond end of month, make it
11757         // end of month
11758         while (test()){
11759             new_date.setUTCDate(--day);
11760             new_date.setUTCMonth(new_month);
11761         }
11762         return new_date;
11763     },
11764
11765     moveYear: function(date, dir){
11766         return this.moveMonth(date, dir*12);
11767     },
11768
11769     dateWithinRange: function(date){
11770         return date >= this.startDate && date <= this.endDate;
11771     },
11772
11773     
11774     remove: function() {
11775         this.picker().remove();
11776     }
11777    
11778 });
11779
11780 Roo.apply(Roo.bootstrap.DateField,  {
11781     
11782     head : {
11783         tag: 'thead',
11784         cn: [
11785         {
11786             tag: 'tr',
11787             cn: [
11788             {
11789                 tag: 'th',
11790                 cls: 'prev',
11791                 html: '<i class="icon-arrow-left"/>'
11792             },
11793             {
11794                 tag: 'th',
11795                 cls: 'switch',
11796                 colspan: '5'
11797             },
11798             {
11799                 tag: 'th',
11800                 cls: 'next',
11801                 html: '<i class="icon-arrow-right"/>'
11802             }
11803
11804             ]
11805         }
11806         ]
11807     },
11808     
11809     content : {
11810         tag: 'tbody',
11811         cn: [
11812         {
11813             tag: 'tr',
11814             cn: [
11815             {
11816                 tag: 'td',
11817                 colspan: '7'
11818             }
11819             ]
11820         }
11821         ]
11822     },
11823     
11824     footer : {
11825         tag: 'tfoot',
11826         cn: [
11827         {
11828             tag: 'tr',
11829             cn: [
11830             {
11831                 tag: 'th',
11832                 colspan: '7',
11833                 cls: 'today'
11834             }
11835                     
11836             ]
11837         }
11838         ]
11839     },
11840     
11841     dates:{
11842         en: {
11843             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11844             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11845             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11846             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11847             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11848             today: "Today"
11849         }
11850     },
11851     
11852     modes: [
11853     {
11854         clsName: 'days',
11855         navFnc: 'Month',
11856         navStep: 1
11857     },
11858     {
11859         clsName: 'months',
11860         navFnc: 'FullYear',
11861         navStep: 1
11862     },
11863     {
11864         clsName: 'years',
11865         navFnc: 'FullYear',
11866         navStep: 10
11867     }]
11868 });
11869
11870 Roo.apply(Roo.bootstrap.DateField,  {
11871   
11872     template : {
11873         tag: 'div',
11874         cls: 'datepicker dropdown-menu',
11875         cn: [
11876         {
11877             tag: 'div',
11878             cls: 'datepicker-days',
11879             cn: [
11880             {
11881                 tag: 'table',
11882                 cls: 'table-condensed',
11883                 cn:[
11884                 Roo.bootstrap.DateField.head,
11885                 {
11886                     tag: 'tbody'
11887                 },
11888                 Roo.bootstrap.DateField.footer
11889                 ]
11890             }
11891             ]
11892         },
11893         {
11894             tag: 'div',
11895             cls: 'datepicker-months',
11896             cn: [
11897             {
11898                 tag: 'table',
11899                 cls: 'table-condensed',
11900                 cn:[
11901                 Roo.bootstrap.DateField.head,
11902                 Roo.bootstrap.DateField.content,
11903                 Roo.bootstrap.DateField.footer
11904                 ]
11905             }
11906             ]
11907         },
11908         {
11909             tag: 'div',
11910             cls: 'datepicker-years',
11911             cn: [
11912             {
11913                 tag: 'table',
11914                 cls: 'table-condensed',
11915                 cn:[
11916                 Roo.bootstrap.DateField.head,
11917                 Roo.bootstrap.DateField.content,
11918                 Roo.bootstrap.DateField.footer
11919                 ]
11920             }
11921             ]
11922         }
11923         ]
11924     }
11925 });
11926
11927  
11928
11929  /*
11930  * - LGPL
11931  *
11932  * TimeField
11933  * 
11934  */
11935
11936 /**
11937  * @class Roo.bootstrap.TimeField
11938  * @extends Roo.bootstrap.Input
11939  * Bootstrap DateField class
11940  * 
11941  * 
11942  * @constructor
11943  * Create a new TimeField
11944  * @param {Object} config The config object
11945  */
11946
11947 Roo.bootstrap.TimeField = function(config){
11948     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11949     this.addEvents({
11950             /**
11951              * @event show
11952              * Fires when this field show.
11953              * @param {Roo.bootstrap.DateField} this
11954              * @param {Mixed} date The date value
11955              */
11956             show : true,
11957             /**
11958              * @event show
11959              * Fires when this field hide.
11960              * @param {Roo.bootstrap.DateField} this
11961              * @param {Mixed} date The date value
11962              */
11963             hide : true,
11964             /**
11965              * @event select
11966              * Fires when select a date.
11967              * @param {Roo.bootstrap.DateField} this
11968              * @param {Mixed} date The date value
11969              */
11970             select : true
11971         });
11972 };
11973
11974 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11975     
11976     /**
11977      * @cfg {String} format
11978      * The default time format string which can be overriden for localization support.  The format must be
11979      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11980      */
11981     format : "H:i",
11982        
11983     onRender: function(ct, position)
11984     {
11985         
11986         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11987                 
11988         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11989         
11990         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11991         
11992         this.pop = this.picker().select('>.datepicker-time',true).first();
11993         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11994         
11995         this.picker().on('mousedown', this.onMousedown, this);
11996         this.picker().on('click', this.onClick, this);
11997         
11998         this.picker().addClass('datepicker-dropdown');
11999     
12000         this.fillTime();
12001         this.update();
12002             
12003         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12004         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12005         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12006         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12007         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12008         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12009
12010     },
12011     
12012     fireKey: function(e){
12013         if (!this.picker().isVisible()){
12014             if (e.keyCode == 27) // allow escape to hide and re-show picker
12015                 this.show();
12016             return;
12017         }
12018
12019         e.preventDefault();
12020         
12021         switch(e.keyCode){
12022             case 27: // escape
12023                 this.hide();
12024                 break;
12025             case 37: // left
12026             case 39: // right
12027                 this.onTogglePeriod();
12028                 break;
12029             case 38: // up
12030                 this.onIncrementMinutes();
12031                 break;
12032             case 40: // down
12033                 this.onDecrementMinutes();
12034                 break;
12035             case 13: // enter
12036             case 9: // tab
12037                 this.setTime();
12038                 break;
12039         }
12040     },
12041     
12042     onClick: function(e) {
12043         e.stopPropagation();
12044         e.preventDefault();
12045     },
12046     
12047     picker : function()
12048     {
12049         return this.el.select('.datepicker', true).first();
12050     },
12051     
12052     fillTime: function()
12053     {    
12054         var time = this.pop.select('tbody', true).first();
12055         
12056         time.dom.innerHTML = '';
12057         
12058         time.createChild({
12059             tag: 'tr',
12060             cn: [
12061                 {
12062                     tag: 'td',
12063                     cn: [
12064                         {
12065                             tag: 'a',
12066                             href: '#',
12067                             cls: 'btn',
12068                             cn: [
12069                                 {
12070                                     tag: 'span',
12071                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12072                                 }
12073                             ]
12074                         } 
12075                     ]
12076                 },
12077                 {
12078                     tag: 'td',
12079                     cls: 'separator'
12080                 },
12081                 {
12082                     tag: 'td',
12083                     cn: [
12084                         {
12085                             tag: 'a',
12086                             href: '#',
12087                             cls: 'btn',
12088                             cn: [
12089                                 {
12090                                     tag: 'span',
12091                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12092                                 }
12093                             ]
12094                         }
12095                     ]
12096                 },
12097                 {
12098                     tag: 'td',
12099                     cls: 'separator'
12100                 }
12101             ]
12102         });
12103         
12104         time.createChild({
12105             tag: 'tr',
12106             cn: [
12107                 {
12108                     tag: 'td',
12109                     cn: [
12110                         {
12111                             tag: 'span',
12112                             cls: 'timepicker-hour',
12113                             html: '00'
12114                         }  
12115                     ]
12116                 },
12117                 {
12118                     tag: 'td',
12119                     cls: 'separator',
12120                     html: ':'
12121                 },
12122                 {
12123                     tag: 'td',
12124                     cn: [
12125                         {
12126                             tag: 'span',
12127                             cls: 'timepicker-minute',
12128                             html: '00'
12129                         }  
12130                     ]
12131                 },
12132                 {
12133                     tag: 'td',
12134                     cls: 'separator'
12135                 },
12136                 {
12137                     tag: 'td',
12138                     cn: [
12139                         {
12140                             tag: 'button',
12141                             type: 'button',
12142                             cls: 'btn btn-primary period',
12143                             html: 'AM'
12144                             
12145                         }
12146                     ]
12147                 }
12148             ]
12149         });
12150         
12151         time.createChild({
12152             tag: 'tr',
12153             cn: [
12154                 {
12155                     tag: 'td',
12156                     cn: [
12157                         {
12158                             tag: 'a',
12159                             href: '#',
12160                             cls: 'btn',
12161                             cn: [
12162                                 {
12163                                     tag: 'span',
12164                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12165                                 }
12166                             ]
12167                         }
12168                     ]
12169                 },
12170                 {
12171                     tag: 'td',
12172                     cls: 'separator'
12173                 },
12174                 {
12175                     tag: 'td',
12176                     cn: [
12177                         {
12178                             tag: 'a',
12179                             href: '#',
12180                             cls: 'btn',
12181                             cn: [
12182                                 {
12183                                     tag: 'span',
12184                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12185                                 }
12186                             ]
12187                         }
12188                     ]
12189                 },
12190                 {
12191                     tag: 'td',
12192                     cls: 'separator'
12193                 }
12194             ]
12195         });
12196         
12197     },
12198     
12199     update: function()
12200     {
12201         
12202         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12203         
12204         this.fill();
12205     },
12206     
12207     fill: function() 
12208     {
12209         var hours = this.time.getHours();
12210         var minutes = this.time.getMinutes();
12211         var period = 'AM';
12212         
12213         if(hours > 11){
12214             period = 'PM';
12215         }
12216         
12217         if(hours == 0){
12218             hours = 12;
12219         }
12220         
12221         
12222         if(hours > 12){
12223             hours = hours - 12;
12224         }
12225         
12226         if(hours < 10){
12227             hours = '0' + hours;
12228         }
12229         
12230         if(minutes < 10){
12231             minutes = '0' + minutes;
12232         }
12233         
12234         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12235         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12236         this.pop.select('button', true).first().dom.innerHTML = period;
12237         
12238     },
12239     
12240     place: function()
12241     {   
12242         this.picker().removeClass(['bottom', 'top']);
12243         
12244         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12245             /*
12246              * place to the top of element!
12247              *
12248              */
12249             
12250             this.picker().addClass('top');
12251             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12252             
12253             return;
12254         }
12255         
12256         this.picker().addClass('bottom');
12257         
12258         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12259     },
12260   
12261     onFocus : function()
12262     {
12263         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12264         this.show();
12265     },
12266     
12267     onBlur : function()
12268     {
12269         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12270         this.hide();
12271     },
12272     
12273     show : function()
12274     {
12275         this.picker().show();
12276         this.pop.show();
12277         this.update();
12278         this.place();
12279         
12280         this.fireEvent('show', this, this.date);
12281     },
12282     
12283     hide : function()
12284     {
12285         this.picker().hide();
12286         this.pop.hide();
12287         
12288         this.fireEvent('hide', this, this.date);
12289     },
12290     
12291     setTime : function()
12292     {
12293         this.hide();
12294         this.setValue(this.time.format(this.format));
12295         
12296         this.fireEvent('select', this, this.date);
12297         
12298         
12299     },
12300     
12301     onMousedown: function(e){
12302         e.stopPropagation();
12303         e.preventDefault();
12304     },
12305     
12306     onIncrementHours: function()
12307     {
12308         Roo.log('onIncrementHours');
12309         this.time = this.time.add(Date.HOUR, 1);
12310         this.update();
12311         
12312     },
12313     
12314     onDecrementHours: function()
12315     {
12316         Roo.log('onDecrementHours');
12317         this.time = this.time.add(Date.HOUR, -1);
12318         this.update();
12319     },
12320     
12321     onIncrementMinutes: function()
12322     {
12323         Roo.log('onIncrementMinutes');
12324         this.time = this.time.add(Date.MINUTE, 1);
12325         this.update();
12326     },
12327     
12328     onDecrementMinutes: function()
12329     {
12330         Roo.log('onDecrementMinutes');
12331         this.time = this.time.add(Date.MINUTE, -1);
12332         this.update();
12333     },
12334     
12335     onTogglePeriod: function()
12336     {
12337         Roo.log('onTogglePeriod');
12338         this.time = this.time.add(Date.HOUR, 12);
12339         this.update();
12340     }
12341     
12342    
12343 });
12344
12345 Roo.apply(Roo.bootstrap.TimeField,  {
12346     
12347     content : {
12348         tag: 'tbody',
12349         cn: [
12350             {
12351                 tag: 'tr',
12352                 cn: [
12353                 {
12354                     tag: 'td',
12355                     colspan: '7'
12356                 }
12357                 ]
12358             }
12359         ]
12360     },
12361     
12362     footer : {
12363         tag: 'tfoot',
12364         cn: [
12365             {
12366                 tag: 'tr',
12367                 cn: [
12368                 {
12369                     tag: 'th',
12370                     colspan: '7',
12371                     cls: '',
12372                     cn: [
12373                         {
12374                             tag: 'button',
12375                             cls: 'btn btn-info ok',
12376                             html: 'OK'
12377                         }
12378                     ]
12379                 }
12380
12381                 ]
12382             }
12383         ]
12384     }
12385 });
12386
12387 Roo.apply(Roo.bootstrap.TimeField,  {
12388   
12389     template : {
12390         tag: 'div',
12391         cls: 'datepicker dropdown-menu',
12392         cn: [
12393             {
12394                 tag: 'div',
12395                 cls: 'datepicker-time',
12396                 cn: [
12397                 {
12398                     tag: 'table',
12399                     cls: 'table-condensed',
12400                     cn:[
12401                     Roo.bootstrap.TimeField.content,
12402                     Roo.bootstrap.TimeField.footer
12403                     ]
12404                 }
12405                 ]
12406             }
12407         ]
12408     }
12409 });
12410
12411  
12412
12413  /*
12414  * - LGPL
12415  *
12416  * CheckBox
12417  * 
12418  */
12419
12420 /**
12421  * @class Roo.bootstrap.CheckBox
12422  * @extends Roo.bootstrap.Input
12423  * Bootstrap CheckBox class
12424  * 
12425  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12426  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12427  * @cfg {String} boxLabel The text that appears beside the checkbox
12428  * @cfg {Boolean} checked initnal the element
12429  * 
12430  * @constructor
12431  * Create a new CheckBox
12432  * @param {Object} config The config object
12433  */
12434
12435 Roo.bootstrap.CheckBox = function(config){
12436     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12437    
12438         this.addEvents({
12439             /**
12440             * @event check
12441             * Fires when the element is checked or unchecked.
12442             * @param {Roo.bootstrap.CheckBox} this This input
12443             * @param {Boolean} checked The new checked value
12444             */
12445            check : true
12446         });
12447 };
12448
12449 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12450     
12451     inputType: 'checkbox',
12452     inputValue: 1,
12453     valueOff: 0,
12454     boxLabel: false,
12455     checked: false,
12456     
12457     getAutoCreate : function()
12458     {
12459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12460         
12461         var id = Roo.id();
12462         
12463         var cfg = {};
12464         
12465         cfg.cls = 'form-group' //input-group
12466         
12467         var input =  {
12468             tag: 'input',
12469             id : id,
12470             type : this.inputType,
12471             value : (!this.checked) ? this.valueOff : this.inputValue,
12472             cls : 'form-box',
12473             placeholder : this.placeholder || ''
12474             
12475         };
12476         
12477         if (this.disabled) {
12478             input.disabled=true;
12479         }
12480         
12481         if(this.checked){
12482             input.checked = this.checked;
12483         }
12484         
12485         if (this.name) {
12486             input.name = this.name;
12487         }
12488         
12489         if (this.size) {
12490             input.cls += ' input-' + this.size;
12491         }
12492         
12493         var settings=this;
12494         ['xs','sm','md','lg'].map(function(size){
12495             if (settings[size]) {
12496                 cfg.cls += ' col-' + size + '-' + settings[size];
12497             }
12498         });
12499         
12500         var inputblock = input;
12501         
12502         if (this.before || this.after) {
12503             
12504             inputblock = {
12505                 cls : 'input-group',
12506                 cn :  [] 
12507             };
12508             if (this.before) {
12509                 inputblock.cn.push({
12510                     tag :'span',
12511                     cls : 'input-group-addon',
12512                     html : this.before
12513                 });
12514             }
12515             inputblock.cn.push(input);
12516             if (this.after) {
12517                 inputblock.cn.push({
12518                     tag :'span',
12519                     cls : 'input-group-addon',
12520                     html : this.after
12521                 });
12522             }
12523             
12524         };
12525         
12526         if (align ==='left' && this.fieldLabel.length) {
12527                 Roo.log("left and has label");
12528                 cfg.cn = [
12529                     
12530                     {
12531                         tag: 'label',
12532                         'for' :  id,
12533                         cls : 'control-label col-md-' + this.labelWidth,
12534                         html : this.fieldLabel
12535                         
12536                     },
12537                     {
12538                         cls : "col-md-" + (12 - this.labelWidth), 
12539                         cn: [
12540                             inputblock
12541                         ]
12542                     }
12543                     
12544                 ];
12545         } else if ( this.fieldLabel.length) {
12546                 Roo.log(" label");
12547                 cfg.cn = [
12548                    
12549                     {
12550                         tag: this.boxLabel ? 'span' : 'label',
12551                         'for': id,
12552                         cls: 'control-label box-input-label',
12553                         //cls : 'input-group-addon',
12554                         html : this.fieldLabel
12555                         
12556                     },
12557                     
12558                     inputblock
12559                     
12560                 ];
12561
12562         } else {
12563             
12564                    Roo.log(" no label && no align");
12565                 cfg.cn = [
12566                     
12567                         inputblock
12568                     
12569                 ];
12570                 
12571                 
12572         };
12573         
12574         if(this.boxLabel){
12575             cfg.cn.push({
12576                 tag: 'label',
12577                 'for': id,
12578                 cls: 'box-label',
12579                 html: this.boxLabel
12580             })
12581         }
12582         
12583         return cfg;
12584         
12585     },
12586     
12587     /**
12588      * return the real input element.
12589      */
12590     inputEl: function ()
12591     {
12592         return this.el.select('input.form-box',true).first();
12593     },
12594     
12595     initEvents : function()
12596     {
12597 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12598         
12599         this.inputEl().on('click', this.onClick,  this);
12600         
12601     },
12602     
12603     onClick : function()
12604     {   
12605         this.setChecked(!this.checked);
12606     },
12607     
12608     setChecked : function(state,suppressEvent)
12609     {
12610         this.checked = state;
12611         
12612         this.inputEl().dom.checked = state;
12613         
12614         if(suppressEvent !== true){
12615             this.fireEvent('check', this, state);
12616         }
12617         
12618         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12619         
12620     },
12621     
12622     setValue : function(v,suppressEvent)
12623     {
12624         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12625     }
12626     
12627 });
12628
12629  
12630 /*
12631  * - LGPL
12632  *
12633  * Radio
12634  * 
12635  */
12636
12637 /**
12638  * @class Roo.bootstrap.Radio
12639  * @extends Roo.bootstrap.CheckBox
12640  * Bootstrap Radio class
12641
12642  * @constructor
12643  * Create a new Radio
12644  * @param {Object} config The config object
12645  */
12646
12647 Roo.bootstrap.Radio = function(config){
12648     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12649    
12650 };
12651
12652 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12653     
12654     inputType: 'radio',
12655     inputValue: '',
12656     valueOff: '',
12657     
12658     getAutoCreate : function()
12659     {
12660         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12661         
12662         var id = Roo.id();
12663         
12664         var cfg = {};
12665         
12666         cfg.cls = 'form-group' //input-group
12667         
12668         var input =  {
12669             tag: 'input',
12670             id : id,
12671             type : this.inputType,
12672             value : (!this.checked) ? this.valueOff : this.inputValue,
12673             cls : 'form-box',
12674             placeholder : this.placeholder || ''
12675             
12676         };
12677         
12678         if (this.disabled) {
12679             input.disabled=true;
12680         }
12681         
12682         if(this.checked){
12683             input.checked = this.checked;
12684         }
12685         
12686         if (this.name) {
12687             input.name = this.name;
12688         }
12689         
12690         if (this.size) {
12691             input.cls += ' input-' + this.size;
12692         }
12693         
12694         var settings=this;
12695         ['xs','sm','md','lg'].map(function(size){
12696             if (settings[size]) {
12697                 cfg.cls += ' col-' + size + '-' + settings[size];
12698             }
12699         });
12700         
12701         var inputblock = input;
12702         
12703         if (this.before || this.after) {
12704             
12705             inputblock = {
12706                 cls : 'input-group',
12707                 cn :  [] 
12708             };
12709             if (this.before) {
12710                 inputblock.cn.push({
12711                     tag :'span',
12712                     cls : 'input-group-addon',
12713                     html : this.before
12714                 });
12715             }
12716             inputblock.cn.push(input);
12717             if (this.after) {
12718                 inputblock.cn.push({
12719                     tag :'span',
12720                     cls : 'input-group-addon',
12721                     html : this.after
12722                 });
12723             }
12724             
12725         };
12726         
12727         if (align ==='left' && this.fieldLabel.length) {
12728                 Roo.log("left and has label");
12729                 cfg.cn = [
12730                     
12731                     {
12732                         tag: 'label',
12733                         'for' :  id,
12734                         cls : 'control-label col-md-' + this.labelWidth,
12735                         html : this.fieldLabel
12736                         
12737                     },
12738                     {
12739                         cls : "col-md-" + (12 - this.labelWidth), 
12740                         cn: [
12741                             inputblock
12742                         ]
12743                     }
12744                     
12745                 ];
12746         } else if ( this.fieldLabel.length) {
12747                 Roo.log(" label");
12748                  cfg.cn = [
12749                    
12750                     {
12751                         tag: 'label',
12752                         'for': id,
12753                         cls: 'control-label box-input-label',
12754                         //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756                         
12757                     },
12758                     
12759                     inputblock
12760                     
12761                 ];
12762
12763         } else {
12764             
12765                    Roo.log(" no label && no align");
12766                 cfg.cn = [
12767                     
12768                         inputblock
12769                     
12770                 ];
12771                 
12772                 
12773         };
12774         
12775         if(this.boxLabel){
12776             cfg.cn.push({
12777                 tag: 'label',
12778                 'for': id,
12779                 cls: 'box-label',
12780                 html: this.boxLabel
12781             })
12782         }
12783         
12784         return cfg;
12785         
12786     },
12787    
12788     onClick : function()
12789     {   
12790         this.setChecked(true);
12791     },
12792     
12793     setChecked : function(state,suppressEvent)
12794     {
12795         if(state){
12796             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12797                 v.dom.checked = false;
12798             });
12799         }
12800         
12801         this.checked = state;
12802         this.inputEl().dom.checked = state;
12803         
12804         if(suppressEvent !== true){
12805             this.fireEvent('check', this, state);
12806         }
12807         
12808         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12809         
12810     },
12811     
12812     getGroupValue : function()
12813     {
12814         var value = ''
12815         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12816             if(v.dom.checked == true){
12817                 value = v.dom.value;
12818             }
12819         });
12820         
12821         return value;
12822     },
12823     
12824     /**
12825      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12826      * @return {Mixed} value The field value
12827      */
12828     getValue : function(){
12829         return this.getGroupValue();
12830     }
12831     
12832 });
12833
12834  
12835 //<script type="text/javascript">
12836
12837 /*
12838  * Based  Ext JS Library 1.1.1
12839  * Copyright(c) 2006-2007, Ext JS, LLC.
12840  * LGPL
12841  *
12842  */
12843  
12844 /**
12845  * @class Roo.HtmlEditorCore
12846  * @extends Roo.Component
12847  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12848  *
12849  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12850  */
12851
12852 Roo.HtmlEditorCore = function(config){
12853     
12854     
12855     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12856     this.addEvents({
12857         /**
12858          * @event initialize
12859          * Fires when the editor is fully initialized (including the iframe)
12860          * @param {Roo.HtmlEditorCore} this
12861          */
12862         initialize: true,
12863         /**
12864          * @event activate
12865          * Fires when the editor is first receives the focus. Any insertion must wait
12866          * until after this event.
12867          * @param {Roo.HtmlEditorCore} this
12868          */
12869         activate: true,
12870          /**
12871          * @event beforesync
12872          * Fires before the textarea is updated with content from the editor iframe. Return false
12873          * to cancel the sync.
12874          * @param {Roo.HtmlEditorCore} this
12875          * @param {String} html
12876          */
12877         beforesync: true,
12878          /**
12879          * @event beforepush
12880          * Fires before the iframe editor is updated with content from the textarea. Return false
12881          * to cancel the push.
12882          * @param {Roo.HtmlEditorCore} this
12883          * @param {String} html
12884          */
12885         beforepush: true,
12886          /**
12887          * @event sync
12888          * Fires when the textarea is updated with content from the editor iframe.
12889          * @param {Roo.HtmlEditorCore} this
12890          * @param {String} html
12891          */
12892         sync: true,
12893          /**
12894          * @event push
12895          * Fires when the iframe editor is updated with content from the textarea.
12896          * @param {Roo.HtmlEditorCore} this
12897          * @param {String} html
12898          */
12899         push: true,
12900         
12901         /**
12902          * @event editorevent
12903          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12904          * @param {Roo.HtmlEditorCore} this
12905          */
12906         editorevent: true
12907     });
12908      
12909 };
12910
12911
12912 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12913
12914
12915      /**
12916      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12917      */
12918     
12919     owner : false,
12920     
12921      /**
12922      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12923      *                        Roo.resizable.
12924      */
12925     resizable : false,
12926      /**
12927      * @cfg {Number} height (in pixels)
12928      */   
12929     height: 300,
12930    /**
12931      * @cfg {Number} width (in pixels)
12932      */   
12933     width: 500,
12934     
12935     /**
12936      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12937      * 
12938      */
12939     stylesheets: false,
12940     
12941     // id of frame..
12942     frameId: false,
12943     
12944     // private properties
12945     validationEvent : false,
12946     deferHeight: true,
12947     initialized : false,
12948     activated : false,
12949     sourceEditMode : false,
12950     onFocus : Roo.emptyFn,
12951     iframePad:3,
12952     hideMode:'offsets',
12953     
12954     clearUp: true,
12955     
12956      
12957     
12958
12959     /**
12960      * Protected method that will not generally be called directly. It
12961      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12962      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12963      */
12964     getDocMarkup : function(){
12965         // body styles..
12966         var st = '';
12967         Roo.log(this.stylesheets);
12968         
12969         // inherit styels from page...?? 
12970         if (this.stylesheets === false) {
12971             
12972             Roo.get(document.head).select('style').each(function(node) {
12973                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12974             });
12975             
12976             Roo.get(document.head).select('link').each(function(node) { 
12977                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12978             });
12979             
12980         } else if (!this.stylesheets.length) {
12981                 // simple..
12982                 st = '<style type="text/css">' +
12983                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12984                    '</style>';
12985         } else {
12986             Roo.each(this.stylesheets, function(s) {
12987                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12988             });
12989             
12990         }
12991         
12992         st +=  '<style type="text/css">' +
12993             'IMG { cursor: pointer } ' +
12994         '</style>';
12995
12996         
12997         return '<html><head>' + st  +
12998             //<style type="text/css">' +
12999             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13000             //'</style>' +
13001             ' </head><body class="roo-htmleditor-body"></body></html>';
13002     },
13003
13004     // private
13005     onRender : function(ct, position)
13006     {
13007         var _t = this;
13008         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13009         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13010         
13011         
13012         this.el.dom.style.border = '0 none';
13013         this.el.dom.setAttribute('tabIndex', -1);
13014         this.el.addClass('x-hidden hide');
13015         
13016         
13017         
13018         if(Roo.isIE){ // fix IE 1px bogus margin
13019             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13020         }
13021        
13022         
13023         this.frameId = Roo.id();
13024         
13025          
13026         
13027         var iframe = this.owner.wrap.createChild({
13028             tag: 'iframe',
13029             cls: 'form-control', // bootstrap..
13030             id: this.frameId,
13031             name: this.frameId,
13032             frameBorder : 'no',
13033             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13034         }, this.el
13035         );
13036         
13037         
13038         this.iframe = iframe.dom;
13039
13040          this.assignDocWin();
13041         
13042         this.doc.designMode = 'on';
13043        
13044         this.doc.open();
13045         this.doc.write(this.getDocMarkup());
13046         this.doc.close();
13047
13048         
13049         var task = { // must defer to wait for browser to be ready
13050             run : function(){
13051                 //console.log("run task?" + this.doc.readyState);
13052                 this.assignDocWin();
13053                 if(this.doc.body || this.doc.readyState == 'complete'){
13054                     try {
13055                         this.doc.designMode="on";
13056                     } catch (e) {
13057                         return;
13058                     }
13059                     Roo.TaskMgr.stop(task);
13060                     this.initEditor.defer(10, this);
13061                 }
13062             },
13063             interval : 10,
13064             duration: 10000,
13065             scope: this
13066         };
13067         Roo.TaskMgr.start(task);
13068
13069         
13070          
13071     },
13072
13073     // private
13074     onResize : function(w, h)
13075     {
13076          Roo.log('resize: ' +w + ',' + h );
13077         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13078         if(!this.iframe){
13079             return;
13080         }
13081         if(typeof w == 'number'){
13082             
13083             this.iframe.style.width = w + 'px';
13084         }
13085         if(typeof h == 'number'){
13086             
13087             this.iframe.style.height = h + 'px';
13088             if(this.doc){
13089                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13090             }
13091         }
13092         
13093     },
13094
13095     /**
13096      * Toggles the editor between standard and source edit mode.
13097      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13098      */
13099     toggleSourceEdit : function(sourceEditMode){
13100         
13101         this.sourceEditMode = sourceEditMode === true;
13102         
13103         if(this.sourceEditMode){
13104  
13105             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13106             
13107         }else{
13108             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13109             //this.iframe.className = '';
13110             this.deferFocus();
13111         }
13112         //this.setSize(this.owner.wrap.getSize());
13113         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13114     },
13115
13116     
13117   
13118
13119     /**
13120      * Protected method that will not generally be called directly. If you need/want
13121      * custom HTML cleanup, this is the method you should override.
13122      * @param {String} html The HTML to be cleaned
13123      * return {String} The cleaned HTML
13124      */
13125     cleanHtml : function(html){
13126         html = String(html);
13127         if(html.length > 5){
13128             if(Roo.isSafari){ // strip safari nonsense
13129                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13130             }
13131         }
13132         if(html == '&nbsp;'){
13133             html = '';
13134         }
13135         return html;
13136     },
13137
13138     /**
13139      * HTML Editor -> Textarea
13140      * Protected method that will not generally be called directly. Syncs the contents
13141      * of the editor iframe with the textarea.
13142      */
13143     syncValue : function(){
13144         if(this.initialized){
13145             var bd = (this.doc.body || this.doc.documentElement);
13146             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13147             var html = bd.innerHTML;
13148             if(Roo.isSafari){
13149                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13150                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13151                 if(m && m[1]){
13152                     html = '<div style="'+m[0]+'">' + html + '</div>';
13153                 }
13154             }
13155             html = this.cleanHtml(html);
13156             // fix up the special chars.. normaly like back quotes in word...
13157             // however we do not want to do this with chinese..
13158             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13159                 var cc = b.charCodeAt();
13160                 if (
13161                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13162                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13163                     (cc >= 0xf900 && cc < 0xfb00 )
13164                 ) {
13165                         return b;
13166                 }
13167                 return "&#"+cc+";" 
13168             });
13169             if(this.owner.fireEvent('beforesync', this, html) !== false){
13170                 this.el.dom.value = html;
13171                 this.owner.fireEvent('sync', this, html);
13172             }
13173         }
13174     },
13175
13176     /**
13177      * Protected method that will not generally be called directly. Pushes the value of the textarea
13178      * into the iframe editor.
13179      */
13180     pushValue : function(){
13181         if(this.initialized){
13182             var v = this.el.dom.value.trim();
13183             
13184 //            if(v.length < 1){
13185 //                v = '&#160;';
13186 //            }
13187             
13188             if(this.owner.fireEvent('beforepush', this, v) !== false){
13189                 var d = (this.doc.body || this.doc.documentElement);
13190                 d.innerHTML = v;
13191                 this.cleanUpPaste();
13192                 this.el.dom.value = d.innerHTML;
13193                 this.owner.fireEvent('push', this, v);
13194             }
13195         }
13196     },
13197
13198     // private
13199     deferFocus : function(){
13200         this.focus.defer(10, this);
13201     },
13202
13203     // doc'ed in Field
13204     focus : function(){
13205         if(this.win && !this.sourceEditMode){
13206             this.win.focus();
13207         }else{
13208             this.el.focus();
13209         }
13210     },
13211     
13212     assignDocWin: function()
13213     {
13214         var iframe = this.iframe;
13215         
13216          if(Roo.isIE){
13217             this.doc = iframe.contentWindow.document;
13218             this.win = iframe.contentWindow;
13219         } else {
13220             if (!Roo.get(this.frameId)) {
13221                 return;
13222             }
13223             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13224             this.win = Roo.get(this.frameId).dom.contentWindow;
13225         }
13226     },
13227     
13228     // private
13229     initEditor : function(){
13230         //console.log("INIT EDITOR");
13231         this.assignDocWin();
13232         
13233         
13234         
13235         this.doc.designMode="on";
13236         this.doc.open();
13237         this.doc.write(this.getDocMarkup());
13238         this.doc.close();
13239         
13240         var dbody = (this.doc.body || this.doc.documentElement);
13241         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13242         // this copies styles from the containing element into thsi one..
13243         // not sure why we need all of this..
13244         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13245         ss['background-attachment'] = 'fixed'; // w3c
13246         dbody.bgProperties = 'fixed'; // ie
13247         Roo.DomHelper.applyStyles(dbody, ss);
13248         Roo.EventManager.on(this.doc, {
13249             //'mousedown': this.onEditorEvent,
13250             'mouseup': this.onEditorEvent,
13251             'dblclick': this.onEditorEvent,
13252             'click': this.onEditorEvent,
13253             'keyup': this.onEditorEvent,
13254             buffer:100,
13255             scope: this
13256         });
13257         if(Roo.isGecko){
13258             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13259         }
13260         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13261             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13262         }
13263         this.initialized = true;
13264
13265         this.owner.fireEvent('initialize', this);
13266         this.pushValue();
13267     },
13268
13269     // private
13270     onDestroy : function(){
13271         
13272         
13273         
13274         if(this.rendered){
13275             
13276             //for (var i =0; i < this.toolbars.length;i++) {
13277             //    // fixme - ask toolbars for heights?
13278             //    this.toolbars[i].onDestroy();
13279            // }
13280             
13281             //this.wrap.dom.innerHTML = '';
13282             //this.wrap.remove();
13283         }
13284     },
13285
13286     // private
13287     onFirstFocus : function(){
13288         
13289         this.assignDocWin();
13290         
13291         
13292         this.activated = true;
13293          
13294     
13295         if(Roo.isGecko){ // prevent silly gecko errors
13296             this.win.focus();
13297             var s = this.win.getSelection();
13298             if(!s.focusNode || s.focusNode.nodeType != 3){
13299                 var r = s.getRangeAt(0);
13300                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13301                 r.collapse(true);
13302                 this.deferFocus();
13303             }
13304             try{
13305                 this.execCmd('useCSS', true);
13306                 this.execCmd('styleWithCSS', false);
13307             }catch(e){}
13308         }
13309         this.owner.fireEvent('activate', this);
13310     },
13311
13312     // private
13313     adjustFont: function(btn){
13314         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13315         //if(Roo.isSafari){ // safari
13316         //    adjust *= 2;
13317        // }
13318         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13319         if(Roo.isSafari){ // safari
13320             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13321             v =  (v < 10) ? 10 : v;
13322             v =  (v > 48) ? 48 : v;
13323             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13324             
13325         }
13326         
13327         
13328         v = Math.max(1, v+adjust);
13329         
13330         this.execCmd('FontSize', v  );
13331     },
13332
13333     onEditorEvent : function(e){
13334         this.owner.fireEvent('editorevent', this, e);
13335       //  this.updateToolbar();
13336         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13337     },
13338
13339     insertTag : function(tg)
13340     {
13341         // could be a bit smarter... -> wrap the current selected tRoo..
13342         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13343             
13344             range = this.createRange(this.getSelection());
13345             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13346             wrappingNode.appendChild(range.extractContents());
13347             range.insertNode(wrappingNode);
13348
13349             return;
13350             
13351             
13352             
13353         }
13354         this.execCmd("formatblock",   tg);
13355         
13356     },
13357     
13358     insertText : function(txt)
13359     {
13360         
13361         
13362         var range = this.createRange();
13363         range.deleteContents();
13364                //alert(Sender.getAttribute('label'));
13365                
13366         range.insertNode(this.doc.createTextNode(txt));
13367     } ,
13368     
13369      
13370
13371     /**
13372      * Executes a Midas editor command on the editor document and performs necessary focus and
13373      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13374      * @param {String} cmd The Midas command
13375      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13376      */
13377     relayCmd : function(cmd, value){
13378         this.win.focus();
13379         this.execCmd(cmd, value);
13380         this.owner.fireEvent('editorevent', this);
13381         //this.updateToolbar();
13382         this.owner.deferFocus();
13383     },
13384
13385     /**
13386      * Executes a Midas editor command directly on the editor document.
13387      * For visual commands, you should use {@link #relayCmd} instead.
13388      * <b>This should only be called after the editor is initialized.</b>
13389      * @param {String} cmd The Midas command
13390      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13391      */
13392     execCmd : function(cmd, value){
13393         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13394         this.syncValue();
13395     },
13396  
13397  
13398    
13399     /**
13400      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13401      * to insert tRoo.
13402      * @param {String} text | dom node.. 
13403      */
13404     insertAtCursor : function(text)
13405     {
13406         
13407         
13408         
13409         if(!this.activated){
13410             return;
13411         }
13412         /*
13413         if(Roo.isIE){
13414             this.win.focus();
13415             var r = this.doc.selection.createRange();
13416             if(r){
13417                 r.collapse(true);
13418                 r.pasteHTML(text);
13419                 this.syncValue();
13420                 this.deferFocus();
13421             
13422             }
13423             return;
13424         }
13425         */
13426         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13427             this.win.focus();
13428             
13429             
13430             // from jquery ui (MIT licenced)
13431             var range, node;
13432             var win = this.win;
13433             
13434             if (win.getSelection && win.getSelection().getRangeAt) {
13435                 range = win.getSelection().getRangeAt(0);
13436                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13437                 range.insertNode(node);
13438             } else if (win.document.selection && win.document.selection.createRange) {
13439                 // no firefox support
13440                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13441                 win.document.selection.createRange().pasteHTML(txt);
13442             } else {
13443                 // no firefox support
13444                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13445                 this.execCmd('InsertHTML', txt);
13446             } 
13447             
13448             this.syncValue();
13449             
13450             this.deferFocus();
13451         }
13452     },
13453  // private
13454     mozKeyPress : function(e){
13455         if(e.ctrlKey){
13456             var c = e.getCharCode(), cmd;
13457           
13458             if(c > 0){
13459                 c = String.fromCharCode(c).toLowerCase();
13460                 switch(c){
13461                     case 'b':
13462                         cmd = 'bold';
13463                         break;
13464                     case 'i':
13465                         cmd = 'italic';
13466                         break;
13467                     
13468                     case 'u':
13469                         cmd = 'underline';
13470                         break;
13471                     
13472                     case 'v':
13473                         this.cleanUpPaste.defer(100, this);
13474                         return;
13475                         
13476                 }
13477                 if(cmd){
13478                     this.win.focus();
13479                     this.execCmd(cmd);
13480                     this.deferFocus();
13481                     e.preventDefault();
13482                 }
13483                 
13484             }
13485         }
13486     },
13487
13488     // private
13489     fixKeys : function(){ // load time branching for fastest keydown performance
13490         if(Roo.isIE){
13491             return function(e){
13492                 var k = e.getKey(), r;
13493                 if(k == e.TAB){
13494                     e.stopEvent();
13495                     r = this.doc.selection.createRange();
13496                     if(r){
13497                         r.collapse(true);
13498                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13499                         this.deferFocus();
13500                     }
13501                     return;
13502                 }
13503                 
13504                 if(k == e.ENTER){
13505                     r = this.doc.selection.createRange();
13506                     if(r){
13507                         var target = r.parentElement();
13508                         if(!target || target.tagName.toLowerCase() != 'li'){
13509                             e.stopEvent();
13510                             r.pasteHTML('<br />');
13511                             r.collapse(false);
13512                             r.select();
13513                         }
13514                     }
13515                 }
13516                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13517                     this.cleanUpPaste.defer(100, this);
13518                     return;
13519                 }
13520                 
13521                 
13522             };
13523         }else if(Roo.isOpera){
13524             return function(e){
13525                 var k = e.getKey();
13526                 if(k == e.TAB){
13527                     e.stopEvent();
13528                     this.win.focus();
13529                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13530                     this.deferFocus();
13531                 }
13532                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13533                     this.cleanUpPaste.defer(100, this);
13534                     return;
13535                 }
13536                 
13537             };
13538         }else if(Roo.isSafari){
13539             return function(e){
13540                 var k = e.getKey();
13541                 
13542                 if(k == e.TAB){
13543                     e.stopEvent();
13544                     this.execCmd('InsertText','\t');
13545                     this.deferFocus();
13546                     return;
13547                 }
13548                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13549                     this.cleanUpPaste.defer(100, this);
13550                     return;
13551                 }
13552                 
13553              };
13554         }
13555     }(),
13556     
13557     getAllAncestors: function()
13558     {
13559         var p = this.getSelectedNode();
13560         var a = [];
13561         if (!p) {
13562             a.push(p); // push blank onto stack..
13563             p = this.getParentElement();
13564         }
13565         
13566         
13567         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13568             a.push(p);
13569             p = p.parentNode;
13570         }
13571         a.push(this.doc.body);
13572         return a;
13573     },
13574     lastSel : false,
13575     lastSelNode : false,
13576     
13577     
13578     getSelection : function() 
13579     {
13580         this.assignDocWin();
13581         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13582     },
13583     
13584     getSelectedNode: function() 
13585     {
13586         // this may only work on Gecko!!!
13587         
13588         // should we cache this!!!!
13589         
13590         
13591         
13592          
13593         var range = this.createRange(this.getSelection()).cloneRange();
13594         
13595         if (Roo.isIE) {
13596             var parent = range.parentElement();
13597             while (true) {
13598                 var testRange = range.duplicate();
13599                 testRange.moveToElementText(parent);
13600                 if (testRange.inRange(range)) {
13601                     break;
13602                 }
13603                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13604                     break;
13605                 }
13606                 parent = parent.parentElement;
13607             }
13608             return parent;
13609         }
13610         
13611         // is ancestor a text element.
13612         var ac =  range.commonAncestorContainer;
13613         if (ac.nodeType == 3) {
13614             ac = ac.parentNode;
13615         }
13616         
13617         var ar = ac.childNodes;
13618          
13619         var nodes = [];
13620         var other_nodes = [];
13621         var has_other_nodes = false;
13622         for (var i=0;i<ar.length;i++) {
13623             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13624                 continue;
13625             }
13626             // fullly contained node.
13627             
13628             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13629                 nodes.push(ar[i]);
13630                 continue;
13631             }
13632             
13633             // probably selected..
13634             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13635                 other_nodes.push(ar[i]);
13636                 continue;
13637             }
13638             // outer..
13639             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13640                 continue;
13641             }
13642             
13643             
13644             has_other_nodes = true;
13645         }
13646         if (!nodes.length && other_nodes.length) {
13647             nodes= other_nodes;
13648         }
13649         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13650             return false;
13651         }
13652         
13653         return nodes[0];
13654     },
13655     createRange: function(sel)
13656     {
13657         // this has strange effects when using with 
13658         // top toolbar - not sure if it's a great idea.
13659         //this.editor.contentWindow.focus();
13660         if (typeof sel != "undefined") {
13661             try {
13662                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13663             } catch(e) {
13664                 return this.doc.createRange();
13665             }
13666         } else {
13667             return this.doc.createRange();
13668         }
13669     },
13670     getParentElement: function()
13671     {
13672         
13673         this.assignDocWin();
13674         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13675         
13676         var range = this.createRange(sel);
13677          
13678         try {
13679             var p = range.commonAncestorContainer;
13680             while (p.nodeType == 3) { // text node
13681                 p = p.parentNode;
13682             }
13683             return p;
13684         } catch (e) {
13685             return null;
13686         }
13687     
13688     },
13689     /***
13690      *
13691      * Range intersection.. the hard stuff...
13692      *  '-1' = before
13693      *  '0' = hits..
13694      *  '1' = after.
13695      *         [ -- selected range --- ]
13696      *   [fail]                        [fail]
13697      *
13698      *    basically..
13699      *      if end is before start or  hits it. fail.
13700      *      if start is after end or hits it fail.
13701      *
13702      *   if either hits (but other is outside. - then it's not 
13703      *   
13704      *    
13705      **/
13706     
13707     
13708     // @see http://www.thismuchiknow.co.uk/?p=64.
13709     rangeIntersectsNode : function(range, node)
13710     {
13711         var nodeRange = node.ownerDocument.createRange();
13712         try {
13713             nodeRange.selectNode(node);
13714         } catch (e) {
13715             nodeRange.selectNodeContents(node);
13716         }
13717     
13718         var rangeStartRange = range.cloneRange();
13719         rangeStartRange.collapse(true);
13720     
13721         var rangeEndRange = range.cloneRange();
13722         rangeEndRange.collapse(false);
13723     
13724         var nodeStartRange = nodeRange.cloneRange();
13725         nodeStartRange.collapse(true);
13726     
13727         var nodeEndRange = nodeRange.cloneRange();
13728         nodeEndRange.collapse(false);
13729     
13730         return rangeStartRange.compareBoundaryPoints(
13731                  Range.START_TO_START, nodeEndRange) == -1 &&
13732                rangeEndRange.compareBoundaryPoints(
13733                  Range.START_TO_START, nodeStartRange) == 1;
13734         
13735          
13736     },
13737     rangeCompareNode : function(range, node)
13738     {
13739         var nodeRange = node.ownerDocument.createRange();
13740         try {
13741             nodeRange.selectNode(node);
13742         } catch (e) {
13743             nodeRange.selectNodeContents(node);
13744         }
13745         
13746         
13747         range.collapse(true);
13748     
13749         nodeRange.collapse(true);
13750      
13751         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13752         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13753          
13754         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13755         
13756         var nodeIsBefore   =  ss == 1;
13757         var nodeIsAfter    = ee == -1;
13758         
13759         if (nodeIsBefore && nodeIsAfter)
13760             return 0; // outer
13761         if (!nodeIsBefore && nodeIsAfter)
13762             return 1; //right trailed.
13763         
13764         if (nodeIsBefore && !nodeIsAfter)
13765             return 2;  // left trailed.
13766         // fully contined.
13767         return 3;
13768     },
13769
13770     // private? - in a new class?
13771     cleanUpPaste :  function()
13772     {
13773         // cleans up the whole document..
13774         Roo.log('cleanuppaste');
13775         
13776         this.cleanUpChildren(this.doc.body);
13777         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13778         if (clean != this.doc.body.innerHTML) {
13779             this.doc.body.innerHTML = clean;
13780         }
13781         
13782     },
13783     
13784     cleanWordChars : function(input) {// change the chars to hex code
13785         var he = Roo.HtmlEditorCore;
13786         
13787         var output = input;
13788         Roo.each(he.swapCodes, function(sw) { 
13789             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13790             
13791             output = output.replace(swapper, sw[1]);
13792         });
13793         
13794         return output;
13795     },
13796     
13797     
13798     cleanUpChildren : function (n)
13799     {
13800         if (!n.childNodes.length) {
13801             return;
13802         }
13803         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13804            this.cleanUpChild(n.childNodes[i]);
13805         }
13806     },
13807     
13808     
13809         
13810     
13811     cleanUpChild : function (node)
13812     {
13813         var ed = this;
13814         //console.log(node);
13815         if (node.nodeName == "#text") {
13816             // clean up silly Windows -- stuff?
13817             return; 
13818         }
13819         if (node.nodeName == "#comment") {
13820             node.parentNode.removeChild(node);
13821             // clean up silly Windows -- stuff?
13822             return; 
13823         }
13824         
13825         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13826             // remove node.
13827             node.parentNode.removeChild(node);
13828             return;
13829             
13830         }
13831         
13832         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13833         
13834         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13835         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13836         
13837         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13838         //    remove_keep_children = true;
13839         //}
13840         
13841         if (remove_keep_children) {
13842             this.cleanUpChildren(node);
13843             // inserts everything just before this node...
13844             while (node.childNodes.length) {
13845                 var cn = node.childNodes[0];
13846                 node.removeChild(cn);
13847                 node.parentNode.insertBefore(cn, node);
13848             }
13849             node.parentNode.removeChild(node);
13850             return;
13851         }
13852         
13853         if (!node.attributes || !node.attributes.length) {
13854             this.cleanUpChildren(node);
13855             return;
13856         }
13857         
13858         function cleanAttr(n,v)
13859         {
13860             
13861             if (v.match(/^\./) || v.match(/^\//)) {
13862                 return;
13863             }
13864             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13865                 return;
13866             }
13867             if (v.match(/^#/)) {
13868                 return;
13869             }
13870 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13871             node.removeAttribute(n);
13872             
13873         }
13874         
13875         function cleanStyle(n,v)
13876         {
13877             if (v.match(/expression/)) { //XSS?? should we even bother..
13878                 node.removeAttribute(n);
13879                 return;
13880             }
13881             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13882             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13883             
13884             
13885             var parts = v.split(/;/);
13886             var clean = [];
13887             
13888             Roo.each(parts, function(p) {
13889                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13890                 if (!p.length) {
13891                     return true;
13892                 }
13893                 var l = p.split(':').shift().replace(/\s+/g,'');
13894                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13895                 
13896                 if ( cblack.indexOf(l) > -1) {
13897 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13898                     //node.removeAttribute(n);
13899                     return true;
13900                 }
13901                 //Roo.log()
13902                 // only allow 'c whitelisted system attributes'
13903                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13904 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13905                     //node.removeAttribute(n);
13906                     return true;
13907                 }
13908                 
13909                 
13910                  
13911                 
13912                 clean.push(p);
13913                 return true;
13914             });
13915             if (clean.length) { 
13916                 node.setAttribute(n, clean.join(';'));
13917             } else {
13918                 node.removeAttribute(n);
13919             }
13920             
13921         }
13922         
13923         
13924         for (var i = node.attributes.length-1; i > -1 ; i--) {
13925             var a = node.attributes[i];
13926             //console.log(a);
13927             
13928             if (a.name.toLowerCase().substr(0,2)=='on')  {
13929                 node.removeAttribute(a.name);
13930                 continue;
13931             }
13932             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13933                 node.removeAttribute(a.name);
13934                 continue;
13935             }
13936             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13937                 cleanAttr(a.name,a.value); // fixme..
13938                 continue;
13939             }
13940             if (a.name == 'style') {
13941                 cleanStyle(a.name,a.value);
13942                 continue;
13943             }
13944             /// clean up MS crap..
13945             // tecnically this should be a list of valid class'es..
13946             
13947             
13948             if (a.name == 'class') {
13949                 if (a.value.match(/^Mso/)) {
13950                     node.className = '';
13951                 }
13952                 
13953                 if (a.value.match(/body/)) {
13954                     node.className = '';
13955                 }
13956                 continue;
13957             }
13958             
13959             // style cleanup!?
13960             // class cleanup?
13961             
13962         }
13963         
13964         
13965         this.cleanUpChildren(node);
13966         
13967         
13968     }
13969     
13970     
13971     // hide stuff that is not compatible
13972     /**
13973      * @event blur
13974      * @hide
13975      */
13976     /**
13977      * @event change
13978      * @hide
13979      */
13980     /**
13981      * @event focus
13982      * @hide
13983      */
13984     /**
13985      * @event specialkey
13986      * @hide
13987      */
13988     /**
13989      * @cfg {String} fieldClass @hide
13990      */
13991     /**
13992      * @cfg {String} focusClass @hide
13993      */
13994     /**
13995      * @cfg {String} autoCreate @hide
13996      */
13997     /**
13998      * @cfg {String} inputType @hide
13999      */
14000     /**
14001      * @cfg {String} invalidClass @hide
14002      */
14003     /**
14004      * @cfg {String} invalidText @hide
14005      */
14006     /**
14007      * @cfg {String} msgFx @hide
14008      */
14009     /**
14010      * @cfg {String} validateOnBlur @hide
14011      */
14012 });
14013
14014 Roo.HtmlEditorCore.white = [
14015         'area', 'br', 'img', 'input', 'hr', 'wbr',
14016         
14017        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14018        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14019        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14020        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14021        'table',   'ul',         'xmp', 
14022        
14023        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14024       'thead',   'tr', 
14025      
14026       'dir', 'menu', 'ol', 'ul', 'dl',
14027        
14028       'embed',  'object'
14029 ];
14030
14031
14032 Roo.HtmlEditorCore.black = [
14033     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14034         'applet', // 
14035         'base',   'basefont', 'bgsound', 'blink',  'body', 
14036         'frame',  'frameset', 'head',    'html',   'ilayer', 
14037         'iframe', 'layer',  'link',     'meta',    'object',   
14038         'script', 'style' ,'title',  'xml' // clean later..
14039 ];
14040 Roo.HtmlEditorCore.clean = [
14041     'script', 'style', 'title', 'xml'
14042 ];
14043 Roo.HtmlEditorCore.remove = [
14044     'font'
14045 ];
14046 // attributes..
14047
14048 Roo.HtmlEditorCore.ablack = [
14049     'on'
14050 ];
14051     
14052 Roo.HtmlEditorCore.aclean = [ 
14053     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14054 ];
14055
14056 // protocols..
14057 Roo.HtmlEditorCore.pwhite= [
14058         'http',  'https',  'mailto'
14059 ];
14060
14061 // white listed style attributes.
14062 Roo.HtmlEditorCore.cwhite= [
14063       //  'text-align', /// default is to allow most things..
14064       
14065          
14066 //        'font-size'//??
14067 ];
14068
14069 // black listed style attributes.
14070 Roo.HtmlEditorCore.cblack= [
14071       //  'font-size' -- this can be set by the project 
14072 ];
14073
14074
14075 Roo.HtmlEditorCore.swapCodes   =[ 
14076     [    8211, "--" ], 
14077     [    8212, "--" ], 
14078     [    8216,  "'" ],  
14079     [    8217, "'" ],  
14080     [    8220, '"' ],  
14081     [    8221, '"' ],  
14082     [    8226, "*" ],  
14083     [    8230, "..." ]
14084 ]; 
14085
14086     /*
14087  * - LGPL
14088  *
14089  * HtmlEditor
14090  * 
14091  */
14092
14093 /**
14094  * @class Roo.bootstrap.HtmlEditor
14095  * @extends Roo.bootstrap.TextArea
14096  * Bootstrap HtmlEditor class
14097
14098  * @constructor
14099  * Create a new HtmlEditor
14100  * @param {Object} config The config object
14101  */
14102
14103 Roo.bootstrap.HtmlEditor = function(config){
14104     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14105     if (!this.toolbars) {
14106         this.toolbars = [];
14107     }
14108     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14109     this.addEvents({
14110             /**
14111              * @event initialize
14112              * Fires when the editor is fully initialized (including the iframe)
14113              * @param {HtmlEditor} this
14114              */
14115             initialize: true,
14116             /**
14117              * @event activate
14118              * Fires when the editor is first receives the focus. Any insertion must wait
14119              * until after this event.
14120              * @param {HtmlEditor} this
14121              */
14122             activate: true,
14123              /**
14124              * @event beforesync
14125              * Fires before the textarea is updated with content from the editor iframe. Return false
14126              * to cancel the sync.
14127              * @param {HtmlEditor} this
14128              * @param {String} html
14129              */
14130             beforesync: true,
14131              /**
14132              * @event beforepush
14133              * Fires before the iframe editor is updated with content from the textarea. Return false
14134              * to cancel the push.
14135              * @param {HtmlEditor} this
14136              * @param {String} html
14137              */
14138             beforepush: true,
14139              /**
14140              * @event sync
14141              * Fires when the textarea is updated with content from the editor iframe.
14142              * @param {HtmlEditor} this
14143              * @param {String} html
14144              */
14145             sync: true,
14146              /**
14147              * @event push
14148              * Fires when the iframe editor is updated with content from the textarea.
14149              * @param {HtmlEditor} this
14150              * @param {String} html
14151              */
14152             push: true,
14153              /**
14154              * @event editmodechange
14155              * Fires when the editor switches edit modes
14156              * @param {HtmlEditor} this
14157              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14158              */
14159             editmodechange: true,
14160             /**
14161              * @event editorevent
14162              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14163              * @param {HtmlEditor} this
14164              */
14165             editorevent: true,
14166             /**
14167              * @event firstfocus
14168              * Fires when on first focus - needed by toolbars..
14169              * @param {HtmlEditor} this
14170              */
14171             firstfocus: true,
14172             /**
14173              * @event autosave
14174              * Auto save the htmlEditor value as a file into Events
14175              * @param {HtmlEditor} this
14176              */
14177             autosave: true,
14178             /**
14179              * @event savedpreview
14180              * preview the saved version of htmlEditor
14181              * @param {HtmlEditor} this
14182              */
14183             savedpreview: true
14184         });
14185 };
14186
14187
14188 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14189     
14190     
14191       /**
14192      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14193      */
14194     toolbars : false,
14195    
14196      /**
14197      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14198      *                        Roo.resizable.
14199      */
14200     resizable : false,
14201      /**
14202      * @cfg {Number} height (in pixels)
14203      */   
14204     height: 300,
14205    /**
14206      * @cfg {Number} width (in pixels)
14207      */   
14208     width: false,
14209     
14210     /**
14211      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14212      * 
14213      */
14214     stylesheets: false,
14215     
14216     // id of frame..
14217     frameId: false,
14218     
14219     // private properties
14220     validationEvent : false,
14221     deferHeight: true,
14222     initialized : false,
14223     activated : false,
14224     
14225     onFocus : Roo.emptyFn,
14226     iframePad:3,
14227     hideMode:'offsets',
14228     
14229     
14230     tbContainer : false,
14231     
14232     toolbarContainer :function() {
14233         return this.wrap.select('.x-html-editor-tb',true).first();
14234     },
14235
14236     /**
14237      * Protected method that will not generally be called directly. It
14238      * is called when the editor creates its toolbar. Override this method if you need to
14239      * add custom toolbar buttons.
14240      * @param {HtmlEditor} editor
14241      */
14242     createToolbar : function(){
14243         
14244         Roo.log("create toolbars");
14245         
14246         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14247         this.toolbars[0].render(this.toolbarContainer());
14248         
14249         return;
14250         
14251 //        if (!editor.toolbars || !editor.toolbars.length) {
14252 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14253 //        }
14254 //        
14255 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14256 //            editor.toolbars[i] = Roo.factory(
14257 //                    typeof(editor.toolbars[i]) == 'string' ?
14258 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14259 //                Roo.bootstrap.HtmlEditor);
14260 //            editor.toolbars[i].init(editor);
14261 //        }
14262     },
14263
14264      
14265     // private
14266     onRender : function(ct, position)
14267     {
14268        // Roo.log("Call onRender: " + this.xtype);
14269         var _t = this;
14270         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14271       
14272         this.wrap = this.inputEl().wrap({
14273             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14274         });
14275         
14276         this.editorcore.onRender(ct, position);
14277          
14278         if (this.resizable) {
14279             this.resizeEl = new Roo.Resizable(this.wrap, {
14280                 pinned : true,
14281                 wrap: true,
14282                 dynamic : true,
14283                 minHeight : this.height,
14284                 height: this.height,
14285                 handles : this.resizable,
14286                 width: this.width,
14287                 listeners : {
14288                     resize : function(r, w, h) {
14289                         _t.onResize(w,h); // -something
14290                     }
14291                 }
14292             });
14293             
14294         }
14295         this.createToolbar(this);
14296        
14297         
14298         if(!this.width && this.resizable){
14299             this.setSize(this.wrap.getSize());
14300         }
14301         if (this.resizeEl) {
14302             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14303             // should trigger onReize..
14304         }
14305         
14306     },
14307
14308     // private
14309     onResize : function(w, h)
14310     {
14311         Roo.log('resize: ' +w + ',' + h );
14312         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14313         var ew = false;
14314         var eh = false;
14315         
14316         if(this.inputEl() ){
14317             if(typeof w == 'number'){
14318                 var aw = w - this.wrap.getFrameWidth('lr');
14319                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14320                 ew = aw;
14321             }
14322             if(typeof h == 'number'){
14323                  var tbh = -11;  // fixme it needs to tool bar size!
14324                 for (var i =0; i < this.toolbars.length;i++) {
14325                     // fixme - ask toolbars for heights?
14326                     tbh += this.toolbars[i].el.getHeight();
14327                     //if (this.toolbars[i].footer) {
14328                     //    tbh += this.toolbars[i].footer.el.getHeight();
14329                     //}
14330                 }
14331               
14332                 
14333                 
14334                 
14335                 
14336                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14337                 ah -= 5; // knock a few pixes off for look..
14338                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14339                 var eh = ah;
14340             }
14341         }
14342         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14343         this.editorcore.onResize(ew,eh);
14344         
14345     },
14346
14347     /**
14348      * Toggles the editor between standard and source edit mode.
14349      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14350      */
14351     toggleSourceEdit : function(sourceEditMode)
14352     {
14353         this.editorcore.toggleSourceEdit(sourceEditMode);
14354         
14355         if(this.editorcore.sourceEditMode){
14356             Roo.log('editor - showing textarea');
14357             
14358 //            Roo.log('in');
14359 //            Roo.log(this.syncValue());
14360             this.syncValue();
14361             this.inputEl().removeClass('hide');
14362             this.inputEl().dom.removeAttribute('tabIndex');
14363             this.inputEl().focus();
14364         }else{
14365             Roo.log('editor - hiding textarea');
14366 //            Roo.log('out')
14367 //            Roo.log(this.pushValue()); 
14368             this.pushValue();
14369             
14370             this.inputEl().addClass('hide');
14371             this.inputEl().dom.setAttribute('tabIndex', -1);
14372             //this.deferFocus();
14373         }
14374          
14375         if(this.resizable){
14376             this.setSize(this.wrap.getSize());
14377         }
14378         
14379         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14380     },
14381  
14382     // private (for BoxComponent)
14383     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14384
14385     // private (for BoxComponent)
14386     getResizeEl : function(){
14387         return this.wrap;
14388     },
14389
14390     // private (for BoxComponent)
14391     getPositionEl : function(){
14392         return this.wrap;
14393     },
14394
14395     // private
14396     initEvents : function(){
14397         this.originalValue = this.getValue();
14398     },
14399
14400 //    /**
14401 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14402 //     * @method
14403 //     */
14404 //    markInvalid : Roo.emptyFn,
14405 //    /**
14406 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14407 //     * @method
14408 //     */
14409 //    clearInvalid : Roo.emptyFn,
14410
14411     setValue : function(v){
14412         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14413         this.editorcore.pushValue();
14414     },
14415
14416      
14417     // private
14418     deferFocus : function(){
14419         this.focus.defer(10, this);
14420     },
14421
14422     // doc'ed in Field
14423     focus : function(){
14424         this.editorcore.focus();
14425         
14426     },
14427       
14428
14429     // private
14430     onDestroy : function(){
14431         
14432         
14433         
14434         if(this.rendered){
14435             
14436             for (var i =0; i < this.toolbars.length;i++) {
14437                 // fixme - ask toolbars for heights?
14438                 this.toolbars[i].onDestroy();
14439             }
14440             
14441             this.wrap.dom.innerHTML = '';
14442             this.wrap.remove();
14443         }
14444     },
14445
14446     // private
14447     onFirstFocus : function(){
14448         //Roo.log("onFirstFocus");
14449         this.editorcore.onFirstFocus();
14450          for (var i =0; i < this.toolbars.length;i++) {
14451             this.toolbars[i].onFirstFocus();
14452         }
14453         
14454     },
14455     
14456     // private
14457     syncValue : function()
14458     {   
14459         this.editorcore.syncValue();
14460     },
14461     
14462     pushValue : function()
14463     {   
14464         this.editorcore.pushValue();
14465     }
14466      
14467     
14468     // hide stuff that is not compatible
14469     /**
14470      * @event blur
14471      * @hide
14472      */
14473     /**
14474      * @event change
14475      * @hide
14476      */
14477     /**
14478      * @event focus
14479      * @hide
14480      */
14481     /**
14482      * @event specialkey
14483      * @hide
14484      */
14485     /**
14486      * @cfg {String} fieldClass @hide
14487      */
14488     /**
14489      * @cfg {String} focusClass @hide
14490      */
14491     /**
14492      * @cfg {String} autoCreate @hide
14493      */
14494     /**
14495      * @cfg {String} inputType @hide
14496      */
14497     /**
14498      * @cfg {String} invalidClass @hide
14499      */
14500     /**
14501      * @cfg {String} invalidText @hide
14502      */
14503     /**
14504      * @cfg {String} msgFx @hide
14505      */
14506     /**
14507      * @cfg {String} validateOnBlur @hide
14508      */
14509 });
14510  
14511     
14512    
14513    
14514    
14515       
14516
14517 /**
14518  * @class Roo.bootstrap.HtmlEditorToolbar1
14519  * Basic Toolbar
14520  * 
14521  * Usage:
14522  *
14523  new Roo.bootstrap.HtmlEditor({
14524     ....
14525     toolbars : [
14526         new Roo.bootstrap.HtmlEditorToolbar1({
14527             disable : { fonts: 1 , format: 1, ..., ... , ...],
14528             btns : [ .... ]
14529         })
14530     }
14531      
14532  * 
14533  * @cfg {Object} disable List of elements to disable..
14534  * @cfg {Array} btns List of additional buttons.
14535  * 
14536  * 
14537  * NEEDS Extra CSS? 
14538  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14539  */
14540  
14541 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14542 {
14543     
14544     Roo.apply(this, config);
14545     
14546     // default disabled, based on 'good practice'..
14547     this.disable = this.disable || {};
14548     Roo.applyIf(this.disable, {
14549         fontSize : true,
14550         colors : true,
14551         specialElements : true
14552     });
14553     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14554     
14555     this.editor = config.editor;
14556     this.editorcore = config.editor.editorcore;
14557     
14558     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14559     
14560     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14561     // dont call parent... till later.
14562 }
14563 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14564     
14565     
14566     bar : true,
14567     
14568     editor : false,
14569     editorcore : false,
14570     
14571     
14572     formats : [
14573         "p" ,  
14574         "h1","h2","h3","h4","h5","h6", 
14575         "pre", "code", 
14576         "abbr", "acronym", "address", "cite", "samp", "var",
14577         'div','span'
14578     ],
14579     
14580     onRender : function(ct, position)
14581     {
14582        // Roo.log("Call onRender: " + this.xtype);
14583         
14584        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14585        Roo.log(this.el);
14586        this.el.dom.style.marginBottom = '0';
14587        var _this = this;
14588        var editorcore = this.editorcore;
14589        var editor= this.editor;
14590        
14591        var children = [];
14592        var btn = function(id,cmd , toggle, handler){
14593        
14594             var  event = toggle ? 'toggle' : 'click';
14595        
14596             var a = {
14597                 size : 'sm',
14598                 xtype: 'Button',
14599                 xns: Roo.bootstrap,
14600                 glyphicon : id,
14601                 cmd : id || cmd,
14602                 enableToggle:toggle !== false,
14603                 //html : 'submit'
14604                 pressed : toggle ? false : null,
14605                 listeners : {}
14606             }
14607             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14608                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14609             }
14610             children.push(a);
14611             return a;
14612        }
14613         
14614         var style = {
14615                 xtype: 'Button',
14616                 size : 'sm',
14617                 xns: Roo.bootstrap,
14618                 glyphicon : 'font',
14619                 //html : 'submit'
14620                 menu : {
14621                     xtype: 'Menu',
14622                     xns: Roo.bootstrap,
14623                     items:  []
14624                 }
14625         };
14626         Roo.each(this.formats, function(f) {
14627             style.menu.items.push({
14628                 xtype :'MenuItem',
14629                 xns: Roo.bootstrap,
14630                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14631                 tagname : f,
14632                 listeners : {
14633                     click : function()
14634                     {
14635                         editorcore.insertTag(this.tagname);
14636                         editor.focus();
14637                     }
14638                 }
14639                 
14640             });
14641         });
14642          children.push(style);   
14643             
14644             
14645         btn('bold',false,true);
14646         btn('italic',false,true);
14647         btn('align-left', 'justifyleft',true);
14648         btn('align-center', 'justifycenter',true);
14649         btn('align-right' , 'justifyright',true);
14650         btn('link', false, false, function(btn) {
14651             //Roo.log("create link?");
14652             var url = prompt(this.createLinkText, this.defaultLinkValue);
14653             if(url && url != 'http:/'+'/'){
14654                 this.editorcore.relayCmd('createlink', url);
14655             }
14656         }),
14657         btn('list','insertunorderedlist',true);
14658         btn('pencil', false,true, function(btn){
14659                 Roo.log(this);
14660                 
14661                 this.toggleSourceEdit(btn.pressed);
14662         });
14663         /*
14664         var cog = {
14665                 xtype: 'Button',
14666                 size : 'sm',
14667                 xns: Roo.bootstrap,
14668                 glyphicon : 'cog',
14669                 //html : 'submit'
14670                 menu : {
14671                     xtype: 'Menu',
14672                     xns: Roo.bootstrap,
14673                     items:  []
14674                 }
14675         };
14676         
14677         cog.menu.items.push({
14678             xtype :'MenuItem',
14679             xns: Roo.bootstrap,
14680             html : Clean styles,
14681             tagname : f,
14682             listeners : {
14683                 click : function()
14684                 {
14685                     editorcore.insertTag(this.tagname);
14686                     editor.focus();
14687                 }
14688             }
14689             
14690         });
14691        */
14692         
14693          
14694        this.xtype = 'Navbar';
14695         
14696         for(var i=0;i< children.length;i++) {
14697             
14698             this.buttons.add(this.addxtypeChild(children[i]));
14699             
14700         }
14701         
14702         editor.on('editorevent', this.updateToolbar, this);
14703     },
14704     onBtnClick : function(id)
14705     {
14706        this.editorcore.relayCmd(id);
14707        this.editorcore.focus();
14708     },
14709     
14710     /**
14711      * Protected method that will not generally be called directly. It triggers
14712      * a toolbar update by reading the markup state of the current selection in the editor.
14713      */
14714     updateToolbar: function(){
14715
14716         if(!this.editorcore.activated){
14717             this.editor.onFirstFocus(); // is this neeed?
14718             return;
14719         }
14720
14721         var btns = this.buttons; 
14722         var doc = this.editorcore.doc;
14723         btns.get('bold').setActive(doc.queryCommandState('bold'));
14724         btns.get('italic').setActive(doc.queryCommandState('italic'));
14725         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14726         
14727         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14728         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14729         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14730         
14731         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14732         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14733          /*
14734         
14735         var ans = this.editorcore.getAllAncestors();
14736         if (this.formatCombo) {
14737             
14738             
14739             var store = this.formatCombo.store;
14740             this.formatCombo.setValue("");
14741             for (var i =0; i < ans.length;i++) {
14742                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14743                     // select it..
14744                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14745                     break;
14746                 }
14747             }
14748         }
14749         
14750         
14751         
14752         // hides menus... - so this cant be on a menu...
14753         Roo.bootstrap.MenuMgr.hideAll();
14754         */
14755         Roo.bootstrap.MenuMgr.hideAll();
14756         //this.editorsyncValue();
14757     },
14758     onFirstFocus: function() {
14759         this.buttons.each(function(item){
14760            item.enable();
14761         });
14762     },
14763     toggleSourceEdit : function(sourceEditMode){
14764         
14765           
14766         if(sourceEditMode){
14767             Roo.log("disabling buttons");
14768            this.buttons.each( function(item){
14769                 if(item.cmd != 'pencil'){
14770                     item.disable();
14771                 }
14772             });
14773           
14774         }else{
14775             Roo.log("enabling buttons");
14776             if(this.editorcore.initialized){
14777                 this.buttons.each( function(item){
14778                     item.enable();
14779                 });
14780             }
14781             
14782         }
14783         Roo.log("calling toggole on editor");
14784         // tell the editor that it's been pressed..
14785         this.editor.toggleSourceEdit(sourceEditMode);
14786        
14787     }
14788 });
14789
14790
14791
14792
14793
14794 /**
14795  * @class Roo.bootstrap.Table.AbstractSelectionModel
14796  * @extends Roo.util.Observable
14797  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14798  * implemented by descendant classes.  This class should not be directly instantiated.
14799  * @constructor
14800  */
14801 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14802     this.locked = false;
14803     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14804 };
14805
14806
14807 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14808     /** @ignore Called by the grid automatically. Do not call directly. */
14809     init : function(grid){
14810         this.grid = grid;
14811         this.initEvents();
14812     },
14813
14814     /**
14815      * Locks the selections.
14816      */
14817     lock : function(){
14818         this.locked = true;
14819     },
14820
14821     /**
14822      * Unlocks the selections.
14823      */
14824     unlock : function(){
14825         this.locked = false;
14826     },
14827
14828     /**
14829      * Returns true if the selections are locked.
14830      * @return {Boolean}
14831      */
14832     isLocked : function(){
14833         return this.locked;
14834     }
14835 });
14836 /**
14837  * @class Roo.bootstrap.Table.ColumnModel
14838  * @extends Roo.util.Observable
14839  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14840  * the columns in the table.
14841  
14842  * @constructor
14843  * @param {Object} config An Array of column config objects. See this class's
14844  * config objects for details.
14845 */
14846 Roo.bootstrap.Table.ColumnModel = function(config){
14847         /**
14848      * The config passed into the constructor
14849      */
14850     this.config = config;
14851     this.lookup = {};
14852
14853     // if no id, create one
14854     // if the column does not have a dataIndex mapping,
14855     // map it to the order it is in the config
14856     for(var i = 0, len = config.length; i < len; i++){
14857         var c = config[i];
14858         if(typeof c.dataIndex == "undefined"){
14859             c.dataIndex = i;
14860         }
14861         if(typeof c.renderer == "string"){
14862             c.renderer = Roo.util.Format[c.renderer];
14863         }
14864         if(typeof c.id == "undefined"){
14865             c.id = Roo.id();
14866         }
14867 //        if(c.editor && c.editor.xtype){
14868 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14869 //        }
14870 //        if(c.editor && c.editor.isFormField){
14871 //            c.editor = new Roo.grid.GridEditor(c.editor);
14872 //        }
14873
14874         this.lookup[c.id] = c;
14875     }
14876
14877     /**
14878      * The width of columns which have no width specified (defaults to 100)
14879      * @type Number
14880      */
14881     this.defaultWidth = 100;
14882
14883     /**
14884      * Default sortable of columns which have no sortable specified (defaults to false)
14885      * @type Boolean
14886      */
14887     this.defaultSortable = false;
14888
14889     this.addEvents({
14890         /**
14891              * @event widthchange
14892              * Fires when the width of a column changes.
14893              * @param {ColumnModel} this
14894              * @param {Number} columnIndex The column index
14895              * @param {Number} newWidth The new width
14896              */
14897             "widthchange": true,
14898         /**
14899              * @event headerchange
14900              * Fires when the text of a header changes.
14901              * @param {ColumnModel} this
14902              * @param {Number} columnIndex The column index
14903              * @param {Number} newText The new header text
14904              */
14905             "headerchange": true,
14906         /**
14907              * @event hiddenchange
14908              * Fires when a column is hidden or "unhidden".
14909              * @param {ColumnModel} this
14910              * @param {Number} columnIndex The column index
14911              * @param {Boolean} hidden true if hidden, false otherwise
14912              */
14913             "hiddenchange": true,
14914             /**
14915          * @event columnmoved
14916          * Fires when a column is moved.
14917          * @param {ColumnModel} this
14918          * @param {Number} oldIndex
14919          * @param {Number} newIndex
14920          */
14921         "columnmoved" : true,
14922         /**
14923          * @event columlockchange
14924          * Fires when a column's locked state is changed
14925          * @param {ColumnModel} this
14926          * @param {Number} colIndex
14927          * @param {Boolean} locked true if locked
14928          */
14929         "columnlockchange" : true
14930     });
14931     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14932 };
14933 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14934     /**
14935      * @cfg {String} header The header text to display in the Grid view.
14936      */
14937     /**
14938      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14939      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14940      * specified, the column's index is used as an index into the Record's data Array.
14941      */
14942     /**
14943      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14944      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14945      */
14946     /**
14947      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14948      * Defaults to the value of the {@link #defaultSortable} property.
14949      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14950      */
14951     /**
14952      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14953      */
14954     /**
14955      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14956      */
14957     /**
14958      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14959      */
14960     /**
14961      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14962      */
14963     /**
14964      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14965      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14966      * default renderer uses the raw data value.
14967      */
14968     /**
14969      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14970      */
14971
14972     /**
14973      * Returns the id of the column at the specified index.
14974      * @param {Number} index The column index
14975      * @return {String} the id
14976      */
14977     getColumnId : function(index){
14978         return this.config[index].id;
14979     },
14980
14981     /**
14982      * Returns the column for a specified id.
14983      * @param {String} id The column id
14984      * @return {Object} the column
14985      */
14986     getColumnById : function(id){
14987         return this.lookup[id];
14988     },
14989
14990     
14991     /**
14992      * Returns the column for a specified dataIndex.
14993      * @param {String} dataIndex The column dataIndex
14994      * @return {Object|Boolean} the column or false if not found
14995      */
14996     getColumnByDataIndex: function(dataIndex){
14997         var index = this.findColumnIndex(dataIndex);
14998         return index > -1 ? this.config[index] : false;
14999     },
15000     
15001     /**
15002      * Returns the index for a specified column id.
15003      * @param {String} id The column id
15004      * @return {Number} the index, or -1 if not found
15005      */
15006     getIndexById : function(id){
15007         for(var i = 0, len = this.config.length; i < len; i++){
15008             if(this.config[i].id == id){
15009                 return i;
15010             }
15011         }
15012         return -1;
15013     },
15014     
15015     /**
15016      * Returns the index for a specified column dataIndex.
15017      * @param {String} dataIndex The column dataIndex
15018      * @return {Number} the index, or -1 if not found
15019      */
15020     
15021     findColumnIndex : function(dataIndex){
15022         for(var i = 0, len = this.config.length; i < len; i++){
15023             if(this.config[i].dataIndex == dataIndex){
15024                 return i;
15025             }
15026         }
15027         return -1;
15028     },
15029     
15030     
15031     moveColumn : function(oldIndex, newIndex){
15032         var c = this.config[oldIndex];
15033         this.config.splice(oldIndex, 1);
15034         this.config.splice(newIndex, 0, c);
15035         this.dataMap = null;
15036         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15037     },
15038
15039     isLocked : function(colIndex){
15040         return this.config[colIndex].locked === true;
15041     },
15042
15043     setLocked : function(colIndex, value, suppressEvent){
15044         if(this.isLocked(colIndex) == value){
15045             return;
15046         }
15047         this.config[colIndex].locked = value;
15048         if(!suppressEvent){
15049             this.fireEvent("columnlockchange", this, colIndex, value);
15050         }
15051     },
15052
15053     getTotalLockedWidth : function(){
15054         var totalWidth = 0;
15055         for(var i = 0; i < this.config.length; i++){
15056             if(this.isLocked(i) && !this.isHidden(i)){
15057                 this.totalWidth += this.getColumnWidth(i);
15058             }
15059         }
15060         return totalWidth;
15061     },
15062
15063     getLockedCount : function(){
15064         for(var i = 0, len = this.config.length; i < len; i++){
15065             if(!this.isLocked(i)){
15066                 return i;
15067             }
15068         }
15069     },
15070
15071     /**
15072      * Returns the number of columns.
15073      * @return {Number}
15074      */
15075     getColumnCount : function(visibleOnly){
15076         if(visibleOnly === true){
15077             var c = 0;
15078             for(var i = 0, len = this.config.length; i < len; i++){
15079                 if(!this.isHidden(i)){
15080                     c++;
15081                 }
15082             }
15083             return c;
15084         }
15085         return this.config.length;
15086     },
15087
15088     /**
15089      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15090      * @param {Function} fn
15091      * @param {Object} scope (optional)
15092      * @return {Array} result
15093      */
15094     getColumnsBy : function(fn, scope){
15095         var r = [];
15096         for(var i = 0, len = this.config.length; i < len; i++){
15097             var c = this.config[i];
15098             if(fn.call(scope||this, c, i) === true){
15099                 r[r.length] = c;
15100             }
15101         }
15102         return r;
15103     },
15104
15105     /**
15106      * Returns true if the specified column is sortable.
15107      * @param {Number} col The column index
15108      * @return {Boolean}
15109      */
15110     isSortable : function(col){
15111         if(typeof this.config[col].sortable == "undefined"){
15112             return this.defaultSortable;
15113         }
15114         return this.config[col].sortable;
15115     },
15116
15117     /**
15118      * Returns the rendering (formatting) function defined for the column.
15119      * @param {Number} col The column index.
15120      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15121      */
15122     getRenderer : function(col){
15123         if(!this.config[col].renderer){
15124             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15125         }
15126         return this.config[col].renderer;
15127     },
15128
15129     /**
15130      * Sets the rendering (formatting) function for a column.
15131      * @param {Number} col The column index
15132      * @param {Function} fn The function to use to process the cell's raw data
15133      * to return HTML markup for the grid view. The render function is called with
15134      * the following parameters:<ul>
15135      * <li>Data value.</li>
15136      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15137      * <li>css A CSS style string to apply to the table cell.</li>
15138      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15139      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15140      * <li>Row index</li>
15141      * <li>Column index</li>
15142      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15143      */
15144     setRenderer : function(col, fn){
15145         this.config[col].renderer = fn;
15146     },
15147
15148     /**
15149      * Returns the width for the specified column.
15150      * @param {Number} col The column index
15151      * @return {Number}
15152      */
15153     getColumnWidth : function(col){
15154         return this.config[col].width * 1 || this.defaultWidth;
15155     },
15156
15157     /**
15158      * Sets the width for a column.
15159      * @param {Number} col The column index
15160      * @param {Number} width The new width
15161      */
15162     setColumnWidth : function(col, width, suppressEvent){
15163         this.config[col].width = width;
15164         this.totalWidth = null;
15165         if(!suppressEvent){
15166              this.fireEvent("widthchange", this, col, width);
15167         }
15168     },
15169
15170     /**
15171      * Returns the total width of all columns.
15172      * @param {Boolean} includeHidden True to include hidden column widths
15173      * @return {Number}
15174      */
15175     getTotalWidth : function(includeHidden){
15176         if(!this.totalWidth){
15177             this.totalWidth = 0;
15178             for(var i = 0, len = this.config.length; i < len; i++){
15179                 if(includeHidden || !this.isHidden(i)){
15180                     this.totalWidth += this.getColumnWidth(i);
15181                 }
15182             }
15183         }
15184         return this.totalWidth;
15185     },
15186
15187     /**
15188      * Returns the header for the specified column.
15189      * @param {Number} col The column index
15190      * @return {String}
15191      */
15192     getColumnHeader : function(col){
15193         return this.config[col].header;
15194     },
15195
15196     /**
15197      * Sets the header for a column.
15198      * @param {Number} col The column index
15199      * @param {String} header The new header
15200      */
15201     setColumnHeader : function(col, header){
15202         this.config[col].header = header;
15203         this.fireEvent("headerchange", this, col, header);
15204     },
15205
15206     /**
15207      * Returns the tooltip for the specified column.
15208      * @param {Number} col The column index
15209      * @return {String}
15210      */
15211     getColumnTooltip : function(col){
15212             return this.config[col].tooltip;
15213     },
15214     /**
15215      * Sets the tooltip for a column.
15216      * @param {Number} col The column index
15217      * @param {String} tooltip The new tooltip
15218      */
15219     setColumnTooltip : function(col, tooltip){
15220             this.config[col].tooltip = tooltip;
15221     },
15222
15223     /**
15224      * Returns the dataIndex for the specified column.
15225      * @param {Number} col The column index
15226      * @return {Number}
15227      */
15228     getDataIndex : function(col){
15229         return this.config[col].dataIndex;
15230     },
15231
15232     /**
15233      * Sets the dataIndex for a column.
15234      * @param {Number} col The column index
15235      * @param {Number} dataIndex The new dataIndex
15236      */
15237     setDataIndex : function(col, dataIndex){
15238         this.config[col].dataIndex = dataIndex;
15239     },
15240
15241     
15242     
15243     /**
15244      * Returns true if the cell is editable.
15245      * @param {Number} colIndex The column index
15246      * @param {Number} rowIndex The row index
15247      * @return {Boolean}
15248      */
15249     isCellEditable : function(colIndex, rowIndex){
15250         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15251     },
15252
15253     /**
15254      * Returns the editor defined for the cell/column.
15255      * return false or null to disable editing.
15256      * @param {Number} colIndex The column index
15257      * @param {Number} rowIndex The row index
15258      * @return {Object}
15259      */
15260     getCellEditor : function(colIndex, rowIndex){
15261         return this.config[colIndex].editor;
15262     },
15263
15264     /**
15265      * Sets if a column is editable.
15266      * @param {Number} col The column index
15267      * @param {Boolean} editable True if the column is editable
15268      */
15269     setEditable : function(col, editable){
15270         this.config[col].editable = editable;
15271     },
15272
15273
15274     /**
15275      * Returns true if the column is hidden.
15276      * @param {Number} colIndex The column index
15277      * @return {Boolean}
15278      */
15279     isHidden : function(colIndex){
15280         return this.config[colIndex].hidden;
15281     },
15282
15283
15284     /**
15285      * Returns true if the column width cannot be changed
15286      */
15287     isFixed : function(colIndex){
15288         return this.config[colIndex].fixed;
15289     },
15290
15291     /**
15292      * Returns true if the column can be resized
15293      * @return {Boolean}
15294      */
15295     isResizable : function(colIndex){
15296         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15297     },
15298     /**
15299      * Sets if a column is hidden.
15300      * @param {Number} colIndex The column index
15301      * @param {Boolean} hidden True if the column is hidden
15302      */
15303     setHidden : function(colIndex, hidden){
15304         this.config[colIndex].hidden = hidden;
15305         this.totalWidth = null;
15306         this.fireEvent("hiddenchange", this, colIndex, hidden);
15307     },
15308
15309     /**
15310      * Sets the editor for a column.
15311      * @param {Number} col The column index
15312      * @param {Object} editor The editor object
15313      */
15314     setEditor : function(col, editor){
15315         this.config[col].editor = editor;
15316     }
15317 });
15318
15319 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15320         if(typeof value == "string" && value.length < 1){
15321             return "&#160;";
15322         }
15323         return value;
15324 };
15325
15326 // Alias for backwards compatibility
15327 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15328
15329 /**
15330  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15331  * @class Roo.bootstrap.Table.RowSelectionModel
15332  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15333  * It supports multiple selections and keyboard selection/navigation. 
15334  * @constructor
15335  * @param {Object} config
15336  */
15337
15338 Roo.bootstrap.Table.RowSelectionModel = function(config){
15339     Roo.apply(this, config);
15340     this.selections = new Roo.util.MixedCollection(false, function(o){
15341         return o.id;
15342     });
15343
15344     this.last = false;
15345     this.lastActive = false;
15346
15347     this.addEvents({
15348         /**
15349              * @event selectionchange
15350              * Fires when the selection changes
15351              * @param {SelectionModel} this
15352              */
15353             "selectionchange" : true,
15354         /**
15355              * @event afterselectionchange
15356              * Fires after the selection changes (eg. by key press or clicking)
15357              * @param {SelectionModel} this
15358              */
15359             "afterselectionchange" : true,
15360         /**
15361              * @event beforerowselect
15362              * Fires when a row is selected being selected, return false to cancel.
15363              * @param {SelectionModel} this
15364              * @param {Number} rowIndex The selected index
15365              * @param {Boolean} keepExisting False if other selections will be cleared
15366              */
15367             "beforerowselect" : true,
15368         /**
15369              * @event rowselect
15370              * Fires when a row is selected.
15371              * @param {SelectionModel} this
15372              * @param {Number} rowIndex The selected index
15373              * @param {Roo.data.Record} r The record
15374              */
15375             "rowselect" : true,
15376         /**
15377              * @event rowdeselect
15378              * Fires when a row is deselected.
15379              * @param {SelectionModel} this
15380              * @param {Number} rowIndex The selected index
15381              */
15382         "rowdeselect" : true
15383     });
15384     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15385     this.locked = false;
15386 };
15387
15388 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15389     /**
15390      * @cfg {Boolean} singleSelect
15391      * True to allow selection of only one row at a time (defaults to false)
15392      */
15393     singleSelect : false,
15394
15395     // private
15396     initEvents : function(){
15397
15398         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15399             this.grid.on("mousedown", this.handleMouseDown, this);
15400         }else{ // allow click to work like normal
15401             this.grid.on("rowclick", this.handleDragableRowClick, this);
15402         }
15403
15404         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15405             "up" : function(e){
15406                 if(!e.shiftKey){
15407                     this.selectPrevious(e.shiftKey);
15408                 }else if(this.last !== false && this.lastActive !== false){
15409                     var last = this.last;
15410                     this.selectRange(this.last,  this.lastActive-1);
15411                     this.grid.getView().focusRow(this.lastActive);
15412                     if(last !== false){
15413                         this.last = last;
15414                     }
15415                 }else{
15416                     this.selectFirstRow();
15417                 }
15418                 this.fireEvent("afterselectionchange", this);
15419             },
15420             "down" : function(e){
15421                 if(!e.shiftKey){
15422                     this.selectNext(e.shiftKey);
15423                 }else if(this.last !== false && this.lastActive !== false){
15424                     var last = this.last;
15425                     this.selectRange(this.last,  this.lastActive+1);
15426                     this.grid.getView().focusRow(this.lastActive);
15427                     if(last !== false){
15428                         this.last = last;
15429                     }
15430                 }else{
15431                     this.selectFirstRow();
15432                 }
15433                 this.fireEvent("afterselectionchange", this);
15434             },
15435             scope: this
15436         });
15437
15438         var view = this.grid.view;
15439         view.on("refresh", this.onRefresh, this);
15440         view.on("rowupdated", this.onRowUpdated, this);
15441         view.on("rowremoved", this.onRemove, this);
15442     },
15443
15444     // private
15445     onRefresh : function(){
15446         var ds = this.grid.dataSource, i, v = this.grid.view;
15447         var s = this.selections;
15448         s.each(function(r){
15449             if((i = ds.indexOfId(r.id)) != -1){
15450                 v.onRowSelect(i);
15451             }else{
15452                 s.remove(r);
15453             }
15454         });
15455     },
15456
15457     // private
15458     onRemove : function(v, index, r){
15459         this.selections.remove(r);
15460     },
15461
15462     // private
15463     onRowUpdated : function(v, index, r){
15464         if(this.isSelected(r)){
15465             v.onRowSelect(index);
15466         }
15467     },
15468
15469     /**
15470      * Select records.
15471      * @param {Array} records The records to select
15472      * @param {Boolean} keepExisting (optional) True to keep existing selections
15473      */
15474     selectRecords : function(records, keepExisting){
15475         if(!keepExisting){
15476             this.clearSelections();
15477         }
15478         var ds = this.grid.dataSource;
15479         for(var i = 0, len = records.length; i < len; i++){
15480             this.selectRow(ds.indexOf(records[i]), true);
15481         }
15482     },
15483
15484     /**
15485      * Gets the number of selected rows.
15486      * @return {Number}
15487      */
15488     getCount : function(){
15489         return this.selections.length;
15490     },
15491
15492     /**
15493      * Selects the first row in the grid.
15494      */
15495     selectFirstRow : function(){
15496         this.selectRow(0);
15497     },
15498
15499     /**
15500      * Select the last row.
15501      * @param {Boolean} keepExisting (optional) True to keep existing selections
15502      */
15503     selectLastRow : function(keepExisting){
15504         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15505     },
15506
15507     /**
15508      * Selects the row immediately following the last selected row.
15509      * @param {Boolean} keepExisting (optional) True to keep existing selections
15510      */
15511     selectNext : function(keepExisting){
15512         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15513             this.selectRow(this.last+1, keepExisting);
15514             this.grid.getView().focusRow(this.last);
15515         }
15516     },
15517
15518     /**
15519      * Selects the row that precedes the last selected row.
15520      * @param {Boolean} keepExisting (optional) True to keep existing selections
15521      */
15522     selectPrevious : function(keepExisting){
15523         if(this.last){
15524             this.selectRow(this.last-1, keepExisting);
15525             this.grid.getView().focusRow(this.last);
15526         }
15527     },
15528
15529     /**
15530      * Returns the selected records
15531      * @return {Array} Array of selected records
15532      */
15533     getSelections : function(){
15534         return [].concat(this.selections.items);
15535     },
15536
15537     /**
15538      * Returns the first selected record.
15539      * @return {Record}
15540      */
15541     getSelected : function(){
15542         return this.selections.itemAt(0);
15543     },
15544
15545
15546     /**
15547      * Clears all selections.
15548      */
15549     clearSelections : function(fast){
15550         if(this.locked) return;
15551         if(fast !== true){
15552             var ds = this.grid.dataSource;
15553             var s = this.selections;
15554             s.each(function(r){
15555                 this.deselectRow(ds.indexOfId(r.id));
15556             }, this);
15557             s.clear();
15558         }else{
15559             this.selections.clear();
15560         }
15561         this.last = false;
15562     },
15563
15564
15565     /**
15566      * Selects all rows.
15567      */
15568     selectAll : function(){
15569         if(this.locked) return;
15570         this.selections.clear();
15571         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15572             this.selectRow(i, true);
15573         }
15574     },
15575
15576     /**
15577      * Returns True if there is a selection.
15578      * @return {Boolean}
15579      */
15580     hasSelection : function(){
15581         return this.selections.length > 0;
15582     },
15583
15584     /**
15585      * Returns True if the specified row is selected.
15586      * @param {Number/Record} record The record or index of the record to check
15587      * @return {Boolean}
15588      */
15589     isSelected : function(index){
15590         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15591         return (r && this.selections.key(r.id) ? true : false);
15592     },
15593
15594     /**
15595      * Returns True if the specified record id is selected.
15596      * @param {String} id The id of record to check
15597      * @return {Boolean}
15598      */
15599     isIdSelected : function(id){
15600         return (this.selections.key(id) ? true : false);
15601     },
15602
15603     // private
15604     handleMouseDown : function(e, t){
15605         var view = this.grid.getView(), rowIndex;
15606         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15607             return;
15608         };
15609         if(e.shiftKey && this.last !== false){
15610             var last = this.last;
15611             this.selectRange(last, rowIndex, e.ctrlKey);
15612             this.last = last; // reset the last
15613             view.focusRow(rowIndex);
15614         }else{
15615             var isSelected = this.isSelected(rowIndex);
15616             if(e.button !== 0 && isSelected){
15617                 view.focusRow(rowIndex);
15618             }else if(e.ctrlKey && isSelected){
15619                 this.deselectRow(rowIndex);
15620             }else if(!isSelected){
15621                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15622                 view.focusRow(rowIndex);
15623             }
15624         }
15625         this.fireEvent("afterselectionchange", this);
15626     },
15627     // private
15628     handleDragableRowClick :  function(grid, rowIndex, e) 
15629     {
15630         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15631             this.selectRow(rowIndex, false);
15632             grid.view.focusRow(rowIndex);
15633              this.fireEvent("afterselectionchange", this);
15634         }
15635     },
15636     
15637     /**
15638      * Selects multiple rows.
15639      * @param {Array} rows Array of the indexes of the row to select
15640      * @param {Boolean} keepExisting (optional) True to keep existing selections
15641      */
15642     selectRows : function(rows, keepExisting){
15643         if(!keepExisting){
15644             this.clearSelections();
15645         }
15646         for(var i = 0, len = rows.length; i < len; i++){
15647             this.selectRow(rows[i], true);
15648         }
15649     },
15650
15651     /**
15652      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15653      * @param {Number} startRow The index of the first row in the range
15654      * @param {Number} endRow The index of the last row in the range
15655      * @param {Boolean} keepExisting (optional) True to retain existing selections
15656      */
15657     selectRange : function(startRow, endRow, keepExisting){
15658         if(this.locked) return;
15659         if(!keepExisting){
15660             this.clearSelections();
15661         }
15662         if(startRow <= endRow){
15663             for(var i = startRow; i <= endRow; i++){
15664                 this.selectRow(i, true);
15665             }
15666         }else{
15667             for(var i = startRow; i >= endRow; i--){
15668                 this.selectRow(i, true);
15669             }
15670         }
15671     },
15672
15673     /**
15674      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15675      * @param {Number} startRow The index of the first row in the range
15676      * @param {Number} endRow The index of the last row in the range
15677      */
15678     deselectRange : function(startRow, endRow, preventViewNotify){
15679         if(this.locked) return;
15680         for(var i = startRow; i <= endRow; i++){
15681             this.deselectRow(i, preventViewNotify);
15682         }
15683     },
15684
15685     /**
15686      * Selects a row.
15687      * @param {Number} row The index of the row to select
15688      * @param {Boolean} keepExisting (optional) True to keep existing selections
15689      */
15690     selectRow : function(index, keepExisting, preventViewNotify){
15691         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15692         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15693             if(!keepExisting || this.singleSelect){
15694                 this.clearSelections();
15695             }
15696             var r = this.grid.dataSource.getAt(index);
15697             this.selections.add(r);
15698             this.last = this.lastActive = index;
15699             if(!preventViewNotify){
15700                 this.grid.getView().onRowSelect(index);
15701             }
15702             this.fireEvent("rowselect", this, index, r);
15703             this.fireEvent("selectionchange", this);
15704         }
15705     },
15706
15707     /**
15708      * Deselects a row.
15709      * @param {Number} row The index of the row to deselect
15710      */
15711     deselectRow : function(index, preventViewNotify){
15712         if(this.locked) return;
15713         if(this.last == index){
15714             this.last = false;
15715         }
15716         if(this.lastActive == index){
15717             this.lastActive = false;
15718         }
15719         var r = this.grid.dataSource.getAt(index);
15720         this.selections.remove(r);
15721         if(!preventViewNotify){
15722             this.grid.getView().onRowDeselect(index);
15723         }
15724         this.fireEvent("rowdeselect", this, index);
15725         this.fireEvent("selectionchange", this);
15726     },
15727
15728     // private
15729     restoreLast : function(){
15730         if(this._last){
15731             this.last = this._last;
15732         }
15733     },
15734
15735     // private
15736     acceptsNav : function(row, col, cm){
15737         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15738     },
15739
15740     // private
15741     onEditorKey : function(field, e){
15742         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15743         if(k == e.TAB){
15744             e.stopEvent();
15745             ed.completeEdit();
15746             if(e.shiftKey){
15747                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15748             }else{
15749                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15750             }
15751         }else if(k == e.ENTER && !e.ctrlKey){
15752             e.stopEvent();
15753             ed.completeEdit();
15754             if(e.shiftKey){
15755                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15756             }else{
15757                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15758             }
15759         }else if(k == e.ESC){
15760             ed.cancelEdit();
15761         }
15762         if(newCell){
15763             g.startEditing(newCell[0], newCell[1]);
15764         }
15765     }
15766 });/*
15767  * - LGPL
15768  *
15769  * element
15770  * 
15771  */
15772
15773 /**
15774  * @class Roo.bootstrap.MessageBar
15775  * @extends Roo.bootstrap.Component
15776  * Bootstrap MessageBar class
15777  * @cfg {String} html contents of the MessageBar
15778  * @cfg {String} weight (info | success | warning | danger) default info
15779  * @cfg {String} beforeClass insert the bar before the given class
15780  * @cfg {Boolean} closable (true | false) default false
15781  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15782  * 
15783  * @constructor
15784  * Create a new Element
15785  * @param {Object} config The config object
15786  */
15787
15788 Roo.bootstrap.MessageBar = function(config){
15789     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15790 };
15791
15792 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15793     
15794     html: '',
15795     weight: 'info',
15796     closable: false,
15797     fixed: false,
15798     beforeClass: 'bootstrap-sticky-wrap',
15799     
15800     getAutoCreate : function(){
15801         
15802         var cfg = {
15803             tag: 'div',
15804             cls: 'alert alert-dismissable alert-' + this.weight,
15805             cn: [
15806                 {
15807                     tag: 'span',
15808                     cls: 'message',
15809                     html: this.html || ''
15810                 }
15811             ]
15812         }
15813         
15814         if(this.fixed){
15815             cfg.cls += ' alert-messages-fixed';
15816         }
15817         
15818         if(this.closable){
15819             cfg.cn.push({
15820                 tag: 'button',
15821                 cls: 'close',
15822                 html: 'x'
15823             });
15824         }
15825         
15826         return cfg;
15827     },
15828     
15829     onRender : function(ct, position)
15830     {
15831         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15832         
15833         if(!this.el){
15834             var cfg = Roo.apply({},  this.getAutoCreate());
15835             cfg.id = Roo.id();
15836             
15837             if (this.cls) {
15838                 cfg.cls += ' ' + this.cls;
15839             }
15840             if (this.style) {
15841                 cfg.style = this.style;
15842             }
15843             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15844             
15845             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15846         }
15847         
15848         this.el.select('>button.close').on('click', this.hide, this);
15849         
15850     },
15851     
15852     show : function()
15853     {
15854         if (!this.rendered) {
15855             this.render();
15856         }
15857         
15858         this.el.show();
15859         
15860         this.fireEvent('show', this);
15861         
15862     },
15863     
15864     hide : function()
15865     {
15866         if (!this.rendered) {
15867             this.render();
15868         }
15869         
15870         this.el.hide();
15871         
15872         this.fireEvent('hide', this);
15873     },
15874     
15875     update : function()
15876     {
15877 //        var e = this.el.dom.firstChild;
15878 //        
15879 //        if(this.closable){
15880 //            e = e.nextSibling;
15881 //        }
15882 //        
15883 //        e.data = this.html || '';
15884
15885         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15886     }
15887    
15888 });
15889
15890  
15891
15892