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 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 //        this.maskEl.enableDisplayMode("block");
2955 //        this.maskEl.show();
2956         
2957         this.store.on('load', this.onLoad, this);
2958         this.store.on('beforeload', this.onBeforeLoad, this);
2959         
2960         this.store.load();
2961         
2962         
2963         
2964     },
2965     
2966     renderHeader : function()
2967     {
2968         var header = {
2969             tag: 'thead',
2970             cn : []
2971         };
2972         
2973         var cm = this.cm;
2974         var cls = '';
2975         var config = '';
2976         
2977         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2978             cls = 'glyphicon';
2979             
2980             config = cm.config[i];
2981             
2982             if(typeof(config.sortable) != 'undefined' && config.sortable){
2983                 cls += ' sortable';
2984             }
2985             
2986             header.cn.push({
2987                 tag: 'th',
2988                 cls: cls,
2989                 sort: (typeof(config.dataIndex) != 'undefined') ? config.dataIndex : '',
2990                 html: cm.getColumnHeader(i)
2991             })
2992         }
2993         
2994         return header;
2995     },
2996     
2997     renderBody : function()
2998     {
2999         var body = {
3000             tag: 'tbody',
3001             cn : []
3002         };
3003         
3004         return body;
3005     },
3006     
3007     renderFooter : function()
3008     {
3009         var footer = {
3010             tag: 'tfoot',
3011             cn : []
3012         };
3013         
3014         return footer;
3015     },
3016     
3017     onLoad : function()
3018     {
3019         Roo.log('ds onload');
3020         
3021         var cm = this.cm;
3022         
3023         var tbody = this.el.select('tbody', true).first();
3024         
3025         var renders = [];
3026         
3027         if(this.store.getCount() > 0){
3028             this.store.data.each(function(d){
3029                 var row = {
3030                     tag : 'tr',
3031                     cn : []
3032                 };
3033                 
3034                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3035                     var renderer = cm.getRenderer(i);
3036                     var config = cm.config[i];
3037                     var value = '';
3038                     var id = Roo.id();
3039                     
3040                     if(typeof(renderer) !== 'undefined'){
3041                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3042                     }
3043                     
3044                     if(typeof(value) === 'object'){
3045                         renders.push({
3046                             id : id,
3047                             cfg : value 
3048                         })
3049                     }
3050                     
3051                     var td = {
3052                         tag: 'td',
3053                         id: id,
3054                         html: (typeof(value) === 'object') ? '' : value
3055                     };
3056                     
3057                     if(typeof(config.width) != 'undefined'){
3058                         td.width = config.width;
3059                     }
3060                     
3061                     row.cn.push(td);
3062                    
3063                 }
3064                 
3065                 tbody.createChild(row);
3066                 
3067             });
3068         }
3069         
3070         
3071         if(renders.length){
3072             var _this = this;
3073             Roo.each(renders, function(r){
3074                 _this.renderColumn(r);
3075             })
3076         }
3077 //        
3078 //        if(this.loadMask){
3079 //            this.maskEl.hide();
3080 //        }
3081     },
3082     
3083     onBeforeLoad : function()
3084     {
3085         Roo.log('ds onBeforeLoad');
3086         
3087         this.clear();
3088         
3089 //        if(this.loadMask){
3090 //            this.maskEl.show();
3091 //        }
3092     },
3093     
3094     clear : function()
3095     {
3096         this.el.select('tbody', true).first().dom.innerHTML = '';
3097     },
3098     
3099     getSelectionModel : function(){
3100         if(!this.selModel){
3101             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3102         }
3103         return this.selModel;
3104     },
3105     
3106     renderColumn : function(r)
3107     {
3108         var _this = this;
3109         r.cfg.render(Roo.get(r.id));
3110         
3111         if(r.cfg.cn){
3112             Roo.each(r.cfg.cn, function(c){
3113                 var child = {
3114                     id: r.id,
3115                     cfg: c
3116                 }
3117                 _this.renderColumn(child);
3118             })
3119         }
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * - LGPL
3128  *
3129  * table cell
3130  * 
3131  */
3132
3133 /**
3134  * @class Roo.bootstrap.TableCell
3135  * @extends Roo.bootstrap.Component
3136  * Bootstrap TableCell class
3137  * @cfg {String} html cell contain text
3138  * @cfg {String} cls cell class
3139  * @cfg {String} tag cell tag (td|th) default td
3140  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3141  * @cfg {String} align Aligns the content in a cell
3142  * @cfg {String} axis Categorizes cells
3143  * @cfg {String} bgcolor Specifies the background color of a cell
3144  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3145  * @cfg {Number} colspan Specifies the number of columns a cell should span
3146  * @cfg {String} headers Specifies one or more header cells a cell is related to
3147  * @cfg {Number} height Sets the height of a cell
3148  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3149  * @cfg {Number} rowspan Sets the number of rows a cell should span
3150  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3151  * @cfg {String} valign Vertical aligns the content in a cell
3152  * @cfg {Number} width Specifies the width of a cell
3153  * 
3154  * @constructor
3155  * Create a new TableCell
3156  * @param {Object} config The config object
3157  */
3158
3159 Roo.bootstrap.TableCell = function(config){
3160     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3161 };
3162
3163 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3164     
3165     html: false,
3166     cls: false,
3167     tag: false,
3168     abbr: false,
3169     align: false,
3170     axis: false,
3171     bgcolor: false,
3172     charoff: false,
3173     colspan: false,
3174     headers: false,
3175     height: false,
3176     nowrap: false,
3177     rowspan: false,
3178     scope: false,
3179     valign: false,
3180     width: false,
3181     
3182     
3183     getAutoCreate : function(){
3184         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3185         
3186         cfg = {
3187             tag: 'td'
3188         }
3189         
3190         if(this.tag){
3191             cfg.tag = this.tag;
3192         }
3193         
3194         if (this.html) {
3195             cfg.html=this.html
3196         }
3197         if (this.cls) {
3198             cfg.cls=this.cls
3199         }
3200         if (this.abbr) {
3201             cfg.abbr=this.abbr
3202         }
3203         if (this.align) {
3204             cfg.align=this.align
3205         }
3206         if (this.axis) {
3207             cfg.axis=this.axis
3208         }
3209         if (this.bgcolor) {
3210             cfg.bgcolor=this.bgcolor
3211         }
3212         if (this.charoff) {
3213             cfg.charoff=this.charoff
3214         }
3215         if (this.colspan) {
3216             cfg.colspan=this.colspan
3217         }
3218         if (this.headers) {
3219             cfg.headers=this.headers
3220         }
3221         if (this.height) {
3222             cfg.height=this.height
3223         }
3224         if (this.nowrap) {
3225             cfg.nowrap=this.nowrap
3226         }
3227         if (this.rowspan) {
3228             cfg.rowspan=this.rowspan
3229         }
3230         if (this.scope) {
3231             cfg.scope=this.scope
3232         }
3233         if (this.valign) {
3234             cfg.valign=this.valign
3235         }
3236         if (this.width) {
3237             cfg.width=this.width
3238         }
3239         
3240         
3241         return cfg;
3242     }
3243    
3244 });
3245
3246  
3247
3248  /*
3249  * - LGPL
3250  *
3251  * table row
3252  * 
3253  */
3254
3255 /**
3256  * @class Roo.bootstrap.TableRow
3257  * @extends Roo.bootstrap.Component
3258  * Bootstrap TableRow class
3259  * @cfg {String} cls row class
3260  * @cfg {String} align Aligns the content in a table row
3261  * @cfg {String} bgcolor Specifies a background color for a table row
3262  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3263  * @cfg {String} valign Vertical aligns the content in a table row
3264  * 
3265  * @constructor
3266  * Create a new TableRow
3267  * @param {Object} config The config object
3268  */
3269
3270 Roo.bootstrap.TableRow = function(config){
3271     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3272 };
3273
3274 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3275     
3276     cls: false,
3277     align: false,
3278     bgcolor: false,
3279     charoff: false,
3280     valign: false,
3281     
3282     getAutoCreate : function(){
3283         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3284         
3285         cfg = {
3286             tag: 'tr'
3287         }
3288             
3289         if(this.cls){
3290             cfg.cls = this.cls;
3291         }
3292         if(this.align){
3293             cfg.align = this.align;
3294         }
3295         if(this.bgcolor){
3296             cfg.bgcolor = this.bgcolor;
3297         }
3298         if(this.charoff){
3299             cfg.charoff = this.charoff;
3300         }
3301         if(this.valign){
3302             cfg.valign = this.valign;
3303         }
3304         
3305         return cfg;
3306     }
3307    
3308 });
3309
3310  
3311
3312  /*
3313  * - LGPL
3314  *
3315  * table body
3316  * 
3317  */
3318
3319 /**
3320  * @class Roo.bootstrap.TableBody
3321  * @extends Roo.bootstrap.Component
3322  * Bootstrap TableBody class
3323  * @cfg {String} cls element class
3324  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3325  * @cfg {String} align Aligns the content inside the element
3326  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3327  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3328  * 
3329  * @constructor
3330  * Create a new TableBody
3331  * @param {Object} config The config object
3332  */
3333
3334 Roo.bootstrap.TableBody = function(config){
3335     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3336 };
3337
3338 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3339     
3340     cls: false,
3341     tag: false,
3342     align: false,
3343     charoff: false,
3344     valign: false,
3345     
3346     getAutoCreate : function(){
3347         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3348         
3349         cfg = {
3350             tag: 'tbody'
3351         }
3352             
3353         if (this.cls) {
3354             cfg.cls=this.cls
3355         }
3356         if(this.tag){
3357             cfg.tag = this.tag;
3358         }
3359         
3360         if(this.align){
3361             cfg.align = this.align;
3362         }
3363         if(this.charoff){
3364             cfg.charoff = this.charoff;
3365         }
3366         if(this.valign){
3367             cfg.valign = this.valign;
3368         }
3369         
3370         return cfg;
3371     }
3372     
3373     
3374 //    initEvents : function()
3375 //    {
3376 //        
3377 //        if(!this.store){
3378 //            return;
3379 //        }
3380 //        
3381 //        this.store = Roo.factory(this.store, Roo.data);
3382 //        this.store.on('load', this.onLoad, this);
3383 //        
3384 //        this.store.load();
3385 //        
3386 //    },
3387 //    
3388 //    onLoad: function () 
3389 //    {   
3390 //        this.fireEvent('load', this);
3391 //    }
3392 //    
3393 //   
3394 });
3395
3396  
3397
3398  /*
3399  * Based on:
3400  * Ext JS Library 1.1.1
3401  * Copyright(c) 2006-2007, Ext JS, LLC.
3402  *
3403  * Originally Released Under LGPL - original licence link has changed is not relivant.
3404  *
3405  * Fork - LGPL
3406  * <script type="text/javascript">
3407  */
3408
3409 // as we use this in bootstrap.
3410 Roo.namespace('Roo.form');
3411  /**
3412  * @class Roo.form.Action
3413  * Internal Class used to handle form actions
3414  * @constructor
3415  * @param {Roo.form.BasicForm} el The form element or its id
3416  * @param {Object} config Configuration options
3417  */
3418
3419  
3420  
3421 // define the action interface
3422 Roo.form.Action = function(form, options){
3423     this.form = form;
3424     this.options = options || {};
3425 };
3426 /**
3427  * Client Validation Failed
3428  * @const 
3429  */
3430 Roo.form.Action.CLIENT_INVALID = 'client';
3431 /**
3432  * Server Validation Failed
3433  * @const 
3434  */
3435 Roo.form.Action.SERVER_INVALID = 'server';
3436  /**
3437  * Connect to Server Failed
3438  * @const 
3439  */
3440 Roo.form.Action.CONNECT_FAILURE = 'connect';
3441 /**
3442  * Reading Data from Server Failed
3443  * @const 
3444  */
3445 Roo.form.Action.LOAD_FAILURE = 'load';
3446
3447 Roo.form.Action.prototype = {
3448     type : 'default',
3449     failureType : undefined,
3450     response : undefined,
3451     result : undefined,
3452
3453     // interface method
3454     run : function(options){
3455
3456     },
3457
3458     // interface method
3459     success : function(response){
3460
3461     },
3462
3463     // interface method
3464     handleResponse : function(response){
3465
3466     },
3467
3468     // default connection failure
3469     failure : function(response){
3470         
3471         this.response = response;
3472         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3473         this.form.afterAction(this, false);
3474     },
3475
3476     processResponse : function(response){
3477         this.response = response;
3478         if(!response.responseText){
3479             return true;
3480         }
3481         this.result = this.handleResponse(response);
3482         return this.result;
3483     },
3484
3485     // utility functions used internally
3486     getUrl : function(appendParams){
3487         var url = this.options.url || this.form.url || this.form.el.dom.action;
3488         if(appendParams){
3489             var p = this.getParams();
3490             if(p){
3491                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3492             }
3493         }
3494         return url;
3495     },
3496
3497     getMethod : function(){
3498         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3499     },
3500
3501     getParams : function(){
3502         var bp = this.form.baseParams;
3503         var p = this.options.params;
3504         if(p){
3505             if(typeof p == "object"){
3506                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3507             }else if(typeof p == 'string' && bp){
3508                 p += '&' + Roo.urlEncode(bp);
3509             }
3510         }else if(bp){
3511             p = Roo.urlEncode(bp);
3512         }
3513         return p;
3514     },
3515
3516     createCallback : function(){
3517         return {
3518             success: this.success,
3519             failure: this.failure,
3520             scope: this,
3521             timeout: (this.form.timeout*1000),
3522             upload: this.form.fileUpload ? this.success : undefined
3523         };
3524     }
3525 };
3526
3527 Roo.form.Action.Submit = function(form, options){
3528     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3529 };
3530
3531 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3532     type : 'submit',
3533
3534     haveProgress : false,
3535     uploadComplete : false,
3536     
3537     // uploadProgress indicator.
3538     uploadProgress : function()
3539     {
3540         if (!this.form.progressUrl) {
3541             return;
3542         }
3543         
3544         if (!this.haveProgress) {
3545             Roo.MessageBox.progress("Uploading", "Uploading");
3546         }
3547         if (this.uploadComplete) {
3548            Roo.MessageBox.hide();
3549            return;
3550         }
3551         
3552         this.haveProgress = true;
3553    
3554         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3555         
3556         var c = new Roo.data.Connection();
3557         c.request({
3558             url : this.form.progressUrl,
3559             params: {
3560                 id : uid
3561             },
3562             method: 'GET',
3563             success : function(req){
3564                //console.log(data);
3565                 var rdata = false;
3566                 var edata;
3567                 try  {
3568                    rdata = Roo.decode(req.responseText)
3569                 } catch (e) {
3570                     Roo.log("Invalid data from server..");
3571                     Roo.log(edata);
3572                     return;
3573                 }
3574                 if (!rdata || !rdata.success) {
3575                     Roo.log(rdata);
3576                     Roo.MessageBox.alert(Roo.encode(rdata));
3577                     return;
3578                 }
3579                 var data = rdata.data;
3580                 
3581                 if (this.uploadComplete) {
3582                    Roo.MessageBox.hide();
3583                    return;
3584                 }
3585                    
3586                 if (data){
3587                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3588                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3589                     );
3590                 }
3591                 this.uploadProgress.defer(2000,this);
3592             },
3593        
3594             failure: function(data) {
3595                 Roo.log('progress url failed ');
3596                 Roo.log(data);
3597             },
3598             scope : this
3599         });
3600            
3601     },
3602     
3603     
3604     run : function()
3605     {
3606         // run get Values on the form, so it syncs any secondary forms.
3607         this.form.getValues();
3608         
3609         var o = this.options;
3610         var method = this.getMethod();
3611         var isPost = method == 'POST';
3612         if(o.clientValidation === false || this.form.isValid()){
3613             
3614             if (this.form.progressUrl) {
3615                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3616                     (new Date() * 1) + '' + Math.random());
3617                     
3618             } 
3619             
3620             
3621             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3622                 form:this.form.el.dom,
3623                 url:this.getUrl(!isPost),
3624                 method: method,
3625                 params:isPost ? this.getParams() : null,
3626                 isUpload: this.form.fileUpload
3627             }));
3628             
3629             this.uploadProgress();
3630
3631         }else if (o.clientValidation !== false){ // client validation failed
3632             this.failureType = Roo.form.Action.CLIENT_INVALID;
3633             this.form.afterAction(this, false);
3634         }
3635     },
3636
3637     success : function(response)
3638     {
3639         this.uploadComplete= true;
3640         if (this.haveProgress) {
3641             Roo.MessageBox.hide();
3642         }
3643         
3644         
3645         var result = this.processResponse(response);
3646         if(result === true || result.success){
3647             this.form.afterAction(this, true);
3648             return;
3649         }
3650         if(result.errors){
3651             this.form.markInvalid(result.errors);
3652             this.failureType = Roo.form.Action.SERVER_INVALID;
3653         }
3654         this.form.afterAction(this, false);
3655     },
3656     failure : function(response)
3657     {
3658         this.uploadComplete= true;
3659         if (this.haveProgress) {
3660             Roo.MessageBox.hide();
3661         }
3662         
3663         this.response = response;
3664         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3665         this.form.afterAction(this, false);
3666     },
3667     
3668     handleResponse : function(response){
3669         if(this.form.errorReader){
3670             var rs = this.form.errorReader.read(response);
3671             var errors = [];
3672             if(rs.records){
3673                 for(var i = 0, len = rs.records.length; i < len; i++) {
3674                     var r = rs.records[i];
3675                     errors[i] = r.data;
3676                 }
3677             }
3678             if(errors.length < 1){
3679                 errors = null;
3680             }
3681             return {
3682                 success : rs.success,
3683                 errors : errors
3684             };
3685         }
3686         var ret = false;
3687         try {
3688             ret = Roo.decode(response.responseText);
3689         } catch (e) {
3690             ret = {
3691                 success: false,
3692                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3693                 errors : []
3694             };
3695         }
3696         return ret;
3697         
3698     }
3699 });
3700
3701
3702 Roo.form.Action.Load = function(form, options){
3703     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3704     this.reader = this.form.reader;
3705 };
3706
3707 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3708     type : 'load',
3709
3710     run : function(){
3711         
3712         Roo.Ajax.request(Roo.apply(
3713                 this.createCallback(), {
3714                     method:this.getMethod(),
3715                     url:this.getUrl(false),
3716                     params:this.getParams()
3717         }));
3718     },
3719
3720     success : function(response){
3721         
3722         var result = this.processResponse(response);
3723         if(result === true || !result.success || !result.data){
3724             this.failureType = Roo.form.Action.LOAD_FAILURE;
3725             this.form.afterAction(this, false);
3726             return;
3727         }
3728         this.form.clearInvalid();
3729         this.form.setValues(result.data);
3730         this.form.afterAction(this, true);
3731     },
3732
3733     handleResponse : function(response){
3734         if(this.form.reader){
3735             var rs = this.form.reader.read(response);
3736             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3737             return {
3738                 success : rs.success,
3739                 data : data
3740             };
3741         }
3742         return Roo.decode(response.responseText);
3743     }
3744 });
3745
3746 Roo.form.Action.ACTION_TYPES = {
3747     'load' : Roo.form.Action.Load,
3748     'submit' : Roo.form.Action.Submit
3749 };/*
3750  * - LGPL
3751  *
3752  * form
3753  * 
3754  */
3755
3756 /**
3757  * @class Roo.bootstrap.Form
3758  * @extends Roo.bootstrap.Component
3759  * Bootstrap Form class
3760  * @cfg {String} method  GET | POST (default POST)
3761  * @cfg {String} labelAlign top | left (default top)
3762   * @cfg {String} align left  | right - for navbars
3763
3764  * 
3765  * @constructor
3766  * Create a new Form
3767  * @param {Object} config The config object
3768  */
3769
3770
3771 Roo.bootstrap.Form = function(config){
3772     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3773     this.addEvents({
3774         /**
3775          * @event clientvalidation
3776          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3777          * @param {Form} this
3778          * @param {Boolean} valid true if the form has passed client-side validation
3779          */
3780         clientvalidation: true,
3781         /**
3782          * @event beforeaction
3783          * Fires before any action is performed. Return false to cancel the action.
3784          * @param {Form} this
3785          * @param {Action} action The action to be performed
3786          */
3787         beforeaction: true,
3788         /**
3789          * @event actionfailed
3790          * Fires when an action fails.
3791          * @param {Form} this
3792          * @param {Action} action The action that failed
3793          */
3794         actionfailed : true,
3795         /**
3796          * @event actioncomplete
3797          * Fires when an action is completed.
3798          * @param {Form} this
3799          * @param {Action} action The action that completed
3800          */
3801         actioncomplete : true
3802     });
3803     
3804 };
3805
3806 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3807       
3808      /**
3809      * @cfg {String} method
3810      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3811      */
3812     method : 'POST',
3813     /**
3814      * @cfg {String} url
3815      * The URL to use for form actions if one isn't supplied in the action options.
3816      */
3817     /**
3818      * @cfg {Boolean} fileUpload
3819      * Set to true if this form is a file upload.
3820      */
3821      
3822     /**
3823      * @cfg {Object} baseParams
3824      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3825      */
3826       
3827     /**
3828      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3829      */
3830     timeout: 30,
3831     /**
3832      * @cfg {Sting} align (left|right) for navbar forms
3833      */
3834     align : 'left',
3835
3836     // private
3837     activeAction : null,
3838  
3839     /**
3840      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3841      * element by passing it or its id or mask the form itself by passing in true.
3842      * @type Mixed
3843      */
3844     waitMsgTarget : false,
3845     
3846      
3847     
3848     /**
3849      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3850      * element by passing it or its id or mask the form itself by passing in true.
3851      * @type Mixed
3852      */
3853     
3854     getAutoCreate : function(){
3855         
3856         var cfg = {
3857             tag: 'form',
3858             method : this.method || 'POST',
3859             id : this.id || Roo.id(),
3860             cls : ''
3861         }
3862         if (this.parent().xtype.match(/^Nav/)) {
3863             cfg.cls = 'navbar-form navbar-' + this.align;
3864             
3865         }
3866         
3867         if (this.labelAlign == 'left' ) {
3868             cfg.cls += ' form-horizontal';
3869         }
3870         
3871         
3872         return cfg;
3873     },
3874     initEvents : function()
3875     {
3876         this.el.on('submit', this.onSubmit, this);
3877         
3878         
3879     },
3880     // private
3881     onSubmit : function(e){
3882         e.stopEvent();
3883     },
3884     
3885      /**
3886      * Returns true if client-side validation on the form is successful.
3887      * @return Boolean
3888      */
3889     isValid : function(){
3890         var items = this.getItems();
3891         var valid = true;
3892         items.each(function(f){
3893            if(!f.validate()){
3894                valid = false;
3895                
3896            }
3897         });
3898         return valid;
3899     },
3900     /**
3901      * Returns true if any fields in this form have changed since their original load.
3902      * @return Boolean
3903      */
3904     isDirty : function(){
3905         var dirty = false;
3906         var items = this.getItems();
3907         items.each(function(f){
3908            if(f.isDirty()){
3909                dirty = true;
3910                return false;
3911            }
3912            return true;
3913         });
3914         return dirty;
3915     },
3916      /**
3917      * Performs a predefined action (submit or load) or custom actions you define on this form.
3918      * @param {String} actionName The name of the action type
3919      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3920      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3921      * accept other config options):
3922      * <pre>
3923 Property          Type             Description
3924 ----------------  ---------------  ----------------------------------------------------------------------------------
3925 url               String           The url for the action (defaults to the form's url)
3926 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3927 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3928 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3929                                    validate the form on the client (defaults to false)
3930      * </pre>
3931      * @return {BasicForm} this
3932      */
3933     doAction : function(action, options){
3934         if(typeof action == 'string'){
3935             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3936         }
3937         if(this.fireEvent('beforeaction', this, action) !== false){
3938             this.beforeAction(action);
3939             action.run.defer(100, action);
3940         }
3941         return this;
3942     },
3943     
3944     // private
3945     beforeAction : function(action){
3946         var o = action.options;
3947         
3948         // not really supported yet.. ??
3949         
3950         //if(this.waitMsgTarget === true){
3951             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3952         //}else if(this.waitMsgTarget){
3953         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3954         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3955         //}else {
3956         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3957        // }
3958          
3959     },
3960
3961     // private
3962     afterAction : function(action, success){
3963         this.activeAction = null;
3964         var o = action.options;
3965         
3966         //if(this.waitMsgTarget === true){
3967             this.el.unmask();
3968         //}else if(this.waitMsgTarget){
3969         //    this.waitMsgTarget.unmask();
3970         //}else{
3971         //    Roo.MessageBox.updateProgress(1);
3972         //    Roo.MessageBox.hide();
3973        // }
3974         // 
3975         if(success){
3976             if(o.reset){
3977                 this.reset();
3978             }
3979             Roo.callback(o.success, o.scope, [this, action]);
3980             this.fireEvent('actioncomplete', this, action);
3981             
3982         }else{
3983             
3984             // failure condition..
3985             // we have a scenario where updates need confirming.
3986             // eg. if a locking scenario exists..
3987             // we look for { errors : { needs_confirm : true }} in the response.
3988             if (
3989                 (typeof(action.result) != 'undefined')  &&
3990                 (typeof(action.result.errors) != 'undefined')  &&
3991                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3992            ){
3993                 var _t = this;
3994                 Roo.log("not supported yet");
3995                  /*
3996                 
3997                 Roo.MessageBox.confirm(
3998                     "Change requires confirmation",
3999                     action.result.errorMsg,
4000                     function(r) {
4001                         if (r != 'yes') {
4002                             return;
4003                         }
4004                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4005                     }
4006                     
4007                 );
4008                 */
4009                 
4010                 
4011                 return;
4012             }
4013             
4014             Roo.callback(o.failure, o.scope, [this, action]);
4015             // show an error message if no failed handler is set..
4016             if (!this.hasListener('actionfailed')) {
4017                 Roo.log("need to add dialog support");
4018                 /*
4019                 Roo.MessageBox.alert("Error",
4020                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4021                         action.result.errorMsg :
4022                         "Saving Failed, please check your entries or try again"
4023                 );
4024                 */
4025             }
4026             
4027             this.fireEvent('actionfailed', this, action);
4028         }
4029         
4030     },
4031     /**
4032      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4033      * @param {String} id The value to search for
4034      * @return Field
4035      */
4036     findField : function(id){
4037         var items = this.getItems();
4038         var field = items.get(id);
4039         if(!field){
4040              items.each(function(f){
4041                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4042                     field = f;
4043                     return false;
4044                 }
4045                 return true;
4046             });
4047         }
4048         return field || null;
4049     },
4050      /**
4051      * Mark fields in this form invalid in bulk.
4052      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4053      * @return {BasicForm} this
4054      */
4055     markInvalid : function(errors){
4056         if(errors instanceof Array){
4057             for(var i = 0, len = errors.length; i < len; i++){
4058                 var fieldError = errors[i];
4059                 var f = this.findField(fieldError.id);
4060                 if(f){
4061                     f.markInvalid(fieldError.msg);
4062                 }
4063             }
4064         }else{
4065             var field, id;
4066             for(id in errors){
4067                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4068                     field.markInvalid(errors[id]);
4069                 }
4070             }
4071         }
4072         //Roo.each(this.childForms || [], function (f) {
4073         //    f.markInvalid(errors);
4074         //});
4075         
4076         return this;
4077     },
4078
4079     /**
4080      * Set values for fields in this form in bulk.
4081      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4082      * @return {BasicForm} this
4083      */
4084     setValues : function(values){
4085         if(values instanceof Array){ // array of objects
4086             for(var i = 0, len = values.length; i < len; i++){
4087                 var v = values[i];
4088                 var f = this.findField(v.id);
4089                 if(f){
4090                     f.setValue(v.value);
4091                     if(this.trackResetOnLoad){
4092                         f.originalValue = f.getValue();
4093                     }
4094                 }
4095             }
4096         }else{ // object hash
4097             var field, id;
4098             for(id in values){
4099                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4100                     
4101                     if (field.setFromData && 
4102                         field.valueField && 
4103                         field.displayField &&
4104                         // combos' with local stores can 
4105                         // be queried via setValue()
4106                         // to set their value..
4107                         (field.store && !field.store.isLocal)
4108                         ) {
4109                         // it's a combo
4110                         var sd = { };
4111                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4112                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4113                         field.setFromData(sd);
4114                         
4115                     } else {
4116                         field.setValue(values[id]);
4117                     }
4118                     
4119                     
4120                     if(this.trackResetOnLoad){
4121                         field.originalValue = field.getValue();
4122                     }
4123                 }
4124             }
4125         }
4126          
4127         //Roo.each(this.childForms || [], function (f) {
4128         //    f.setValues(values);
4129         //});
4130                 
4131         return this;
4132     },
4133
4134     /**
4135      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4136      * they are returned as an array.
4137      * @param {Boolean} asString
4138      * @return {Object}
4139      */
4140     getValues : function(asString){
4141         //if (this.childForms) {
4142             // copy values from the child forms
4143         //    Roo.each(this.childForms, function (f) {
4144         //        this.setValues(f.getValues());
4145         //    }, this);
4146         //}
4147         
4148         
4149         
4150         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4151         if(asString === true){
4152             return fs;
4153         }
4154         return Roo.urlDecode(fs);
4155     },
4156     
4157     /**
4158      * Returns the fields in this form as an object with key/value pairs. 
4159      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4160      * @return {Object}
4161      */
4162     getFieldValues : function(with_hidden)
4163     {
4164         var items = this.getItems();
4165         var ret = {};
4166         items.each(function(f){
4167             if (!f.getName()) {
4168                 return;
4169             }
4170             var v = f.getValue();
4171             if (f.inputType =='radio') {
4172                 if (typeof(ret[f.getName()]) == 'undefined') {
4173                     ret[f.getName()] = ''; // empty..
4174                 }
4175                 
4176                 if (!f.el.dom.checked) {
4177                     return;
4178                     
4179                 }
4180                 v = f.el.dom.value;
4181                 
4182             }
4183             
4184             // not sure if this supported any more..
4185             if ((typeof(v) == 'object') && f.getRawValue) {
4186                 v = f.getRawValue() ; // dates..
4187             }
4188             // combo boxes where name != hiddenName...
4189             if (f.name != f.getName()) {
4190                 ret[f.name] = f.getRawValue();
4191             }
4192             ret[f.getName()] = v;
4193         });
4194         
4195         return ret;
4196     },
4197
4198     /**
4199      * Clears all invalid messages in this form.
4200      * @return {BasicForm} this
4201      */
4202     clearInvalid : function(){
4203         var items = this.getItems();
4204         
4205         items.each(function(f){
4206            f.clearInvalid();
4207         });
4208         
4209         
4210         
4211         return this;
4212     },
4213
4214     /**
4215      * Resets this form.
4216      * @return {BasicForm} this
4217      */
4218     reset : function(){
4219         var items = this.getItems();
4220         items.each(function(f){
4221             f.reset();
4222         });
4223         
4224         Roo.each(this.childForms || [], function (f) {
4225             f.reset();
4226         });
4227        
4228         
4229         return this;
4230     },
4231     getItems : function()
4232     {
4233         var r=new Roo.util.MixedCollection(false, function(o){
4234             return o.id || (o.id = Roo.id());
4235         });
4236         var iter = function(el) {
4237             if (el.inputEl) {
4238                 r.add(el);
4239             }
4240             if (!el.items) {
4241                 return;
4242             }
4243             Roo.each(el.items,function(e) {
4244                 iter(e);
4245             });
4246             
4247             
4248         };
4249         iter(this);
4250         return r;
4251         
4252         
4253         
4254         
4255     }
4256     
4257 });
4258
4259  
4260 /*
4261  * Based on:
4262  * Ext JS Library 1.1.1
4263  * Copyright(c) 2006-2007, Ext JS, LLC.
4264  *
4265  * Originally Released Under LGPL - original licence link has changed is not relivant.
4266  *
4267  * Fork - LGPL
4268  * <script type="text/javascript">
4269  */
4270 /**
4271  * @class Roo.form.VTypes
4272  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4273  * @singleton
4274  */
4275 Roo.form.VTypes = function(){
4276     // closure these in so they are only created once.
4277     var alpha = /^[a-zA-Z_]+$/;
4278     var alphanum = /^[a-zA-Z0-9_]+$/;
4279     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4280     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4281
4282     // All these messages and functions are configurable
4283     return {
4284         /**
4285          * The function used to validate email addresses
4286          * @param {String} value The email address
4287          */
4288         'email' : function(v){
4289             return email.test(v);
4290         },
4291         /**
4292          * The error text to display when the email validation function returns false
4293          * @type String
4294          */
4295         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4296         /**
4297          * The keystroke filter mask to be applied on email input
4298          * @type RegExp
4299          */
4300         'emailMask' : /[a-z0-9_\.\-@]/i,
4301
4302         /**
4303          * The function used to validate URLs
4304          * @param {String} value The URL
4305          */
4306         'url' : function(v){
4307             return url.test(v);
4308         },
4309         /**
4310          * The error text to display when the url validation function returns false
4311          * @type String
4312          */
4313         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4314         
4315         /**
4316          * The function used to validate alpha values
4317          * @param {String} value The value
4318          */
4319         'alpha' : function(v){
4320             return alpha.test(v);
4321         },
4322         /**
4323          * The error text to display when the alpha validation function returns false
4324          * @type String
4325          */
4326         'alphaText' : 'This field should only contain letters and _',
4327         /**
4328          * The keystroke filter mask to be applied on alpha input
4329          * @type RegExp
4330          */
4331         'alphaMask' : /[a-z_]/i,
4332
4333         /**
4334          * The function used to validate alphanumeric values
4335          * @param {String} value The value
4336          */
4337         'alphanum' : function(v){
4338             return alphanum.test(v);
4339         },
4340         /**
4341          * The error text to display when the alphanumeric validation function returns false
4342          * @type String
4343          */
4344         'alphanumText' : 'This field should only contain letters, numbers and _',
4345         /**
4346          * The keystroke filter mask to be applied on alphanumeric input
4347          * @type RegExp
4348          */
4349         'alphanumMask' : /[a-z0-9_]/i
4350     };
4351 }();/*
4352  * - LGPL
4353  *
4354  * Input
4355  * 
4356  */
4357
4358 /**
4359  * @class Roo.bootstrap.Input
4360  * @extends Roo.bootstrap.Component
4361  * Bootstrap Input class
4362  * @cfg {Boolean} disabled is it disabled
4363  * @cfg {String} fieldLabel - the label associated
4364  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4365  * @cfg {String} name name of the input
4366  * @cfg {string} fieldLabel - the label associated
4367  * @cfg {string}  inputType - input / file submit ...
4368  * @cfg {string} placeholder - placeholder to put in text.
4369  * @cfg {string}  before - input group add on before
4370  * @cfg {string} after - input group add on after
4371  * @cfg {string} size - (lg|sm) or leave empty..
4372  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4373  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4374  * @cfg {Number} md colspan out of 12 for computer-sized screens
4375  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4376  * @cfg {string} value default value of the input
4377  * @cfg {Number} labelWidth set the width of label (0-12)
4378  * @cfg {String} labelAlign (top|left)
4379  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4380  * 
4381  * 
4382  * @constructor
4383  * Create a new Input
4384  * @param {Object} config The config object
4385  */
4386
4387 Roo.bootstrap.Input = function(config){
4388     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4389    
4390         this.addEvents({
4391             /**
4392              * @event focus
4393              * Fires when this field receives input focus.
4394              * @param {Roo.form.Field} this
4395              */
4396             focus : true,
4397             /**
4398              * @event blur
4399              * Fires when this field loses input focus.
4400              * @param {Roo.form.Field} this
4401              */
4402             blur : true,
4403             /**
4404              * @event specialkey
4405              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4406              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4407              * @param {Roo.form.Field} this
4408              * @param {Roo.EventObject} e The event object
4409              */
4410             specialkey : true,
4411             /**
4412              * @event change
4413              * Fires just before the field blurs if the field value has changed.
4414              * @param {Roo.form.Field} this
4415              * @param {Mixed} newValue The new value
4416              * @param {Mixed} oldValue The original value
4417              */
4418             change : true,
4419             /**
4420              * @event invalid
4421              * Fires after the field has been marked as invalid.
4422              * @param {Roo.form.Field} this
4423              * @param {String} msg The validation message
4424              */
4425             invalid : true,
4426             /**
4427              * @event valid
4428              * Fires after the field has been validated with no errors.
4429              * @param {Roo.form.Field} this
4430              */
4431             valid : true,
4432              /**
4433              * @event keyup
4434              * Fires after the key up
4435              * @param {Roo.form.Field} this
4436              * @param {Roo.EventObject}  e The event Object
4437              */
4438             keyup : true
4439         });
4440 };
4441
4442 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4443      /**
4444      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4445       automatic validation (defaults to "keyup").
4446      */
4447     validationEvent : "keyup",
4448      /**
4449      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4450      */
4451     validateOnBlur : true,
4452     /**
4453      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4454      */
4455     validationDelay : 250,
4456      /**
4457      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4458      */
4459     focusClass : "x-form-focus",  // not needed???
4460     
4461        
4462     /**
4463      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4464      */
4465     invalidClass : "has-error",
4466     
4467     /**
4468      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4469      */
4470     selectOnFocus : false,
4471     
4472      /**
4473      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4474      */
4475     maskRe : null,
4476        /**
4477      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4478      */
4479     vtype : null,
4480     
4481       /**
4482      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4483      */
4484     disableKeyFilter : false,
4485     
4486        /**
4487      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4488      */
4489     disabled : false,
4490      /**
4491      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4492      */
4493     allowBlank : true,
4494     /**
4495      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4496      */
4497     blankText : "This field is required",
4498     
4499      /**
4500      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4501      */
4502     minLength : 0,
4503     /**
4504      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4505      */
4506     maxLength : Number.MAX_VALUE,
4507     /**
4508      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4509      */
4510     minLengthText : "The minimum length for this field is {0}",
4511     /**
4512      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4513      */
4514     maxLengthText : "The maximum length for this field is {0}",
4515   
4516     
4517     /**
4518      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4519      * If available, this function will be called only after the basic validators all return true, and will be passed the
4520      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4521      */
4522     validator : null,
4523     /**
4524      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4525      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4526      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4527      */
4528     regex : null,
4529     /**
4530      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4531      */
4532     regexText : "",
4533     
4534     
4535     
4536     fieldLabel : '',
4537     inputType : 'text',
4538     
4539     name : false,
4540     placeholder: false,
4541     before : false,
4542     after : false,
4543     size : false,
4544     // private
4545     hasFocus : false,
4546     preventMark: false,
4547     isFormField : true,
4548     value : '',
4549     labelWidth : 2,
4550     labelAlign : false,
4551     readOnly : false,
4552     
4553     parentLabelAlign : function()
4554     {
4555         var parent = this;
4556         while (parent.parent()) {
4557             parent = parent.parent();
4558             if (typeof(parent.labelAlign) !='undefined') {
4559                 return parent.labelAlign;
4560             }
4561         }
4562         return 'left';
4563         
4564     },
4565     
4566     getAutoCreate : function(){
4567         
4568         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4569         
4570         var id = Roo.id();
4571         
4572         var cfg = {};
4573         
4574         if(this.inputType != 'hidden'){
4575             cfg.cls = 'form-group' //input-group
4576         }
4577         
4578         var input =  {
4579             tag: 'input',
4580             id : id,
4581             type : this.inputType,
4582             value : this.value,
4583             cls : 'form-control',
4584             placeholder : this.placeholder || ''
4585             
4586         };
4587         
4588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4589             input.maxLength = this.maxLength;
4590         }
4591         
4592         if (this.disabled) {
4593             input.disabled=true;
4594         }
4595         
4596         if (this.readOnly) {
4597             input.readonly=true;
4598         }
4599         
4600         if (this.name) {
4601             input.name = this.name;
4602         }
4603         if (this.size) {
4604             input.cls += ' input-' + this.size;
4605         }
4606         var settings=this;
4607         ['xs','sm','md','lg'].map(function(size){
4608             if (settings[size]) {
4609                 cfg.cls += ' col-' + size + '-' + settings[size];
4610             }
4611         });
4612         
4613         var inputblock = input;
4614         
4615         if (this.before || this.after) {
4616             
4617             inputblock = {
4618                 cls : 'input-group',
4619                 cn :  [] 
4620             };
4621             if (this.before) {
4622                 inputblock.cn.push({
4623                     tag :'span',
4624                     cls : 'input-group-addon',
4625                     html : this.before
4626                 });
4627             }
4628             inputblock.cn.push(input);
4629             if (this.after) {
4630                 inputblock.cn.push({
4631                     tag :'span',
4632                     cls : 'input-group-addon',
4633                     html : this.after
4634                 });
4635             }
4636             
4637         };
4638         
4639         if (align ==='left' && this.fieldLabel.length) {
4640                 Roo.log("left and has label");
4641                 cfg.cn = [
4642                     
4643                     {
4644                         tag: 'label',
4645                         'for' :  id,
4646                         cls : 'control-label col-sm-' + this.labelWidth,
4647                         html : this.fieldLabel
4648                         
4649                     },
4650                     {
4651                         cls : "col-sm-" + (12 - this.labelWidth), 
4652                         cn: [
4653                             inputblock
4654                         ]
4655                     }
4656                     
4657                 ];
4658         } else if ( this.fieldLabel.length) {
4659                 Roo.log(" label");
4660                  cfg.cn = [
4661                    
4662                     {
4663                         tag: 'label',
4664                         //cls : 'input-group-addon',
4665                         html : this.fieldLabel
4666                         
4667                     },
4668                     
4669                     inputblock
4670                     
4671                 ];
4672
4673         } else {
4674             
4675                 Roo.log(" no label && no align");
4676                 cfg.cn = [
4677                     
4678                         inputblock
4679                     
4680                 ];
4681                 
4682                 
4683         };
4684         Roo.log('input-parentType: ' + this.parentType);
4685         
4686         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4687            cfg.cls += ' navbar-form';
4688            Roo.log(cfg);
4689         }
4690         
4691         return cfg;
4692         
4693     },
4694     /**
4695      * return the real input element.
4696      */
4697     inputEl: function ()
4698     {
4699         return this.el.select('input.form-control',true).first();
4700     },
4701     setDisabled : function(v)
4702     {
4703         var i  = this.inputEl().dom;
4704         if (!v) {
4705             i.removeAttribute('disabled');
4706             return;
4707             
4708         }
4709         i.setAttribute('disabled','true');
4710     },
4711     initEvents : function()
4712     {
4713         
4714         this.inputEl().on("keydown" , this.fireKey,  this);
4715         this.inputEl().on("focus", this.onFocus,  this);
4716         this.inputEl().on("blur", this.onBlur,  this);
4717         
4718         this.inputEl().relayEvent('keyup', this);
4719
4720         // reference to original value for reset
4721         this.originalValue = this.getValue();
4722         //Roo.form.TextField.superclass.initEvents.call(this);
4723         if(this.validationEvent == 'keyup'){
4724             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4725             this.inputEl().on('keyup', this.filterValidation, this);
4726         }
4727         else if(this.validationEvent !== false){
4728             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4729         }
4730         
4731         if(this.selectOnFocus){
4732             this.on("focus", this.preFocus, this);
4733             
4734         }
4735         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4736             this.inputEl().on("keypress", this.filterKeys, this);
4737         }
4738        /* if(this.grow){
4739             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4740             this.el.on("click", this.autoSize,  this);
4741         }
4742         */
4743         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4744             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4745         }
4746         
4747     },
4748     filterValidation : function(e){
4749         if(!e.isNavKeyPress()){
4750             this.validationTask.delay(this.validationDelay);
4751         }
4752     },
4753      /**
4754      * Validates the field value
4755      * @return {Boolean} True if the value is valid, else false
4756      */
4757     validate : function(){
4758         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4759         if(this.disabled || this.validateValue(this.getRawValue())){
4760             this.clearInvalid();
4761             return true;
4762         }
4763         return false;
4764     },
4765     
4766     
4767     /**
4768      * Validates a value according to the field's validation rules and marks the field as invalid
4769      * if the validation fails
4770      * @param {Mixed} value The value to validate
4771      * @return {Boolean} True if the value is valid, else false
4772      */
4773     validateValue : function(value){
4774         if(value.length < 1)  { // if it's blank
4775              if(this.allowBlank){
4776                 this.clearInvalid();
4777                 return true;
4778              }else{
4779                 this.markInvalid(this.blankText);
4780                 return false;
4781              }
4782         }
4783         if(value.length < this.minLength){
4784             this.markInvalid(String.format(this.minLengthText, this.minLength));
4785             return false;
4786         }
4787         if(value.length > this.maxLength){
4788             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4789             return false;
4790         }
4791         if(this.vtype){
4792             var vt = Roo.form.VTypes;
4793             if(!vt[this.vtype](value, this)){
4794                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4795                 return false;
4796             }
4797         }
4798         if(typeof this.validator == "function"){
4799             var msg = this.validator(value);
4800             if(msg !== true){
4801                 this.markInvalid(msg);
4802                 return false;
4803             }
4804         }
4805         if(this.regex && !this.regex.test(value)){
4806             this.markInvalid(this.regexText);
4807             return false;
4808         }
4809         return true;
4810     },
4811
4812     
4813     
4814      // private
4815     fireKey : function(e){
4816         //Roo.log('field ' + e.getKey());
4817         if(e.isNavKeyPress()){
4818             this.fireEvent("specialkey", this, e);
4819         }
4820     },
4821     focus : function (selectText){
4822         if(this.rendered){
4823             this.inputEl().focus();
4824             if(selectText === true){
4825                 this.inputEl().dom.select();
4826             }
4827         }
4828         return this;
4829     } ,
4830     
4831     onFocus : function(){
4832         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4833            // this.el.addClass(this.focusClass);
4834         }
4835         if(!this.hasFocus){
4836             this.hasFocus = true;
4837             this.startValue = this.getValue();
4838             this.fireEvent("focus", this);
4839         }
4840     },
4841     
4842     beforeBlur : Roo.emptyFn,
4843
4844     
4845     // private
4846     onBlur : function(){
4847         this.beforeBlur();
4848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4849             //this.el.removeClass(this.focusClass);
4850         }
4851         this.hasFocus = false;
4852         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4853             this.validate();
4854         }
4855         var v = this.getValue();
4856         if(String(v) !== String(this.startValue)){
4857             this.fireEvent('change', this, v, this.startValue);
4858         }
4859         this.fireEvent("blur", this);
4860     },
4861     
4862     /**
4863      * Resets the current field value to the originally loaded value and clears any validation messages
4864      */
4865     reset : function(){
4866         this.setValue(this.originalValue);
4867         this.clearInvalid();
4868     },
4869      /**
4870      * Returns the name of the field
4871      * @return {Mixed} name The name field
4872      */
4873     getName: function(){
4874         return this.name;
4875     },
4876      /**
4877      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4878      * @return {Mixed} value The field value
4879      */
4880     getValue : function(){
4881         return this.inputEl().getValue();
4882     },
4883     /**
4884      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4885      * @return {Mixed} value The field value
4886      */
4887     getRawValue : function(){
4888         var v = this.inputEl().getValue();
4889         
4890         return v;
4891     },
4892     
4893     /**
4894      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4895      * @param {Mixed} value The value to set
4896      */
4897     setRawValue : function(v){
4898         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4899     },
4900     
4901     selectText : function(start, end){
4902         var v = this.getRawValue();
4903         if(v.length > 0){
4904             start = start === undefined ? 0 : start;
4905             end = end === undefined ? v.length : end;
4906             var d = this.inputEl().dom;
4907             if(d.setSelectionRange){
4908                 d.setSelectionRange(start, end);
4909             }else if(d.createTextRange){
4910                 var range = d.createTextRange();
4911                 range.moveStart("character", start);
4912                 range.moveEnd("character", v.length-end);
4913                 range.select();
4914             }
4915         }
4916     },
4917     
4918     /**
4919      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4920      * @param {Mixed} value The value to set
4921      */
4922     setValue : function(v){
4923         this.value = v;
4924         if(this.rendered){
4925             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4926             this.validate();
4927         }
4928     },
4929     
4930     /*
4931     processValue : function(value){
4932         if(this.stripCharsRe){
4933             var newValue = value.replace(this.stripCharsRe, '');
4934             if(newValue !== value){
4935                 this.setRawValue(newValue);
4936                 return newValue;
4937             }
4938         }
4939         return value;
4940     },
4941   */
4942     preFocus : function(){
4943         
4944         if(this.selectOnFocus){
4945             this.inputEl().dom.select();
4946         }
4947     },
4948     filterKeys : function(e){
4949         var k = e.getKey();
4950         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4951             return;
4952         }
4953         var c = e.getCharCode(), cc = String.fromCharCode(c);
4954         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4955             return;
4956         }
4957         if(!this.maskRe.test(cc)){
4958             e.stopEvent();
4959         }
4960     },
4961      /**
4962      * Clear any invalid styles/messages for this field
4963      */
4964     clearInvalid : function(){
4965         
4966         if(!this.el || this.preventMark){ // not rendered
4967             return;
4968         }
4969         this.el.removeClass(this.invalidClass);
4970         /*
4971         switch(this.msgTarget){
4972             case 'qtip':
4973                 this.el.dom.qtip = '';
4974                 break;
4975             case 'title':
4976                 this.el.dom.title = '';
4977                 break;
4978             case 'under':
4979                 if(this.errorEl){
4980                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4981                 }
4982                 break;
4983             case 'side':
4984                 if(this.errorIcon){
4985                     this.errorIcon.dom.qtip = '';
4986                     this.errorIcon.hide();
4987                     this.un('resize', this.alignErrorIcon, this);
4988                 }
4989                 break;
4990             default:
4991                 var t = Roo.getDom(this.msgTarget);
4992                 t.innerHTML = '';
4993                 t.style.display = 'none';
4994                 break;
4995         }
4996         */
4997         this.fireEvent('valid', this);
4998     },
4999      /**
5000      * Mark this field as invalid
5001      * @param {String} msg The validation message
5002      */
5003     markInvalid : function(msg){
5004         if(!this.el  || this.preventMark){ // not rendered
5005             return;
5006         }
5007         this.el.addClass(this.invalidClass);
5008         /*
5009         msg = msg || this.invalidText;
5010         switch(this.msgTarget){
5011             case 'qtip':
5012                 this.el.dom.qtip = msg;
5013                 this.el.dom.qclass = 'x-form-invalid-tip';
5014                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5015                     Roo.QuickTips.enable();
5016                 }
5017                 break;
5018             case 'title':
5019                 this.el.dom.title = msg;
5020                 break;
5021             case 'under':
5022                 if(!this.errorEl){
5023                     var elp = this.el.findParent('.x-form-element', 5, true);
5024                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5025                     this.errorEl.setWidth(elp.getWidth(true)-20);
5026                 }
5027                 this.errorEl.update(msg);
5028                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5029                 break;
5030             case 'side':
5031                 if(!this.errorIcon){
5032                     var elp = this.el.findParent('.x-form-element', 5, true);
5033                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5034                 }
5035                 this.alignErrorIcon();
5036                 this.errorIcon.dom.qtip = msg;
5037                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5038                 this.errorIcon.show();
5039                 this.on('resize', this.alignErrorIcon, this);
5040                 break;
5041             default:
5042                 var t = Roo.getDom(this.msgTarget);
5043                 t.innerHTML = msg;
5044                 t.style.display = this.msgDisplay;
5045                 break;
5046         }
5047         */
5048         this.fireEvent('invalid', this, msg);
5049     },
5050     // private
5051     SafariOnKeyDown : function(event)
5052     {
5053         // this is a workaround for a password hang bug on chrome/ webkit.
5054         
5055         var isSelectAll = false;
5056         
5057         if(this.inputEl().dom.selectionEnd > 0){
5058             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5059         }
5060         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5061             event.preventDefault();
5062             this.setValue('');
5063             return;
5064         }
5065         
5066         if(isSelectAll){ // backspace and delete key
5067             
5068             event.preventDefault();
5069             // this is very hacky as keydown always get's upper case.
5070             //
5071             var cc = String.fromCharCode(event.getCharCode());
5072             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5073             
5074         }
5075     },
5076     adjustWidth : function(tag, w){
5077         tag = tag.toLowerCase();
5078         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5079             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5080                 if(tag == 'input'){
5081                     return w + 2;
5082                 }
5083                 if(tag == 'textarea'){
5084                     return w-2;
5085                 }
5086             }else if(Roo.isOpera){
5087                 if(tag == 'input'){
5088                     return w + 2;
5089                 }
5090                 if(tag == 'textarea'){
5091                     return w-2;
5092                 }
5093             }
5094         }
5095         return w;
5096     }
5097     
5098 });
5099
5100  
5101 /*
5102  * - LGPL
5103  *
5104  * Input
5105  * 
5106  */
5107
5108 /**
5109  * @class Roo.bootstrap.TextArea
5110  * @extends Roo.bootstrap.Input
5111  * Bootstrap TextArea class
5112  * @cfg {Number} cols Specifies the visible width of a text area
5113  * @cfg {Number} rows Specifies the visible number of lines in a text area
5114  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5115  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5116  * @cfg {string} html text
5117  * 
5118  * @constructor
5119  * Create a new TextArea
5120  * @param {Object} config The config object
5121  */
5122
5123 Roo.bootstrap.TextArea = function(config){
5124     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5125    
5126 };
5127
5128 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5129      
5130     cols : false,
5131     rows : 5,
5132     readOnly : false,
5133     warp : 'soft',
5134     resize : false,
5135     value: false,
5136     html: false,
5137     
5138     getAutoCreate : function(){
5139         
5140         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5141         
5142         var id = Roo.id();
5143         
5144         var cfg = {};
5145         
5146         var input =  {
5147             tag: 'textarea',
5148             id : id,
5149             warp : this.warp,
5150             rows : this.rows,
5151             value : this.value || '',
5152             html: this.html || '',
5153             cls : 'form-control',
5154             placeholder : this.placeholder || '' 
5155             
5156         };
5157         
5158         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5159             input.maxLength = this.maxLength;
5160         }
5161         
5162         if(this.resize){
5163             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5164         }
5165         
5166         if(this.cols){
5167             input.cols = this.cols;
5168         }
5169         
5170         if (this.readOnly) {
5171             input.readonly = true;
5172         }
5173         
5174         if (this.name) {
5175             input.name = this.name;
5176         }
5177         
5178         if (this.size) {
5179             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5180         }
5181         
5182         var settings=this;
5183         ['xs','sm','md','lg'].map(function(size){
5184             if (settings[size]) {
5185                 cfg.cls += ' col-' + size + '-' + settings[size];
5186             }
5187         });
5188         
5189         var inputblock = input;
5190         
5191         if (this.before || this.after) {
5192             
5193             inputblock = {
5194                 cls : 'input-group',
5195                 cn :  [] 
5196             };
5197             if (this.before) {
5198                 inputblock.cn.push({
5199                     tag :'span',
5200                     cls : 'input-group-addon',
5201                     html : this.before
5202                 });
5203             }
5204             inputblock.cn.push(input);
5205             if (this.after) {
5206                 inputblock.cn.push({
5207                     tag :'span',
5208                     cls : 'input-group-addon',
5209                     html : this.after
5210                 });
5211             }
5212             
5213         }
5214         
5215         if (align ==='left' && this.fieldLabel.length) {
5216                 Roo.log("left and has label");
5217                 cfg.cn = [
5218                     
5219                     {
5220                         tag: 'label',
5221                         'for' :  id,
5222                         cls : 'control-label col-sm-' + this.labelWidth,
5223                         html : this.fieldLabel
5224                         
5225                     },
5226                     {
5227                         cls : "col-sm-" + (12 - this.labelWidth), 
5228                         cn: [
5229                             inputblock
5230                         ]
5231                     }
5232                     
5233                 ];
5234         } else if ( this.fieldLabel.length) {
5235                 Roo.log(" label");
5236                  cfg.cn = [
5237                    
5238                     {
5239                         tag: 'label',
5240                         //cls : 'input-group-addon',
5241                         html : this.fieldLabel
5242                         
5243                     },
5244                     
5245                     inputblock
5246                     
5247                 ];
5248
5249         } else {
5250             
5251                    Roo.log(" no label && no align");
5252                 cfg.cn = [
5253                     
5254                         inputblock
5255                     
5256                 ];
5257                 
5258                 
5259         }
5260         
5261         if (this.disabled) {
5262             input.disabled=true;
5263         }
5264         
5265         return cfg;
5266         
5267     },
5268     /**
5269      * return the real textarea element.
5270      */
5271     inputEl: function ()
5272     {
5273         return this.el.select('textarea.form-control',true).first();
5274     }
5275 });
5276
5277  
5278 /*
5279  * - LGPL
5280  *
5281  * trigger field - base class for combo..
5282  * 
5283  */
5284  
5285 /**
5286  * @class Roo.bootstrap.TriggerField
5287  * @extends Roo.bootstrap.Input
5288  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5289  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5290  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5291  * for which you can provide a custom implementation.  For example:
5292  * <pre><code>
5293 var trigger = new Roo.bootstrap.TriggerField();
5294 trigger.onTriggerClick = myTriggerFn;
5295 trigger.applyTo('my-field');
5296 </code></pre>
5297  *
5298  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5299  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5300  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5301  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5302  * @constructor
5303  * Create a new TriggerField.
5304  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5305  * to the base TextField)
5306  */
5307 Roo.bootstrap.TriggerField = function(config){
5308     this.mimicing = false;
5309     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5310 };
5311
5312 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5313     /**
5314      * @cfg {String} triggerClass A CSS class to apply to the trigger
5315      */
5316      /**
5317      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5318      */
5319     hideTrigger:false,
5320
5321     /** @cfg {Boolean} grow @hide */
5322     /** @cfg {Number} growMin @hide */
5323     /** @cfg {Number} growMax @hide */
5324
5325     /**
5326      * @hide 
5327      * @method
5328      */
5329     autoSize: Roo.emptyFn,
5330     // private
5331     monitorTab : true,
5332     // private
5333     deferHeight : true,
5334
5335     
5336     actionMode : 'wrap',
5337     
5338     
5339     
5340     getAutoCreate : function(){
5341        
5342         var parent = this.parent();
5343         
5344         var align = this.parentLabelAlign();
5345         
5346         var id = Roo.id();
5347         
5348         var cfg = {
5349             cls: 'form-group' //input-group
5350         };
5351         
5352         
5353         var input =  {
5354             tag: 'input',
5355             id : id,
5356             type : this.inputType,
5357             cls : 'form-control',
5358             autocomplete: 'off',
5359             placeholder : this.placeholder || '' 
5360             
5361         };
5362         if (this.name) {
5363             input.name = this.name;
5364         }
5365         if (this.size) {
5366             input.cls += ' input-' + this.size;
5367         }
5368         
5369         if (this.disabled) {
5370             input.disabled=true;
5371         }
5372         
5373         var inputblock = input;
5374         
5375         if (this.before || this.after) {
5376             
5377             inputblock = {
5378                 cls : 'input-group',
5379                 cn :  [] 
5380             };
5381             if (this.before) {
5382                 inputblock.cn.push({
5383                     tag :'span',
5384                     cls : 'input-group-addon',
5385                     html : this.before
5386                 });
5387             }
5388             inputblock.cn.push(input);
5389             if (this.after) {
5390                 inputblock.cn.push({
5391                     tag :'span',
5392                     cls : 'input-group-addon',
5393                     html : this.after
5394                 });
5395             }
5396             
5397         };
5398         
5399         var box = {
5400             tag: 'div',
5401             cn: [
5402                 {
5403                     tag: 'input',
5404                     type : 'hidden',
5405                     cls: 'form-hidden-field'
5406                 },
5407                 inputblock
5408             ]
5409             
5410         };
5411         
5412         if(this.multiple){
5413             Roo.log('multiple');
5414             
5415             box = {
5416                 tag: 'div',
5417                 cn: [
5418                     {
5419                         tag: 'input',
5420                         type : 'hidden',
5421                         cls: 'form-hidden-field'
5422                     },
5423                     {
5424                         tag: 'ul',
5425                         cls: 'select2-choices',
5426                         cn:[
5427                             {
5428                                 tag: 'li',
5429                                 cls: 'select2-search-field',
5430                                 cn: [
5431
5432                                     inputblock
5433                                 ]
5434                             }
5435                         ]
5436                     }
5437                 ]
5438             }
5439         };
5440         
5441         var combobox = {
5442             cls: 'select2-container input-group',
5443             cn: [
5444                 box,
5445                 {
5446                     tag: 'ul',
5447                     cls: 'typeahead typeahead-long dropdown-menu',
5448                     style: 'display:none'
5449                 }
5450             ]
5451         };
5452         
5453         if(!this.multiple){
5454             combobox.cn.push({
5455                 tag :'span',
5456                 cls : 'input-group-addon btn dropdown-toggle',
5457                 cn : [
5458                     {
5459                         tag: 'span',
5460                         cls: 'caret'
5461                     },
5462                     {
5463                         tag: 'span',
5464                         cls: 'combobox-clear',
5465                         cn  : [
5466                             {
5467                                 tag : 'i',
5468                                 cls: 'icon-remove'
5469                             }
5470                         ]
5471                     }
5472                 ]
5473
5474             })
5475         }
5476         
5477         if(this.multiple){
5478             combobox.cls += ' select2-container-multi';
5479         }
5480         
5481         if (align ==='left' && this.fieldLabel.length) {
5482             
5483                 Roo.log("left and has label");
5484                 cfg.cn = [
5485                     
5486                     {
5487                         tag: 'label',
5488                         'for' :  id,
5489                         cls : 'control-label col-sm-' + this.labelWidth,
5490                         html : this.fieldLabel
5491                         
5492                     },
5493                     {
5494                         cls : "col-sm-" + (12 - this.labelWidth), 
5495                         cn: [
5496                             combobox
5497                         ]
5498                     }
5499                     
5500                 ];
5501         } else if ( this.fieldLabel.length) {
5502                 Roo.log(" label");
5503                  cfg.cn = [
5504                    
5505                     {
5506                         tag: 'label',
5507                         //cls : 'input-group-addon',
5508                         html : this.fieldLabel
5509                         
5510                     },
5511                     
5512                     combobox
5513                     
5514                 ];
5515
5516         } else {
5517             
5518                 Roo.log(" no label && no align");
5519                 cfg = combobox
5520                      
5521                 
5522         }
5523          
5524         var settings=this;
5525         ['xs','sm','md','lg'].map(function(size){
5526             if (settings[size]) {
5527                 cfg.cls += ' col-' + size + '-' + settings[size];
5528             }
5529         });
5530         
5531         return cfg;
5532         
5533     },
5534     
5535     
5536     
5537     // private
5538     onResize : function(w, h){
5539 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5540 //        if(typeof w == 'number'){
5541 //            var x = w - this.trigger.getWidth();
5542 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5543 //            this.trigger.setStyle('left', x+'px');
5544 //        }
5545     },
5546
5547     // private
5548     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5549
5550     // private
5551     getResizeEl : function(){
5552         return this.inputEl();
5553     },
5554
5555     // private
5556     getPositionEl : function(){
5557         return this.inputEl();
5558     },
5559
5560     // private
5561     alignErrorIcon : function(){
5562         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5563     },
5564
5565     // private
5566     initEvents : function(){
5567         
5568         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5569         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5570         if(!this.multiple){
5571             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5572             if(this.hideTrigger){
5573                 this.trigger.setDisplayed(false);
5574             }
5575             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5576         }
5577         
5578         if(this.multiple){
5579             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5580         }
5581         
5582         //this.trigger.addClassOnOver('x-form-trigger-over');
5583         //this.trigger.addClassOnClick('x-form-trigger-click');
5584         
5585         //if(!this.width){
5586         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5587         //}
5588     },
5589
5590     // private
5591     initTrigger : function(){
5592        
5593     },
5594
5595     // private
5596     onDestroy : function(){
5597         if(this.trigger){
5598             this.trigger.removeAllListeners();
5599           //  this.trigger.remove();
5600         }
5601         //if(this.wrap){
5602         //    this.wrap.remove();
5603         //}
5604         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5605     },
5606
5607     // private
5608     onFocus : function(){
5609         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5610         /*
5611         if(!this.mimicing){
5612             this.wrap.addClass('x-trigger-wrap-focus');
5613             this.mimicing = true;
5614             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5615             if(this.monitorTab){
5616                 this.el.on("keydown", this.checkTab, this);
5617             }
5618         }
5619         */
5620     },
5621
5622     // private
5623     checkTab : function(e){
5624         if(e.getKey() == e.TAB){
5625             this.triggerBlur();
5626         }
5627     },
5628
5629     // private
5630     onBlur : function(){
5631         // do nothing
5632     },
5633
5634     // private
5635     mimicBlur : function(e, t){
5636         /*
5637         if(!this.wrap.contains(t) && this.validateBlur()){
5638             this.triggerBlur();
5639         }
5640         */
5641     },
5642
5643     // private
5644     triggerBlur : function(){
5645         this.mimicing = false;
5646         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5647         if(this.monitorTab){
5648             this.el.un("keydown", this.checkTab, this);
5649         }
5650         //this.wrap.removeClass('x-trigger-wrap-focus');
5651         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5652     },
5653
5654     // private
5655     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5656     validateBlur : function(e, t){
5657         return true;
5658     },
5659
5660     // private
5661     onDisable : function(){
5662         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5663         //if(this.wrap){
5664         //    this.wrap.addClass('x-item-disabled');
5665         //}
5666     },
5667
5668     // private
5669     onEnable : function(){
5670         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5671         //if(this.wrap){
5672         //    this.el.removeClass('x-item-disabled');
5673         //}
5674     },
5675
5676     // private
5677     onShow : function(){
5678         var ae = this.getActionEl();
5679         
5680         if(ae){
5681             ae.dom.style.display = '';
5682             ae.dom.style.visibility = 'visible';
5683         }
5684     },
5685
5686     // private
5687     
5688     onHide : function(){
5689         var ae = this.getActionEl();
5690         ae.dom.style.display = 'none';
5691     },
5692
5693     /**
5694      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5695      * by an implementing function.
5696      * @method
5697      * @param {EventObject} e
5698      */
5699     onTriggerClick : Roo.emptyFn
5700 });
5701  /*
5702  * Based on:
5703  * Ext JS Library 1.1.1
5704  * Copyright(c) 2006-2007, Ext JS, LLC.
5705  *
5706  * Originally Released Under LGPL - original licence link has changed is not relivant.
5707  *
5708  * Fork - LGPL
5709  * <script type="text/javascript">
5710  */
5711
5712
5713 /**
5714  * @class Roo.data.SortTypes
5715  * @singleton
5716  * Defines the default sorting (casting?) comparison functions used when sorting data.
5717  */
5718 Roo.data.SortTypes = {
5719     /**
5720      * Default sort that does nothing
5721      * @param {Mixed} s The value being converted
5722      * @return {Mixed} The comparison value
5723      */
5724     none : function(s){
5725         return s;
5726     },
5727     
5728     /**
5729      * The regular expression used to strip tags
5730      * @type {RegExp}
5731      * @property
5732      */
5733     stripTagsRE : /<\/?[^>]+>/gi,
5734     
5735     /**
5736      * Strips all HTML tags to sort on text only
5737      * @param {Mixed} s The value being converted
5738      * @return {String} The comparison value
5739      */
5740     asText : function(s){
5741         return String(s).replace(this.stripTagsRE, "");
5742     },
5743     
5744     /**
5745      * Strips all HTML tags to sort on text only - Case insensitive
5746      * @param {Mixed} s The value being converted
5747      * @return {String} The comparison value
5748      */
5749     asUCText : function(s){
5750         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5751     },
5752     
5753     /**
5754      * Case insensitive string
5755      * @param {Mixed} s The value being converted
5756      * @return {String} The comparison value
5757      */
5758     asUCString : function(s) {
5759         return String(s).toUpperCase();
5760     },
5761     
5762     /**
5763      * Date sorting
5764      * @param {Mixed} s The value being converted
5765      * @return {Number} The comparison value
5766      */
5767     asDate : function(s) {
5768         if(!s){
5769             return 0;
5770         }
5771         if(s instanceof Date){
5772             return s.getTime();
5773         }
5774         return Date.parse(String(s));
5775     },
5776     
5777     /**
5778      * Float sorting
5779      * @param {Mixed} s The value being converted
5780      * @return {Float} The comparison value
5781      */
5782     asFloat : function(s) {
5783         var val = parseFloat(String(s).replace(/,/g, ""));
5784         if(isNaN(val)) val = 0;
5785         return val;
5786     },
5787     
5788     /**
5789      * Integer sorting
5790      * @param {Mixed} s The value being converted
5791      * @return {Number} The comparison value
5792      */
5793     asInt : function(s) {
5794         var val = parseInt(String(s).replace(/,/g, ""));
5795         if(isNaN(val)) val = 0;
5796         return val;
5797     }
5798 };/*
5799  * Based on:
5800  * Ext JS Library 1.1.1
5801  * Copyright(c) 2006-2007, Ext JS, LLC.
5802  *
5803  * Originally Released Under LGPL - original licence link has changed is not relivant.
5804  *
5805  * Fork - LGPL
5806  * <script type="text/javascript">
5807  */
5808
5809 /**
5810 * @class Roo.data.Record
5811  * Instances of this class encapsulate both record <em>definition</em> information, and record
5812  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5813  * to access Records cached in an {@link Roo.data.Store} object.<br>
5814  * <p>
5815  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5816  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5817  * objects.<br>
5818  * <p>
5819  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5820  * @constructor
5821  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5822  * {@link #create}. The parameters are the same.
5823  * @param {Array} data An associative Array of data values keyed by the field name.
5824  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5825  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5826  * not specified an integer id is generated.
5827  */
5828 Roo.data.Record = function(data, id){
5829     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5830     this.data = data;
5831 };
5832
5833 /**
5834  * Generate a constructor for a specific record layout.
5835  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5836  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5837  * Each field definition object may contain the following properties: <ul>
5838  * <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,
5839  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5840  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5841  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5842  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5843  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5844  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5845  * this may be omitted.</p></li>
5846  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5847  * <ul><li>auto (Default, implies no conversion)</li>
5848  * <li>string</li>
5849  * <li>int</li>
5850  * <li>float</li>
5851  * <li>boolean</li>
5852  * <li>date</li></ul></p></li>
5853  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5854  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5855  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5856  * by the Reader into an object that will be stored in the Record. It is passed the
5857  * following parameters:<ul>
5858  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5859  * </ul></p></li>
5860  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5861  * </ul>
5862  * <br>usage:<br><pre><code>
5863 var TopicRecord = Roo.data.Record.create(
5864     {name: 'title', mapping: 'topic_title'},
5865     {name: 'author', mapping: 'username'},
5866     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5867     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5868     {name: 'lastPoster', mapping: 'user2'},
5869     {name: 'excerpt', mapping: 'post_text'}
5870 );
5871
5872 var myNewRecord = new TopicRecord({
5873     title: 'Do my job please',
5874     author: 'noobie',
5875     totalPosts: 1,
5876     lastPost: new Date(),
5877     lastPoster: 'Animal',
5878     excerpt: 'No way dude!'
5879 });
5880 myStore.add(myNewRecord);
5881 </code></pre>
5882  * @method create
5883  * @static
5884  */
5885 Roo.data.Record.create = function(o){
5886     var f = function(){
5887         f.superclass.constructor.apply(this, arguments);
5888     };
5889     Roo.extend(f, Roo.data.Record);
5890     var p = f.prototype;
5891     p.fields = new Roo.util.MixedCollection(false, function(field){
5892         return field.name;
5893     });
5894     for(var i = 0, len = o.length; i < len; i++){
5895         p.fields.add(new Roo.data.Field(o[i]));
5896     }
5897     f.getField = function(name){
5898         return p.fields.get(name);  
5899     };
5900     return f;
5901 };
5902
5903 Roo.data.Record.AUTO_ID = 1000;
5904 Roo.data.Record.EDIT = 'edit';
5905 Roo.data.Record.REJECT = 'reject';
5906 Roo.data.Record.COMMIT = 'commit';
5907
5908 Roo.data.Record.prototype = {
5909     /**
5910      * Readonly flag - true if this record has been modified.
5911      * @type Boolean
5912      */
5913     dirty : false,
5914     editing : false,
5915     error: null,
5916     modified: null,
5917
5918     // private
5919     join : function(store){
5920         this.store = store;
5921     },
5922
5923     /**
5924      * Set the named field to the specified value.
5925      * @param {String} name The name of the field to set.
5926      * @param {Object} value The value to set the field to.
5927      */
5928     set : function(name, value){
5929         if(this.data[name] == value){
5930             return;
5931         }
5932         this.dirty = true;
5933         if(!this.modified){
5934             this.modified = {};
5935         }
5936         if(typeof this.modified[name] == 'undefined'){
5937             this.modified[name] = this.data[name];
5938         }
5939         this.data[name] = value;
5940         if(!this.editing && this.store){
5941             this.store.afterEdit(this);
5942         }       
5943     },
5944
5945     /**
5946      * Get the value of the named field.
5947      * @param {String} name The name of the field to get the value of.
5948      * @return {Object} The value of the field.
5949      */
5950     get : function(name){
5951         return this.data[name]; 
5952     },
5953
5954     // private
5955     beginEdit : function(){
5956         this.editing = true;
5957         this.modified = {}; 
5958     },
5959
5960     // private
5961     cancelEdit : function(){
5962         this.editing = false;
5963         delete this.modified;
5964     },
5965
5966     // private
5967     endEdit : function(){
5968         this.editing = false;
5969         if(this.dirty && this.store){
5970             this.store.afterEdit(this);
5971         }
5972     },
5973
5974     /**
5975      * Usually called by the {@link Roo.data.Store} which owns the Record.
5976      * Rejects all changes made to the Record since either creation, or the last commit operation.
5977      * Modified fields are reverted to their original values.
5978      * <p>
5979      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5980      * of reject operations.
5981      */
5982     reject : function(){
5983         var m = this.modified;
5984         for(var n in m){
5985             if(typeof m[n] != "function"){
5986                 this.data[n] = m[n];
5987             }
5988         }
5989         this.dirty = false;
5990         delete this.modified;
5991         this.editing = false;
5992         if(this.store){
5993             this.store.afterReject(this);
5994         }
5995     },
5996
5997     /**
5998      * Usually called by the {@link Roo.data.Store} which owns the Record.
5999      * Commits all changes made to the Record since either creation, or the last commit operation.
6000      * <p>
6001      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6002      * of commit operations.
6003      */
6004     commit : function(){
6005         this.dirty = false;
6006         delete this.modified;
6007         this.editing = false;
6008         if(this.store){
6009             this.store.afterCommit(this);
6010         }
6011     },
6012
6013     // private
6014     hasError : function(){
6015         return this.error != null;
6016     },
6017
6018     // private
6019     clearError : function(){
6020         this.error = null;
6021     },
6022
6023     /**
6024      * Creates a copy of this record.
6025      * @param {String} id (optional) A new record id if you don't want to use this record's id
6026      * @return {Record}
6027      */
6028     copy : function(newId) {
6029         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6030     }
6031 };/*
6032  * Based on:
6033  * Ext JS Library 1.1.1
6034  * Copyright(c) 2006-2007, Ext JS, LLC.
6035  *
6036  * Originally Released Under LGPL - original licence link has changed is not relivant.
6037  *
6038  * Fork - LGPL
6039  * <script type="text/javascript">
6040  */
6041
6042
6043
6044 /**
6045  * @class Roo.data.Store
6046  * @extends Roo.util.Observable
6047  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6048  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6049  * <p>
6050  * 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
6051  * has no knowledge of the format of the data returned by the Proxy.<br>
6052  * <p>
6053  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6054  * instances from the data object. These records are cached and made available through accessor functions.
6055  * @constructor
6056  * Creates a new Store.
6057  * @param {Object} config A config object containing the objects needed for the Store to access data,
6058  * and read the data into Records.
6059  */
6060 Roo.data.Store = function(config){
6061     this.data = new Roo.util.MixedCollection(false);
6062     this.data.getKey = function(o){
6063         return o.id;
6064     };
6065     this.baseParams = {};
6066     // private
6067     this.paramNames = {
6068         "start" : "start",
6069         "limit" : "limit",
6070         "sort" : "sort",
6071         "dir" : "dir",
6072         "multisort" : "_multisort"
6073     };
6074
6075     if(config && config.data){
6076         this.inlineData = config.data;
6077         delete config.data;
6078     }
6079
6080     Roo.apply(this, config);
6081     
6082     if(this.reader){ // reader passed
6083         this.reader = Roo.factory(this.reader, Roo.data);
6084         this.reader.xmodule = this.xmodule || false;
6085         if(!this.recordType){
6086             this.recordType = this.reader.recordType;
6087         }
6088         if(this.reader.onMetaChange){
6089             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6090         }
6091     }
6092
6093     if(this.recordType){
6094         this.fields = this.recordType.prototype.fields;
6095     }
6096     this.modified = [];
6097
6098     this.addEvents({
6099         /**
6100          * @event datachanged
6101          * Fires when the data cache has changed, and a widget which is using this Store
6102          * as a Record cache should refresh its view.
6103          * @param {Store} this
6104          */
6105         datachanged : true,
6106         /**
6107          * @event metachange
6108          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6109          * @param {Store} this
6110          * @param {Object} meta The JSON metadata
6111          */
6112         metachange : true,
6113         /**
6114          * @event add
6115          * Fires when Records have been added to the Store
6116          * @param {Store} this
6117          * @param {Roo.data.Record[]} records The array of Records added
6118          * @param {Number} index The index at which the record(s) were added
6119          */
6120         add : true,
6121         /**
6122          * @event remove
6123          * Fires when a Record has been removed from the Store
6124          * @param {Store} this
6125          * @param {Roo.data.Record} record The Record that was removed
6126          * @param {Number} index The index at which the record was removed
6127          */
6128         remove : true,
6129         /**
6130          * @event update
6131          * Fires when a Record has been updated
6132          * @param {Store} this
6133          * @param {Roo.data.Record} record The Record that was updated
6134          * @param {String} operation The update operation being performed.  Value may be one of:
6135          * <pre><code>
6136  Roo.data.Record.EDIT
6137  Roo.data.Record.REJECT
6138  Roo.data.Record.COMMIT
6139          * </code></pre>
6140          */
6141         update : true,
6142         /**
6143          * @event clear
6144          * Fires when the data cache has been cleared.
6145          * @param {Store} this
6146          */
6147         clear : true,
6148         /**
6149          * @event beforeload
6150          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6151          * the load action will be canceled.
6152          * @param {Store} this
6153          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6154          */
6155         beforeload : true,
6156         /**
6157          * @event beforeloadadd
6158          * Fires after a new set of Records has been loaded.
6159          * @param {Store} this
6160          * @param {Roo.data.Record[]} records The Records that were loaded
6161          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6162          */
6163         beforeloadadd : true,
6164         /**
6165          * @event load
6166          * Fires after a new set of Records has been loaded, before they are added to the store.
6167          * @param {Store} this
6168          * @param {Roo.data.Record[]} records The Records that were loaded
6169          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6170          * @params {Object} return from reader
6171          */
6172         load : true,
6173         /**
6174          * @event loadexception
6175          * Fires if an exception occurs in the Proxy during loading.
6176          * Called with the signature of the Proxy's "loadexception" event.
6177          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6178          * 
6179          * @param {Proxy} 
6180          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6181          * @param {Object} load options 
6182          * @param {Object} jsonData from your request (normally this contains the Exception)
6183          */
6184         loadexception : true
6185     });
6186     
6187     if(this.proxy){
6188         this.proxy = Roo.factory(this.proxy, Roo.data);
6189         this.proxy.xmodule = this.xmodule || false;
6190         this.relayEvents(this.proxy,  ["loadexception"]);
6191     }
6192     this.sortToggle = {};
6193     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6194
6195     Roo.data.Store.superclass.constructor.call(this);
6196
6197     if(this.inlineData){
6198         this.loadData(this.inlineData);
6199         delete this.inlineData;
6200     }
6201 };
6202
6203 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6204      /**
6205     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6206     * without a remote query - used by combo/forms at present.
6207     */
6208     
6209     /**
6210     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6211     */
6212     /**
6213     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6214     */
6215     /**
6216     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6217     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6218     */
6219     /**
6220     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6221     * on any HTTP request
6222     */
6223     /**
6224     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6225     */
6226     /**
6227     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6228     */
6229     multiSort: false,
6230     /**
6231     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6232     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6233     */
6234     remoteSort : false,
6235
6236     /**
6237     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6238      * loaded or when a record is removed. (defaults to false).
6239     */
6240     pruneModifiedRecords : false,
6241
6242     // private
6243     lastOptions : null,
6244
6245     /**
6246      * Add Records to the Store and fires the add event.
6247      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6248      */
6249     add : function(records){
6250         records = [].concat(records);
6251         for(var i = 0, len = records.length; i < len; i++){
6252             records[i].join(this);
6253         }
6254         var index = this.data.length;
6255         this.data.addAll(records);
6256         this.fireEvent("add", this, records, index);
6257     },
6258
6259     /**
6260      * Remove a Record from the Store and fires the remove event.
6261      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6262      */
6263     remove : function(record){
6264         var index = this.data.indexOf(record);
6265         this.data.removeAt(index);
6266         if(this.pruneModifiedRecords){
6267             this.modified.remove(record);
6268         }
6269         this.fireEvent("remove", this, record, index);
6270     },
6271
6272     /**
6273      * Remove all Records from the Store and fires the clear event.
6274      */
6275     removeAll : function(){
6276         this.data.clear();
6277         if(this.pruneModifiedRecords){
6278             this.modified = [];
6279         }
6280         this.fireEvent("clear", this);
6281     },
6282
6283     /**
6284      * Inserts Records to the Store at the given index and fires the add event.
6285      * @param {Number} index The start index at which to insert the passed Records.
6286      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6287      */
6288     insert : function(index, records){
6289         records = [].concat(records);
6290         for(var i = 0, len = records.length; i < len; i++){
6291             this.data.insert(index, records[i]);
6292             records[i].join(this);
6293         }
6294         this.fireEvent("add", this, records, index);
6295     },
6296
6297     /**
6298      * Get the index within the cache of the passed Record.
6299      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6300      * @return {Number} The index of the passed Record. Returns -1 if not found.
6301      */
6302     indexOf : function(record){
6303         return this.data.indexOf(record);
6304     },
6305
6306     /**
6307      * Get the index within the cache of the Record with the passed id.
6308      * @param {String} id The id of the Record to find.
6309      * @return {Number} The index of the Record. Returns -1 if not found.
6310      */
6311     indexOfId : function(id){
6312         return this.data.indexOfKey(id);
6313     },
6314
6315     /**
6316      * Get the Record with the specified id.
6317      * @param {String} id The id of the Record to find.
6318      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6319      */
6320     getById : function(id){
6321         return this.data.key(id);
6322     },
6323
6324     /**
6325      * Get the Record at the specified index.
6326      * @param {Number} index The index of the Record to find.
6327      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6328      */
6329     getAt : function(index){
6330         return this.data.itemAt(index);
6331     },
6332
6333     /**
6334      * Returns a range of Records between specified indices.
6335      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6336      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6337      * @return {Roo.data.Record[]} An array of Records
6338      */
6339     getRange : function(start, end){
6340         return this.data.getRange(start, end);
6341     },
6342
6343     // private
6344     storeOptions : function(o){
6345         o = Roo.apply({}, o);
6346         delete o.callback;
6347         delete o.scope;
6348         this.lastOptions = o;
6349     },
6350
6351     /**
6352      * Loads the Record cache from the configured Proxy using the configured Reader.
6353      * <p>
6354      * If using remote paging, then the first load call must specify the <em>start</em>
6355      * and <em>limit</em> properties in the options.params property to establish the initial
6356      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6357      * <p>
6358      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6359      * and this call will return before the new data has been loaded. Perform any post-processing
6360      * in a callback function, or in a "load" event handler.</strong>
6361      * <p>
6362      * @param {Object} options An object containing properties which control loading options:<ul>
6363      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6364      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6365      * passed the following arguments:<ul>
6366      * <li>r : Roo.data.Record[]</li>
6367      * <li>options: Options object from the load call</li>
6368      * <li>success: Boolean success indicator</li></ul></li>
6369      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6370      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6371      * </ul>
6372      */
6373     load : function(options){
6374         options = options || {};
6375         if(this.fireEvent("beforeload", this, options) !== false){
6376             this.storeOptions(options);
6377             var p = Roo.apply(options.params || {}, this.baseParams);
6378             // if meta was not loaded from remote source.. try requesting it.
6379             if (!this.reader.metaFromRemote) {
6380                 p._requestMeta = 1;
6381             }
6382             if(this.sortInfo && this.remoteSort){
6383                 var pn = this.paramNames;
6384                 p[pn["sort"]] = this.sortInfo.field;
6385                 p[pn["dir"]] = this.sortInfo.direction;
6386             }
6387             if (this.multiSort) {
6388                 var pn = this.paramNames;
6389                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6390             }
6391             
6392             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6393         }
6394     },
6395
6396     /**
6397      * Reloads the Record cache from the configured Proxy using the configured Reader and
6398      * the options from the last load operation performed.
6399      * @param {Object} options (optional) An object containing properties which may override the options
6400      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6401      * the most recently used options are reused).
6402      */
6403     reload : function(options){
6404         this.load(Roo.applyIf(options||{}, this.lastOptions));
6405     },
6406
6407     // private
6408     // Called as a callback by the Reader during a load operation.
6409     loadRecords : function(o, options, success){
6410         if(!o || success === false){
6411             if(success !== false){
6412                 this.fireEvent("load", this, [], options, o);
6413             }
6414             if(options.callback){
6415                 options.callback.call(options.scope || this, [], options, false);
6416             }
6417             return;
6418         }
6419         // if data returned failure - throw an exception.
6420         if (o.success === false) {
6421             // show a message if no listener is registered.
6422             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6423                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6424             }
6425             // loadmask wil be hooked into this..
6426             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6427             return;
6428         }
6429         var r = o.records, t = o.totalRecords || r.length;
6430         
6431         this.fireEvent("beforeloadadd", this, r, options, o);
6432         
6433         if(!options || options.add !== true){
6434             if(this.pruneModifiedRecords){
6435                 this.modified = [];
6436             }
6437             for(var i = 0, len = r.length; i < len; i++){
6438                 r[i].join(this);
6439             }
6440             if(this.snapshot){
6441                 this.data = this.snapshot;
6442                 delete this.snapshot;
6443             }
6444             this.data.clear();
6445             this.data.addAll(r);
6446             this.totalLength = t;
6447             this.applySort();
6448             this.fireEvent("datachanged", this);
6449         }else{
6450             this.totalLength = Math.max(t, this.data.length+r.length);
6451             this.add(r);
6452         }
6453         this.fireEvent("load", this, r, options, o);
6454         if(options.callback){
6455             options.callback.call(options.scope || this, r, options, true);
6456         }
6457     },
6458
6459
6460     /**
6461      * Loads data from a passed data block. A Reader which understands the format of the data
6462      * must have been configured in the constructor.
6463      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6464      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6465      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6466      */
6467     loadData : function(o, append){
6468         var r = this.reader.readRecords(o);
6469         this.loadRecords(r, {add: append}, true);
6470     },
6471
6472     /**
6473      * Gets the number of cached records.
6474      * <p>
6475      * <em>If using paging, this may not be the total size of the dataset. If the data object
6476      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6477      * the data set size</em>
6478      */
6479     getCount : function(){
6480         return this.data.length || 0;
6481     },
6482
6483     /**
6484      * Gets the total number of records in the dataset as returned by the server.
6485      * <p>
6486      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6487      * the dataset size</em>
6488      */
6489     getTotalCount : function(){
6490         return this.totalLength || 0;
6491     },
6492
6493     /**
6494      * Returns the sort state of the Store as an object with two properties:
6495      * <pre><code>
6496  field {String} The name of the field by which the Records are sorted
6497  direction {String} The sort order, "ASC" or "DESC"
6498      * </code></pre>
6499      */
6500     getSortState : function(){
6501         return this.sortInfo;
6502     },
6503
6504     // private
6505     applySort : function(){
6506         if(this.sortInfo && !this.remoteSort){
6507             var s = this.sortInfo, f = s.field;
6508             var st = this.fields.get(f).sortType;
6509             var fn = function(r1, r2){
6510                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6511                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6512             };
6513             this.data.sort(s.direction, fn);
6514             if(this.snapshot && this.snapshot != this.data){
6515                 this.snapshot.sort(s.direction, fn);
6516             }
6517         }
6518     },
6519
6520     /**
6521      * Sets the default sort column and order to be used by the next load operation.
6522      * @param {String} fieldName The name of the field to sort by.
6523      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6524      */
6525     setDefaultSort : function(field, dir){
6526         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6527     },
6528
6529     /**
6530      * Sort the Records.
6531      * If remote sorting is used, the sort is performed on the server, and the cache is
6532      * reloaded. If local sorting is used, the cache is sorted internally.
6533      * @param {String} fieldName The name of the field to sort by.
6534      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6535      */
6536     sort : function(fieldName, dir){
6537         var f = this.fields.get(fieldName);
6538         if(!dir){
6539             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6540             
6541             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6542                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6543             }else{
6544                 dir = f.sortDir;
6545             }
6546         }
6547         this.sortToggle[f.name] = dir;
6548         this.sortInfo = {field: f.name, direction: dir};
6549         if(!this.remoteSort){
6550             this.applySort();
6551             this.fireEvent("datachanged", this);
6552         }else{
6553             this.load(this.lastOptions);
6554         }
6555     },
6556
6557     /**
6558      * Calls the specified function for each of the Records in the cache.
6559      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6560      * Returning <em>false</em> aborts and exits the iteration.
6561      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6562      */
6563     each : function(fn, scope){
6564         this.data.each(fn, scope);
6565     },
6566
6567     /**
6568      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6569      * (e.g., during paging).
6570      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6571      */
6572     getModifiedRecords : function(){
6573         return this.modified;
6574     },
6575
6576     // private
6577     createFilterFn : function(property, value, anyMatch){
6578         if(!value.exec){ // not a regex
6579             value = String(value);
6580             if(value.length == 0){
6581                 return false;
6582             }
6583             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6584         }
6585         return function(r){
6586             return value.test(r.data[property]);
6587         };
6588     },
6589
6590     /**
6591      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6592      * @param {String} property A field on your records
6593      * @param {Number} start The record index to start at (defaults to 0)
6594      * @param {Number} end The last record index to include (defaults to length - 1)
6595      * @return {Number} The sum
6596      */
6597     sum : function(property, start, end){
6598         var rs = this.data.items, v = 0;
6599         start = start || 0;
6600         end = (end || end === 0) ? end : rs.length-1;
6601
6602         for(var i = start; i <= end; i++){
6603             v += (rs[i].data[property] || 0);
6604         }
6605         return v;
6606     },
6607
6608     /**
6609      * Filter the records by a specified property.
6610      * @param {String} field A field on your records
6611      * @param {String/RegExp} value Either a string that the field
6612      * should start with or a RegExp to test against the field
6613      * @param {Boolean} anyMatch True to match any part not just the beginning
6614      */
6615     filter : function(property, value, anyMatch){
6616         var fn = this.createFilterFn(property, value, anyMatch);
6617         return fn ? this.filterBy(fn) : this.clearFilter();
6618     },
6619
6620     /**
6621      * Filter by a function. The specified function will be called with each
6622      * record in this data source. If the function returns true the record is included,
6623      * otherwise it is filtered.
6624      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6625      * @param {Object} scope (optional) The scope of the function (defaults to this)
6626      */
6627     filterBy : function(fn, scope){
6628         this.snapshot = this.snapshot || this.data;
6629         this.data = this.queryBy(fn, scope||this);
6630         this.fireEvent("datachanged", this);
6631     },
6632
6633     /**
6634      * Query the records by a specified property.
6635      * @param {String} field A field on your records
6636      * @param {String/RegExp} value Either a string that the field
6637      * should start with or a RegExp to test against the field
6638      * @param {Boolean} anyMatch True to match any part not just the beginning
6639      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6640      */
6641     query : function(property, value, anyMatch){
6642         var fn = this.createFilterFn(property, value, anyMatch);
6643         return fn ? this.queryBy(fn) : this.data.clone();
6644     },
6645
6646     /**
6647      * Query by a function. The specified function will be called with each
6648      * record in this data source. If the function returns true the record is included
6649      * in the results.
6650      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6651      * @param {Object} scope (optional) The scope of the function (defaults to this)
6652       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6653      **/
6654     queryBy : function(fn, scope){
6655         var data = this.snapshot || this.data;
6656         return data.filterBy(fn, scope||this);
6657     },
6658
6659     /**
6660      * Collects unique values for a particular dataIndex from this store.
6661      * @param {String} dataIndex The property to collect
6662      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6663      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6664      * @return {Array} An array of the unique values
6665      **/
6666     collect : function(dataIndex, allowNull, bypassFilter){
6667         var d = (bypassFilter === true && this.snapshot) ?
6668                 this.snapshot.items : this.data.items;
6669         var v, sv, r = [], l = {};
6670         for(var i = 0, len = d.length; i < len; i++){
6671             v = d[i].data[dataIndex];
6672             sv = String(v);
6673             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6674                 l[sv] = true;
6675                 r[r.length] = v;
6676             }
6677         }
6678         return r;
6679     },
6680
6681     /**
6682      * Revert to a view of the Record cache with no filtering applied.
6683      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6684      */
6685     clearFilter : function(suppressEvent){
6686         if(this.snapshot && this.snapshot != this.data){
6687             this.data = this.snapshot;
6688             delete this.snapshot;
6689             if(suppressEvent !== true){
6690                 this.fireEvent("datachanged", this);
6691             }
6692         }
6693     },
6694
6695     // private
6696     afterEdit : function(record){
6697         if(this.modified.indexOf(record) == -1){
6698             this.modified.push(record);
6699         }
6700         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6701     },
6702     
6703     // private
6704     afterReject : function(record){
6705         this.modified.remove(record);
6706         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6707     },
6708
6709     // private
6710     afterCommit : function(record){
6711         this.modified.remove(record);
6712         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6713     },
6714
6715     /**
6716      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6717      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6718      */
6719     commitChanges : function(){
6720         var m = this.modified.slice(0);
6721         this.modified = [];
6722         for(var i = 0, len = m.length; i < len; i++){
6723             m[i].commit();
6724         }
6725     },
6726
6727     /**
6728      * Cancel outstanding changes on all changed records.
6729      */
6730     rejectChanges : function(){
6731         var m = this.modified.slice(0);
6732         this.modified = [];
6733         for(var i = 0, len = m.length; i < len; i++){
6734             m[i].reject();
6735         }
6736     },
6737
6738     onMetaChange : function(meta, rtype, o){
6739         this.recordType = rtype;
6740         this.fields = rtype.prototype.fields;
6741         delete this.snapshot;
6742         this.sortInfo = meta.sortInfo || this.sortInfo;
6743         this.modified = [];
6744         this.fireEvent('metachange', this, this.reader.meta);
6745     },
6746     
6747     moveIndex : function(data, type)
6748     {
6749         var index = this.indexOf(data);
6750         
6751         var newIndex = index + type;
6752         
6753         this.remove(data);
6754         
6755         this.insert(newIndex, data);
6756         
6757     }
6758 });/*
6759  * Based on:
6760  * Ext JS Library 1.1.1
6761  * Copyright(c) 2006-2007, Ext JS, LLC.
6762  *
6763  * Originally Released Under LGPL - original licence link has changed is not relivant.
6764  *
6765  * Fork - LGPL
6766  * <script type="text/javascript">
6767  */
6768
6769 /**
6770  * @class Roo.data.SimpleStore
6771  * @extends Roo.data.Store
6772  * Small helper class to make creating Stores from Array data easier.
6773  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6774  * @cfg {Array} fields An array of field definition objects, or field name strings.
6775  * @cfg {Array} data The multi-dimensional array of data
6776  * @constructor
6777  * @param {Object} config
6778  */
6779 Roo.data.SimpleStore = function(config){
6780     Roo.data.SimpleStore.superclass.constructor.call(this, {
6781         isLocal : true,
6782         reader: new Roo.data.ArrayReader({
6783                 id: config.id
6784             },
6785             Roo.data.Record.create(config.fields)
6786         ),
6787         proxy : new Roo.data.MemoryProxy(config.data)
6788     });
6789     this.load();
6790 };
6791 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6792  * Based on:
6793  * Ext JS Library 1.1.1
6794  * Copyright(c) 2006-2007, Ext JS, LLC.
6795  *
6796  * Originally Released Under LGPL - original licence link has changed is not relivant.
6797  *
6798  * Fork - LGPL
6799  * <script type="text/javascript">
6800  */
6801
6802 /**
6803 /**
6804  * @extends Roo.data.Store
6805  * @class Roo.data.JsonStore
6806  * Small helper class to make creating Stores for JSON data easier. <br/>
6807 <pre><code>
6808 var store = new Roo.data.JsonStore({
6809     url: 'get-images.php',
6810     root: 'images',
6811     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6812 });
6813 </code></pre>
6814  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6815  * JsonReader and HttpProxy (unless inline data is provided).</b>
6816  * @cfg {Array} fields An array of field definition objects, or field name strings.
6817  * @constructor
6818  * @param {Object} config
6819  */
6820 Roo.data.JsonStore = function(c){
6821     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6822         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6823         reader: new Roo.data.JsonReader(c, c.fields)
6824     }));
6825 };
6826 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6827  * Based on:
6828  * Ext JS Library 1.1.1
6829  * Copyright(c) 2006-2007, Ext JS, LLC.
6830  *
6831  * Originally Released Under LGPL - original licence link has changed is not relivant.
6832  *
6833  * Fork - LGPL
6834  * <script type="text/javascript">
6835  */
6836
6837  
6838 Roo.data.Field = function(config){
6839     if(typeof config == "string"){
6840         config = {name: config};
6841     }
6842     Roo.apply(this, config);
6843     
6844     if(!this.type){
6845         this.type = "auto";
6846     }
6847     
6848     var st = Roo.data.SortTypes;
6849     // named sortTypes are supported, here we look them up
6850     if(typeof this.sortType == "string"){
6851         this.sortType = st[this.sortType];
6852     }
6853     
6854     // set default sortType for strings and dates
6855     if(!this.sortType){
6856         switch(this.type){
6857             case "string":
6858                 this.sortType = st.asUCString;
6859                 break;
6860             case "date":
6861                 this.sortType = st.asDate;
6862                 break;
6863             default:
6864                 this.sortType = st.none;
6865         }
6866     }
6867
6868     // define once
6869     var stripRe = /[\$,%]/g;
6870
6871     // prebuilt conversion function for this field, instead of
6872     // switching every time we're reading a value
6873     if(!this.convert){
6874         var cv, dateFormat = this.dateFormat;
6875         switch(this.type){
6876             case "":
6877             case "auto":
6878             case undefined:
6879                 cv = function(v){ return v; };
6880                 break;
6881             case "string":
6882                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6883                 break;
6884             case "int":
6885                 cv = function(v){
6886                     return v !== undefined && v !== null && v !== '' ?
6887                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6888                     };
6889                 break;
6890             case "float":
6891                 cv = function(v){
6892                     return v !== undefined && v !== null && v !== '' ?
6893                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6894                     };
6895                 break;
6896             case "bool":
6897             case "boolean":
6898                 cv = function(v){ return v === true || v === "true" || v == 1; };
6899                 break;
6900             case "date":
6901                 cv = function(v){
6902                     if(!v){
6903                         return '';
6904                     }
6905                     if(v instanceof Date){
6906                         return v;
6907                     }
6908                     if(dateFormat){
6909                         if(dateFormat == "timestamp"){
6910                             return new Date(v*1000);
6911                         }
6912                         return Date.parseDate(v, dateFormat);
6913                     }
6914                     var parsed = Date.parse(v);
6915                     return parsed ? new Date(parsed) : null;
6916                 };
6917              break;
6918             
6919         }
6920         this.convert = cv;
6921     }
6922 };
6923
6924 Roo.data.Field.prototype = {
6925     dateFormat: null,
6926     defaultValue: "",
6927     mapping: null,
6928     sortType : null,
6929     sortDir : "ASC"
6930 };/*
6931  * Based on:
6932  * Ext JS Library 1.1.1
6933  * Copyright(c) 2006-2007, Ext JS, LLC.
6934  *
6935  * Originally Released Under LGPL - original licence link has changed is not relivant.
6936  *
6937  * Fork - LGPL
6938  * <script type="text/javascript">
6939  */
6940  
6941 // Base class for reading structured data from a data source.  This class is intended to be
6942 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6943
6944 /**
6945  * @class Roo.data.DataReader
6946  * Base class for reading structured data from a data source.  This class is intended to be
6947  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6948  */
6949
6950 Roo.data.DataReader = function(meta, recordType){
6951     
6952     this.meta = meta;
6953     
6954     this.recordType = recordType instanceof Array ? 
6955         Roo.data.Record.create(recordType) : recordType;
6956 };
6957
6958 Roo.data.DataReader.prototype = {
6959      /**
6960      * Create an empty record
6961      * @param {Object} data (optional) - overlay some values
6962      * @return {Roo.data.Record} record created.
6963      */
6964     newRow :  function(d) {
6965         var da =  {};
6966         this.recordType.prototype.fields.each(function(c) {
6967             switch( c.type) {
6968                 case 'int' : da[c.name] = 0; break;
6969                 case 'date' : da[c.name] = new Date(); break;
6970                 case 'float' : da[c.name] = 0.0; break;
6971                 case 'boolean' : da[c.name] = false; break;
6972                 default : da[c.name] = ""; break;
6973             }
6974             
6975         });
6976         return new this.recordType(Roo.apply(da, d));
6977     }
6978     
6979 };/*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990 /**
6991  * @class Roo.data.DataProxy
6992  * @extends Roo.data.Observable
6993  * This class is an abstract base class for implementations which provide retrieval of
6994  * unformatted data objects.<br>
6995  * <p>
6996  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6997  * (of the appropriate type which knows how to parse the data object) to provide a block of
6998  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6999  * <p>
7000  * Custom implementations must implement the load method as described in
7001  * {@link Roo.data.HttpProxy#load}.
7002  */
7003 Roo.data.DataProxy = function(){
7004     this.addEvents({
7005         /**
7006          * @event beforeload
7007          * Fires before a network request is made to retrieve a data object.
7008          * @param {Object} This DataProxy object.
7009          * @param {Object} params The params parameter to the load function.
7010          */
7011         beforeload : true,
7012         /**
7013          * @event load
7014          * Fires before the load method's callback is called.
7015          * @param {Object} This DataProxy object.
7016          * @param {Object} o The data object.
7017          * @param {Object} arg The callback argument object passed to the load function.
7018          */
7019         load : true,
7020         /**
7021          * @event loadexception
7022          * Fires if an Exception occurs during data retrieval.
7023          * @param {Object} This DataProxy object.
7024          * @param {Object} o The data object.
7025          * @param {Object} arg The callback argument object passed to the load function.
7026          * @param {Object} e The Exception.
7027          */
7028         loadexception : true
7029     });
7030     Roo.data.DataProxy.superclass.constructor.call(this);
7031 };
7032
7033 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7034
7035     /**
7036      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7037      */
7038 /*
7039  * Based on:
7040  * Ext JS Library 1.1.1
7041  * Copyright(c) 2006-2007, Ext JS, LLC.
7042  *
7043  * Originally Released Under LGPL - original licence link has changed is not relivant.
7044  *
7045  * Fork - LGPL
7046  * <script type="text/javascript">
7047  */
7048 /**
7049  * @class Roo.data.MemoryProxy
7050  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7051  * to the Reader when its load method is called.
7052  * @constructor
7053  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7054  */
7055 Roo.data.MemoryProxy = function(data){
7056     if (data.data) {
7057         data = data.data;
7058     }
7059     Roo.data.MemoryProxy.superclass.constructor.call(this);
7060     this.data = data;
7061 };
7062
7063 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7064     /**
7065      * Load data from the requested source (in this case an in-memory
7066      * data object passed to the constructor), read the data object into
7067      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7068      * process that block using the passed callback.
7069      * @param {Object} params This parameter is not used by the MemoryProxy class.
7070      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7071      * object into a block of Roo.data.Records.
7072      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7073      * The function must be passed <ul>
7074      * <li>The Record block object</li>
7075      * <li>The "arg" argument from the load function</li>
7076      * <li>A boolean success indicator</li>
7077      * </ul>
7078      * @param {Object} scope The scope in which to call the callback
7079      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7080      */
7081     load : function(params, reader, callback, scope, arg){
7082         params = params || {};
7083         var result;
7084         try {
7085             result = reader.readRecords(this.data);
7086         }catch(e){
7087             this.fireEvent("loadexception", this, arg, null, e);
7088             callback.call(scope, null, arg, false);
7089             return;
7090         }
7091         callback.call(scope, result, arg, true);
7092     },
7093     
7094     // private
7095     update : function(params, records){
7096         
7097     }
7098 });/*
7099  * Based on:
7100  * Ext JS Library 1.1.1
7101  * Copyright(c) 2006-2007, Ext JS, LLC.
7102  *
7103  * Originally Released Under LGPL - original licence link has changed is not relivant.
7104  *
7105  * Fork - LGPL
7106  * <script type="text/javascript">
7107  */
7108 /**
7109  * @class Roo.data.HttpProxy
7110  * @extends Roo.data.DataProxy
7111  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7112  * configured to reference a certain URL.<br><br>
7113  * <p>
7114  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7115  * from which the running page was served.<br><br>
7116  * <p>
7117  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7118  * <p>
7119  * Be aware that to enable the browser to parse an XML document, the server must set
7120  * the Content-Type header in the HTTP response to "text/xml".
7121  * @constructor
7122  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7123  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7124  * will be used to make the request.
7125  */
7126 Roo.data.HttpProxy = function(conn){
7127     Roo.data.HttpProxy.superclass.constructor.call(this);
7128     // is conn a conn config or a real conn?
7129     this.conn = conn;
7130     this.useAjax = !conn || !conn.events;
7131   
7132 };
7133
7134 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7135     // thse are take from connection...
7136     
7137     /**
7138      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7139      */
7140     /**
7141      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7142      * extra parameters to each request made by this object. (defaults to undefined)
7143      */
7144     /**
7145      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7146      *  to each request made by this object. (defaults to undefined)
7147      */
7148     /**
7149      * @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)
7150      */
7151     /**
7152      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7153      */
7154      /**
7155      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7156      * @type Boolean
7157      */
7158   
7159
7160     /**
7161      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7162      * @type Boolean
7163      */
7164     /**
7165      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7166      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7167      * a finer-grained basis than the DataProxy events.
7168      */
7169     getConnection : function(){
7170         return this.useAjax ? Roo.Ajax : this.conn;
7171     },
7172
7173     /**
7174      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7175      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7176      * process that block using the passed callback.
7177      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7178      * for the request to the remote server.
7179      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7180      * object into a block of Roo.data.Records.
7181      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7182      * The function must be passed <ul>
7183      * <li>The Record block object</li>
7184      * <li>The "arg" argument from the load function</li>
7185      * <li>A boolean success indicator</li>
7186      * </ul>
7187      * @param {Object} scope The scope in which to call the callback
7188      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7189      */
7190     load : function(params, reader, callback, scope, arg){
7191         if(this.fireEvent("beforeload", this, params) !== false){
7192             var  o = {
7193                 params : params || {},
7194                 request: {
7195                     callback : callback,
7196                     scope : scope,
7197                     arg : arg
7198                 },
7199                 reader: reader,
7200                 callback : this.loadResponse,
7201                 scope: this
7202             };
7203             if(this.useAjax){
7204                 Roo.applyIf(o, this.conn);
7205                 if(this.activeRequest){
7206                     Roo.Ajax.abort(this.activeRequest);
7207                 }
7208                 this.activeRequest = Roo.Ajax.request(o);
7209             }else{
7210                 this.conn.request(o);
7211             }
7212         }else{
7213             callback.call(scope||this, null, arg, false);
7214         }
7215     },
7216
7217     // private
7218     loadResponse : function(o, success, response){
7219         delete this.activeRequest;
7220         if(!success){
7221             this.fireEvent("loadexception", this, o, response);
7222             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7223             return;
7224         }
7225         var result;
7226         try {
7227             result = o.reader.read(response);
7228         }catch(e){
7229             this.fireEvent("loadexception", this, o, response, e);
7230             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7231             return;
7232         }
7233         
7234         this.fireEvent("load", this, o, o.request.arg);
7235         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7236     },
7237
7238     // private
7239     update : function(dataSet){
7240
7241     },
7242
7243     // private
7244     updateResponse : function(dataSet){
7245
7246     }
7247 });/*
7248  * Based on:
7249  * Ext JS Library 1.1.1
7250  * Copyright(c) 2006-2007, Ext JS, LLC.
7251  *
7252  * Originally Released Under LGPL - original licence link has changed is not relivant.
7253  *
7254  * Fork - LGPL
7255  * <script type="text/javascript">
7256  */
7257
7258 /**
7259  * @class Roo.data.ScriptTagProxy
7260  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7261  * other than the originating domain of the running page.<br><br>
7262  * <p>
7263  * <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
7264  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7265  * <p>
7266  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7267  * source code that is used as the source inside a &lt;script> tag.<br><br>
7268  * <p>
7269  * In order for the browser to process the returned data, the server must wrap the data object
7270  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7271  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7272  * depending on whether the callback name was passed:
7273  * <p>
7274  * <pre><code>
7275 boolean scriptTag = false;
7276 String cb = request.getParameter("callback");
7277 if (cb != null) {
7278     scriptTag = true;
7279     response.setContentType("text/javascript");
7280 } else {
7281     response.setContentType("application/x-json");
7282 }
7283 Writer out = response.getWriter();
7284 if (scriptTag) {
7285     out.write(cb + "(");
7286 }
7287 out.print(dataBlock.toJsonString());
7288 if (scriptTag) {
7289     out.write(");");
7290 }
7291 </pre></code>
7292  *
7293  * @constructor
7294  * @param {Object} config A configuration object.
7295  */
7296 Roo.data.ScriptTagProxy = function(config){
7297     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7298     Roo.apply(this, config);
7299     this.head = document.getElementsByTagName("head")[0];
7300 };
7301
7302 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7303
7304 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7305     /**
7306      * @cfg {String} url The URL from which to request the data object.
7307      */
7308     /**
7309      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7310      */
7311     timeout : 30000,
7312     /**
7313      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7314      * the server the name of the callback function set up by the load call to process the returned data object.
7315      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7316      * javascript output which calls this named function passing the data object as its only parameter.
7317      */
7318     callbackParam : "callback",
7319     /**
7320      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7321      * name to the request.
7322      */
7323     nocache : true,
7324
7325     /**
7326      * Load data from the configured URL, read the data object into
7327      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7328      * process that block using the passed callback.
7329      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7330      * for the request to the remote server.
7331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7332      * object into a block of Roo.data.Records.
7333      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7334      * The function must be passed <ul>
7335      * <li>The Record block object</li>
7336      * <li>The "arg" argument from the load function</li>
7337      * <li>A boolean success indicator</li>
7338      * </ul>
7339      * @param {Object} scope The scope in which to call the callback
7340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7341      */
7342     load : function(params, reader, callback, scope, arg){
7343         if(this.fireEvent("beforeload", this, params) !== false){
7344
7345             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7346
7347             var url = this.url;
7348             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7349             if(this.nocache){
7350                 url += "&_dc=" + (new Date().getTime());
7351             }
7352             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7353             var trans = {
7354                 id : transId,
7355                 cb : "stcCallback"+transId,
7356                 scriptId : "stcScript"+transId,
7357                 params : params,
7358                 arg : arg,
7359                 url : url,
7360                 callback : callback,
7361                 scope : scope,
7362                 reader : reader
7363             };
7364             var conn = this;
7365
7366             window[trans.cb] = function(o){
7367                 conn.handleResponse(o, trans);
7368             };
7369
7370             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7371
7372             if(this.autoAbort !== false){
7373                 this.abort();
7374             }
7375
7376             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7377
7378             var script = document.createElement("script");
7379             script.setAttribute("src", url);
7380             script.setAttribute("type", "text/javascript");
7381             script.setAttribute("id", trans.scriptId);
7382             this.head.appendChild(script);
7383
7384             this.trans = trans;
7385         }else{
7386             callback.call(scope||this, null, arg, false);
7387         }
7388     },
7389
7390     // private
7391     isLoading : function(){
7392         return this.trans ? true : false;
7393     },
7394
7395     /**
7396      * Abort the current server request.
7397      */
7398     abort : function(){
7399         if(this.isLoading()){
7400             this.destroyTrans(this.trans);
7401         }
7402     },
7403
7404     // private
7405     destroyTrans : function(trans, isLoaded){
7406         this.head.removeChild(document.getElementById(trans.scriptId));
7407         clearTimeout(trans.timeoutId);
7408         if(isLoaded){
7409             window[trans.cb] = undefined;
7410             try{
7411                 delete window[trans.cb];
7412             }catch(e){}
7413         }else{
7414             // if hasn't been loaded, wait for load to remove it to prevent script error
7415             window[trans.cb] = function(){
7416                 window[trans.cb] = undefined;
7417                 try{
7418                     delete window[trans.cb];
7419                 }catch(e){}
7420             };
7421         }
7422     },
7423
7424     // private
7425     handleResponse : function(o, trans){
7426         this.trans = false;
7427         this.destroyTrans(trans, true);
7428         var result;
7429         try {
7430             result = trans.reader.readRecords(o);
7431         }catch(e){
7432             this.fireEvent("loadexception", this, o, trans.arg, e);
7433             trans.callback.call(trans.scope||window, null, trans.arg, false);
7434             return;
7435         }
7436         this.fireEvent("load", this, o, trans.arg);
7437         trans.callback.call(trans.scope||window, result, trans.arg, true);
7438     },
7439
7440     // private
7441     handleFailure : function(trans){
7442         this.trans = false;
7443         this.destroyTrans(trans, false);
7444         this.fireEvent("loadexception", this, null, trans.arg);
7445         trans.callback.call(trans.scope||window, null, trans.arg, false);
7446     }
7447 });/*
7448  * Based on:
7449  * Ext JS Library 1.1.1
7450  * Copyright(c) 2006-2007, Ext JS, LLC.
7451  *
7452  * Originally Released Under LGPL - original licence link has changed is not relivant.
7453  *
7454  * Fork - LGPL
7455  * <script type="text/javascript">
7456  */
7457
7458 /**
7459  * @class Roo.data.JsonReader
7460  * @extends Roo.data.DataReader
7461  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7462  * based on mappings in a provided Roo.data.Record constructor.
7463  * 
7464  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7465  * in the reply previously. 
7466  * 
7467  * <p>
7468  * Example code:
7469  * <pre><code>
7470 var RecordDef = Roo.data.Record.create([
7471     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7472     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7473 ]);
7474 var myReader = new Roo.data.JsonReader({
7475     totalProperty: "results",    // The property which contains the total dataset size (optional)
7476     root: "rows",                // The property which contains an Array of row objects
7477     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7478 }, RecordDef);
7479 </code></pre>
7480  * <p>
7481  * This would consume a JSON file like this:
7482  * <pre><code>
7483 { 'results': 2, 'rows': [
7484     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7485     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7486 }
7487 </code></pre>
7488  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7489  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7490  * paged from the remote server.
7491  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7492  * @cfg {String} root name of the property which contains the Array of row objects.
7493  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7494  * @constructor
7495  * Create a new JsonReader
7496  * @param {Object} meta Metadata configuration options
7497  * @param {Object} recordType Either an Array of field definition objects,
7498  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7499  */
7500 Roo.data.JsonReader = function(meta, recordType){
7501     
7502     meta = meta || {};
7503     // set some defaults:
7504     Roo.applyIf(meta, {
7505         totalProperty: 'total',
7506         successProperty : 'success',
7507         root : 'data',
7508         id : 'id'
7509     });
7510     
7511     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7512 };
7513 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7514     
7515     /**
7516      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7517      * Used by Store query builder to append _requestMeta to params.
7518      * 
7519      */
7520     metaFromRemote : false,
7521     /**
7522      * This method is only used by a DataProxy which has retrieved data from a remote server.
7523      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7524      * @return {Object} data A data block which is used by an Roo.data.Store object as
7525      * a cache of Roo.data.Records.
7526      */
7527     read : function(response){
7528         var json = response.responseText;
7529        
7530         var o = /* eval:var:o */ eval("("+json+")");
7531         if(!o) {
7532             throw {message: "JsonReader.read: Json object not found"};
7533         }
7534         
7535         if(o.metaData){
7536             
7537             delete this.ef;
7538             this.metaFromRemote = true;
7539             this.meta = o.metaData;
7540             this.recordType = Roo.data.Record.create(o.metaData.fields);
7541             this.onMetaChange(this.meta, this.recordType, o);
7542         }
7543         return this.readRecords(o);
7544     },
7545
7546     // private function a store will implement
7547     onMetaChange : function(meta, recordType, o){
7548
7549     },
7550
7551     /**
7552          * @ignore
7553          */
7554     simpleAccess: function(obj, subsc) {
7555         return obj[subsc];
7556     },
7557
7558         /**
7559          * @ignore
7560          */
7561     getJsonAccessor: function(){
7562         var re = /[\[\.]/;
7563         return function(expr) {
7564             try {
7565                 return(re.test(expr))
7566                     ? new Function("obj", "return obj." + expr)
7567                     : function(obj){
7568                         return obj[expr];
7569                     };
7570             } catch(e){}
7571             return Roo.emptyFn;
7572         };
7573     }(),
7574
7575     /**
7576      * Create a data block containing Roo.data.Records from an XML document.
7577      * @param {Object} o An object which contains an Array of row objects in the property specified
7578      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7579      * which contains the total size of the dataset.
7580      * @return {Object} data A data block which is used by an Roo.data.Store object as
7581      * a cache of Roo.data.Records.
7582      */
7583     readRecords : function(o){
7584         /**
7585          * After any data loads, the raw JSON data is available for further custom processing.
7586          * @type Object
7587          */
7588         this.o = o;
7589         var s = this.meta, Record = this.recordType,
7590             f = Record.prototype.fields, fi = f.items, fl = f.length;
7591
7592 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7593         if (!this.ef) {
7594             if(s.totalProperty) {
7595                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7596                 }
7597                 if(s.successProperty) {
7598                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7599                 }
7600                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7601                 if (s.id) {
7602                         var g = this.getJsonAccessor(s.id);
7603                         this.getId = function(rec) {
7604                                 var r = g(rec);
7605                                 return (r === undefined || r === "") ? null : r;
7606                         };
7607                 } else {
7608                         this.getId = function(){return null;};
7609                 }
7610             this.ef = [];
7611             for(var jj = 0; jj < fl; jj++){
7612                 f = fi[jj];
7613                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7614                 this.ef[jj] = this.getJsonAccessor(map);
7615             }
7616         }
7617
7618         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7619         if(s.totalProperty){
7620             var vt = parseInt(this.getTotal(o), 10);
7621             if(!isNaN(vt)){
7622                 totalRecords = vt;
7623             }
7624         }
7625         if(s.successProperty){
7626             var vs = this.getSuccess(o);
7627             if(vs === false || vs === 'false'){
7628                 success = false;
7629             }
7630         }
7631         var records = [];
7632             for(var i = 0; i < c; i++){
7633                     var n = root[i];
7634                 var values = {};
7635                 var id = this.getId(n);
7636                 for(var j = 0; j < fl; j++){
7637                     f = fi[j];
7638                 var v = this.ef[j](n);
7639                 if (!f.convert) {
7640                     Roo.log('missing convert for ' + f.name);
7641                     Roo.log(f);
7642                     continue;
7643                 }
7644                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7645                 }
7646                 var record = new Record(values, id);
7647                 record.json = n;
7648                 records[i] = record;
7649             }
7650             return {
7651             raw : o,
7652                 success : success,
7653                 records : records,
7654                 totalRecords : totalRecords
7655             };
7656     }
7657 });/*
7658  * Based on:
7659  * Ext JS Library 1.1.1
7660  * Copyright(c) 2006-2007, Ext JS, LLC.
7661  *
7662  * Originally Released Under LGPL - original licence link has changed is not relivant.
7663  *
7664  * Fork - LGPL
7665  * <script type="text/javascript">
7666  */
7667
7668 /**
7669  * @class Roo.data.ArrayReader
7670  * @extends Roo.data.DataReader
7671  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7672  * Each element of that Array represents a row of data fields. The
7673  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7674  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7675  * <p>
7676  * Example code:.
7677  * <pre><code>
7678 var RecordDef = Roo.data.Record.create([
7679     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7680     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7681 ]);
7682 var myReader = new Roo.data.ArrayReader({
7683     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7684 }, RecordDef);
7685 </code></pre>
7686  * <p>
7687  * This would consume an Array like this:
7688  * <pre><code>
7689 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7690   </code></pre>
7691  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7692  * @constructor
7693  * Create a new JsonReader
7694  * @param {Object} meta Metadata configuration options.
7695  * @param {Object} recordType Either an Array of field definition objects
7696  * as specified to {@link Roo.data.Record#create},
7697  * or an {@link Roo.data.Record} object
7698  * created using {@link Roo.data.Record#create}.
7699  */
7700 Roo.data.ArrayReader = function(meta, recordType){
7701     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7702 };
7703
7704 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7705     /**
7706      * Create a data block containing Roo.data.Records from an XML document.
7707      * @param {Object} o An Array of row objects which represents the dataset.
7708      * @return {Object} data A data block which is used by an Roo.data.Store object as
7709      * a cache of Roo.data.Records.
7710      */
7711     readRecords : function(o){
7712         var sid = this.meta ? this.meta.id : null;
7713         var recordType = this.recordType, fields = recordType.prototype.fields;
7714         var records = [];
7715         var root = o;
7716             for(var i = 0; i < root.length; i++){
7717                     var n = root[i];
7718                 var values = {};
7719                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7720                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7721                 var f = fields.items[j];
7722                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7723                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7724                 v = f.convert(v);
7725                 values[f.name] = v;
7726             }
7727                 var record = new recordType(values, id);
7728                 record.json = n;
7729                 records[records.length] = record;
7730             }
7731             return {
7732                 records : records,
7733                 totalRecords : records.length
7734             };
7735     }
7736 });/*
7737  * - LGPL
7738  * * 
7739  */
7740
7741 /**
7742  * @class Roo.bootstrap.ComboBox
7743  * @extends Roo.bootstrap.TriggerField
7744  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7745  * @cfg {Boolean} append (true|false) default false
7746  * @constructor
7747  * Create a new ComboBox.
7748  * @param {Object} config Configuration options
7749  */
7750 Roo.bootstrap.ComboBox = function(config){
7751     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7752     this.addEvents({
7753         /**
7754          * @event expand
7755          * Fires when the dropdown list is expanded
7756              * @param {Roo.bootstrap.ComboBox} combo This combo box
7757              */
7758         'expand' : true,
7759         /**
7760          * @event collapse
7761          * Fires when the dropdown list is collapsed
7762              * @param {Roo.bootstrap.ComboBox} combo This combo box
7763              */
7764         'collapse' : true,
7765         /**
7766          * @event beforeselect
7767          * Fires before a list item is selected. Return false to cancel the selection.
7768              * @param {Roo.bootstrap.ComboBox} combo This combo box
7769              * @param {Roo.data.Record} record The data record returned from the underlying store
7770              * @param {Number} index The index of the selected item in the dropdown list
7771              */
7772         'beforeselect' : true,
7773         /**
7774          * @event select
7775          * Fires when a list item is selected
7776              * @param {Roo.bootstrap.ComboBox} combo This combo box
7777              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7778              * @param {Number} index The index of the selected item in the dropdown list
7779              */
7780         'select' : true,
7781         /**
7782          * @event beforequery
7783          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7784          * The event object passed has these properties:
7785              * @param {Roo.bootstrap.ComboBox} combo This combo box
7786              * @param {String} query The query
7787              * @param {Boolean} forceAll true to force "all" query
7788              * @param {Boolean} cancel true to cancel the query
7789              * @param {Object} e The query event object
7790              */
7791         'beforequery': true,
7792          /**
7793          * @event add
7794          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7795              * @param {Roo.bootstrap.ComboBox} combo This combo box
7796              */
7797         'add' : true,
7798         /**
7799          * @event edit
7800          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7801              * @param {Roo.bootstrap.ComboBox} combo This combo box
7802              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7803              */
7804         'edit' : true,
7805         /**
7806          * @event remove
7807          * Fires when the remove value from the combobox array
7808              * @param {Roo.bootstrap.ComboBox} combo This combo box
7809              */
7810         'remove' : true
7811         
7812     });
7813     
7814     
7815     this.selectedIndex = -1;
7816     if(this.mode == 'local'){
7817         if(config.queryDelay === undefined){
7818             this.queryDelay = 10;
7819         }
7820         if(config.minChars === undefined){
7821             this.minChars = 0;
7822         }
7823     }
7824 };
7825
7826 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7827      
7828     /**
7829      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7830      * rendering into an Roo.Editor, defaults to false)
7831      */
7832     /**
7833      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7834      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7835      */
7836     /**
7837      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7838      */
7839     /**
7840      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7841      * the dropdown list (defaults to undefined, with no header element)
7842      */
7843
7844      /**
7845      * @cfg {String/Roo.Template} tpl The template to use to render the output
7846      */
7847      
7848      /**
7849      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7850      */
7851     listWidth: undefined,
7852     /**
7853      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7854      * mode = 'remote' or 'text' if mode = 'local')
7855      */
7856     displayField: undefined,
7857     /**
7858      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7859      * mode = 'remote' or 'value' if mode = 'local'). 
7860      * Note: use of a valueField requires the user make a selection
7861      * in order for a value to be mapped.
7862      */
7863     valueField: undefined,
7864     
7865     
7866     /**
7867      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7868      * field's data value (defaults to the underlying DOM element's name)
7869      */
7870     hiddenName: undefined,
7871     /**
7872      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7873      */
7874     listClass: '',
7875     /**
7876      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7877      */
7878     selectedClass: 'active',
7879     
7880     /**
7881      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7882      */
7883     shadow:'sides',
7884     /**
7885      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7886      * anchor positions (defaults to 'tl-bl')
7887      */
7888     listAlign: 'tl-bl?',
7889     /**
7890      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7891      */
7892     maxHeight: 300,
7893     /**
7894      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7895      * query specified by the allQuery config option (defaults to 'query')
7896      */
7897     triggerAction: 'query',
7898     /**
7899      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7900      * (defaults to 4, does not apply if editable = false)
7901      */
7902     minChars : 4,
7903     /**
7904      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7905      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7906      */
7907     typeAhead: false,
7908     /**
7909      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7910      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7911      */
7912     queryDelay: 500,
7913     /**
7914      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7915      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7916      */
7917     pageSize: 0,
7918     /**
7919      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7920      * when editable = true (defaults to false)
7921      */
7922     selectOnFocus:false,
7923     /**
7924      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7925      */
7926     queryParam: 'query',
7927     /**
7928      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7929      * when mode = 'remote' (defaults to 'Loading...')
7930      */
7931     loadingText: 'Loading...',
7932     /**
7933      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7934      */
7935     resizable: false,
7936     /**
7937      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7938      */
7939     handleHeight : 8,
7940     /**
7941      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7942      * traditional select (defaults to true)
7943      */
7944     editable: true,
7945     /**
7946      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7947      */
7948     allQuery: '',
7949     /**
7950      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7951      */
7952     mode: 'remote',
7953     /**
7954      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7955      * listWidth has a higher value)
7956      */
7957     minListWidth : 70,
7958     /**
7959      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7960      * allow the user to set arbitrary text into the field (defaults to false)
7961      */
7962     forceSelection:false,
7963     /**
7964      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7965      * if typeAhead = true (defaults to 250)
7966      */
7967     typeAheadDelay : 250,
7968     /**
7969      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7970      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7971      */
7972     valueNotFoundText : undefined,
7973     /**
7974      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7975      */
7976     blockFocus : false,
7977     
7978     /**
7979      * @cfg {Boolean} disableClear Disable showing of clear button.
7980      */
7981     disableClear : false,
7982     /**
7983      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7984      */
7985     alwaysQuery : false,
7986     
7987     /**
7988      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7989      */
7990     multiple : false,
7991     
7992     //private
7993     addicon : false,
7994     editicon: false,
7995     
7996     page: 0,
7997     hasQuery: false,
7998     append: false,
7999     loadNext: false,
8000     item: [],
8001     
8002     // element that contains real text value.. (when hidden is used..)
8003      
8004     // private
8005     initEvents: function(){
8006         
8007         if (!this.store) {
8008             throw "can not find store for combo";
8009         }
8010         this.store = Roo.factory(this.store, Roo.data);
8011         
8012         
8013         
8014         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8015         
8016         
8017         if(this.hiddenName){
8018             
8019             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8020             
8021             this.hiddenField.dom.value =
8022                 this.hiddenValue !== undefined ? this.hiddenValue :
8023                 this.value !== undefined ? this.value : '';
8024
8025             // prevent input submission
8026             this.el.dom.removeAttribute('name');
8027             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8028              
8029              
8030         }
8031         //if(Roo.isGecko){
8032         //    this.el.dom.setAttribute('autocomplete', 'off');
8033         //}
8034
8035         var cls = 'x-combo-list';
8036         this.list = this.el.select('ul.dropdown-menu',true).first();
8037
8038         //this.list = new Roo.Layer({
8039         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8040         //});
8041         
8042         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8043         this.list.setWidth(lw);
8044         
8045         this.list.on('mouseover', this.onViewOver, this);
8046         this.list.on('mousemove', this.onViewMove, this);
8047         
8048         this.list.on('scroll', this.onViewScroll, this);
8049         
8050         /*
8051         this.list.swallowEvent('mousewheel');
8052         this.assetHeight = 0;
8053
8054         if(this.title){
8055             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8056             this.assetHeight += this.header.getHeight();
8057         }
8058
8059         this.innerList = this.list.createChild({cls:cls+'-inner'});
8060         this.innerList.on('mouseover', this.onViewOver, this);
8061         this.innerList.on('mousemove', this.onViewMove, this);
8062         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8063         
8064         if(this.allowBlank && !this.pageSize && !this.disableClear){
8065             this.footer = this.list.createChild({cls:cls+'-ft'});
8066             this.pageTb = new Roo.Toolbar(this.footer);
8067            
8068         }
8069         if(this.pageSize){
8070             this.footer = this.list.createChild({cls:cls+'-ft'});
8071             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8072                     {pageSize: this.pageSize});
8073             
8074         }
8075         
8076         if (this.pageTb && this.allowBlank && !this.disableClear) {
8077             var _this = this;
8078             this.pageTb.add(new Roo.Toolbar.Fill(), {
8079                 cls: 'x-btn-icon x-btn-clear',
8080                 text: '&#160;',
8081                 handler: function()
8082                 {
8083                     _this.collapse();
8084                     _this.clearValue();
8085                     _this.onSelect(false, -1);
8086                 }
8087             });
8088         }
8089         if (this.footer) {
8090             this.assetHeight += this.footer.getHeight();
8091         }
8092         */
8093             
8094         if(!this.tpl){
8095             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8096         }
8097
8098         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8099             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8100         });
8101         //this.view.wrapEl.setDisplayed(false);
8102         this.view.on('click', this.onViewClick, this);
8103         
8104         
8105         
8106         this.store.on('beforeload', this.onBeforeLoad, this);
8107         this.store.on('load', this.onLoad, this);
8108         this.store.on('loadexception', this.onLoadException, this);
8109         /*
8110         if(this.resizable){
8111             this.resizer = new Roo.Resizable(this.list,  {
8112                pinned:true, handles:'se'
8113             });
8114             this.resizer.on('resize', function(r, w, h){
8115                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8116                 this.listWidth = w;
8117                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8118                 this.restrictHeight();
8119             }, this);
8120             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8121         }
8122         */
8123         if(!this.editable){
8124             this.editable = true;
8125             this.setEditable(false);
8126         }
8127         
8128         /*
8129         
8130         if (typeof(this.events.add.listeners) != 'undefined') {
8131             
8132             this.addicon = this.wrap.createChild(
8133                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8134        
8135             this.addicon.on('click', function(e) {
8136                 this.fireEvent('add', this);
8137             }, this);
8138         }
8139         if (typeof(this.events.edit.listeners) != 'undefined') {
8140             
8141             this.editicon = this.wrap.createChild(
8142                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8143             if (this.addicon) {
8144                 this.editicon.setStyle('margin-left', '40px');
8145             }
8146             this.editicon.on('click', function(e) {
8147                 
8148                 // we fire even  if inothing is selected..
8149                 this.fireEvent('edit', this, this.lastData );
8150                 
8151             }, this);
8152         }
8153         */
8154         
8155         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8156             "up" : function(e){
8157                 this.inKeyMode = true;
8158                 this.selectPrev();
8159             },
8160
8161             "down" : function(e){
8162                 if(!this.isExpanded()){
8163                     this.onTriggerClick();
8164                 }else{
8165                     this.inKeyMode = true;
8166                     this.selectNext();
8167                 }
8168             },
8169
8170             "enter" : function(e){
8171                 this.onViewClick();
8172                 //return true;
8173             },
8174
8175             "esc" : function(e){
8176                 this.collapse();
8177             },
8178
8179             "tab" : function(e){
8180                 this.collapse();
8181                 
8182                 if(this.fireEvent("specialkey", this, e)){
8183                     this.onViewClick(false);
8184                 }
8185                 
8186                 return true;
8187             },
8188
8189             scope : this,
8190
8191             doRelay : function(foo, bar, hname){
8192                 if(hname == 'down' || this.scope.isExpanded()){
8193                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8194                 }
8195                 return true;
8196             },
8197
8198             forceKeyDown: true
8199         });
8200         
8201         
8202         this.queryDelay = Math.max(this.queryDelay || 10,
8203                 this.mode == 'local' ? 10 : 250);
8204         
8205         
8206         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8207         
8208         if(this.typeAhead){
8209             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8210         }
8211         if(this.editable !== false){
8212             this.inputEl().on("keyup", this.onKeyUp, this);
8213         }
8214         if(this.forceSelection){
8215             this.on('blur', this.doForce, this);
8216         }
8217         
8218         if(this.multiple){
8219             this.choices = this.el.select('ul.select2-choices', true).first();
8220             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8221         }
8222     },
8223
8224     onDestroy : function(){
8225         if(this.view){
8226             this.view.setStore(null);
8227             this.view.el.removeAllListeners();
8228             this.view.el.remove();
8229             this.view.purgeListeners();
8230         }
8231         if(this.list){
8232             this.list.dom.innerHTML  = '';
8233         }
8234         if(this.store){
8235             this.store.un('beforeload', this.onBeforeLoad, this);
8236             this.store.un('load', this.onLoad, this);
8237             this.store.un('loadexception', this.onLoadException, this);
8238         }
8239         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8240     },
8241
8242     // private
8243     fireKey : function(e){
8244         if(e.isNavKeyPress() && !this.list.isVisible()){
8245             this.fireEvent("specialkey", this, e);
8246         }
8247     },
8248
8249     // private
8250     onResize: function(w, h){
8251 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8252 //        
8253 //        if(typeof w != 'number'){
8254 //            // we do not handle it!?!?
8255 //            return;
8256 //        }
8257 //        var tw = this.trigger.getWidth();
8258 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8259 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8260 //        var x = w - tw;
8261 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8262 //            
8263 //        //this.trigger.setStyle('left', x+'px');
8264 //        
8265 //        if(this.list && this.listWidth === undefined){
8266 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8267 //            this.list.setWidth(lw);
8268 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8269 //        }
8270         
8271     
8272         
8273     },
8274
8275     /**
8276      * Allow or prevent the user from directly editing the field text.  If false is passed,
8277      * the user will only be able to select from the items defined in the dropdown list.  This method
8278      * is the runtime equivalent of setting the 'editable' config option at config time.
8279      * @param {Boolean} value True to allow the user to directly edit the field text
8280      */
8281     setEditable : function(value){
8282         if(value == this.editable){
8283             return;
8284         }
8285         this.editable = value;
8286         if(!value){
8287             this.inputEl().dom.setAttribute('readOnly', true);
8288             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8289             this.inputEl().addClass('x-combo-noedit');
8290         }else{
8291             this.inputEl().dom.setAttribute('readOnly', false);
8292             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8293             this.inputEl().removeClass('x-combo-noedit');
8294         }
8295     },
8296
8297     // private
8298     
8299     onBeforeLoad : function(combo,opts){
8300         if(!this.hasFocus){
8301             return;
8302         }
8303          if (!opts.add) {
8304             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8305          }
8306         this.restrictHeight();
8307         this.selectedIndex = -1;
8308     },
8309
8310     // private
8311     onLoad : function(){
8312         
8313         this.hasQuery = false;
8314         
8315         if(!this.hasFocus){
8316             return;
8317         }
8318         
8319         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8320             this.loading.hide();
8321         }
8322         
8323         if(this.store.getCount() > 0){
8324             this.expand();
8325             this.restrictHeight();
8326             if(this.lastQuery == this.allQuery){
8327                 if(this.editable){
8328                     this.inputEl().dom.select();
8329                 }
8330                 if(!this.selectByValue(this.value, true)){
8331                     this.select(0, true);
8332                 }
8333             }else{
8334                 this.selectNext();
8335                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8336                     this.taTask.delay(this.typeAheadDelay);
8337                 }
8338             }
8339         }else{
8340             this.onEmptyResults();
8341         }
8342         
8343         //this.el.focus();
8344     },
8345     // private
8346     onLoadException : function()
8347     {
8348         this.hasQuery = false;
8349         
8350         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8351             this.loading.hide();
8352         }
8353         
8354         this.collapse();
8355         Roo.log(this.store.reader.jsonData);
8356         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8357             // fixme
8358             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8359         }
8360         
8361         
8362     },
8363     // private
8364     onTypeAhead : function(){
8365         if(this.store.getCount() > 0){
8366             var r = this.store.getAt(0);
8367             var newValue = r.data[this.displayField];
8368             var len = newValue.length;
8369             var selStart = this.getRawValue().length;
8370             
8371             if(selStart != len){
8372                 this.setRawValue(newValue);
8373                 this.selectText(selStart, newValue.length);
8374             }
8375         }
8376     },
8377
8378     // private
8379     onSelect : function(record, index){
8380         
8381         if(this.fireEvent('beforeselect', this, record, index) !== false){
8382         
8383             this.setFromData(index > -1 ? record.data : false);
8384             
8385             this.collapse();
8386             this.fireEvent('select', this, record, index);
8387         }
8388     },
8389
8390     /**
8391      * Returns the currently selected field value or empty string if no value is set.
8392      * @return {String} value The selected value
8393      */
8394     getValue : function(){
8395         
8396         if(this.multiple){
8397             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8398         }
8399         
8400         if(this.valueField){
8401             return typeof this.value != 'undefined' ? this.value : '';
8402         }else{
8403             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8404         }
8405     },
8406
8407     /**
8408      * Clears any text/value currently set in the field
8409      */
8410     clearValue : function(){
8411         if(this.hiddenField){
8412             this.hiddenField.dom.value = '';
8413         }
8414         this.value = '';
8415         this.setRawValue('');
8416         this.lastSelectionText = '';
8417         
8418     },
8419
8420     /**
8421      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8422      * will be displayed in the field.  If the value does not match the data value of an existing item,
8423      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8424      * Otherwise the field will be blank (although the value will still be set).
8425      * @param {String} value The value to match
8426      */
8427     setValue : function(v){
8428         if(this.multiple){
8429             this.syncValue();
8430             return;
8431         }
8432         
8433         var text = v;
8434         if(this.valueField){
8435             var r = this.findRecord(this.valueField, v);
8436             if(r){
8437                 text = r.data[this.displayField];
8438             }else if(this.valueNotFoundText !== undefined){
8439                 text = this.valueNotFoundText;
8440             }
8441         }
8442         this.lastSelectionText = text;
8443         if(this.hiddenField){
8444             this.hiddenField.dom.value = v;
8445         }
8446         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8447         this.value = v;
8448     },
8449     /**
8450      * @property {Object} the last set data for the element
8451      */
8452     
8453     lastData : false,
8454     /**
8455      * Sets the value of the field based on a object which is related to the record format for the store.
8456      * @param {Object} value the value to set as. or false on reset?
8457      */
8458     setFromData : function(o){
8459         
8460         if(this.multiple){
8461             this.addItem(o);
8462             return;
8463         }
8464             
8465         var dv = ''; // display value
8466         var vv = ''; // value value..
8467         this.lastData = o;
8468         if (this.displayField) {
8469             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8470         } else {
8471             // this is an error condition!!!
8472             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8473         }
8474         
8475         if(this.valueField){
8476             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8477         }
8478         
8479         if(this.hiddenField){
8480             this.hiddenField.dom.value = vv;
8481             
8482             this.lastSelectionText = dv;
8483             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8484             this.value = vv;
8485             return;
8486         }
8487         // no hidden field.. - we store the value in 'value', but still display
8488         // display field!!!!
8489         this.lastSelectionText = dv;
8490         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8491         this.value = vv;
8492         
8493         
8494     },
8495     // private
8496     reset : function(){
8497         // overridden so that last data is reset..
8498         this.setValue(this.originalValue);
8499         this.clearInvalid();
8500         this.lastData = false;
8501         if (this.view) {
8502             this.view.clearSelections();
8503         }
8504     },
8505     // private
8506     findRecord : function(prop, value){
8507         var record;
8508         if(this.store.getCount() > 0){
8509             this.store.each(function(r){
8510                 if(r.data[prop] == value){
8511                     record = r;
8512                     return false;
8513                 }
8514                 return true;
8515             });
8516         }
8517         return record;
8518     },
8519     
8520     getName: function()
8521     {
8522         // returns hidden if it's set..
8523         if (!this.rendered) {return ''};
8524         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8525         
8526     },
8527     // private
8528     onViewMove : function(e, t){
8529         this.inKeyMode = false;
8530     },
8531
8532     // private
8533     onViewOver : function(e, t){
8534         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8535             return;
8536         }
8537         var item = this.view.findItemFromChild(t);
8538         if(item){
8539             var index = this.view.indexOf(item);
8540             this.select(index, false);
8541         }
8542     },
8543
8544     // private
8545     onViewClick : function(doFocus)
8546     {
8547         var index = this.view.getSelectedIndexes()[0];
8548         var r = this.store.getAt(index);
8549         if(r){
8550             this.onSelect(r, index);
8551         }
8552         if(doFocus !== false && !this.blockFocus){
8553             this.inputEl().focus();
8554         }
8555     },
8556
8557     // private
8558     restrictHeight : function(){
8559         //this.innerList.dom.style.height = '';
8560         //var inner = this.innerList.dom;
8561         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8562         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8563         //this.list.beginUpdate();
8564         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8565         this.list.alignTo(this.inputEl(), this.listAlign);
8566         //this.list.endUpdate();
8567     },
8568
8569     // private
8570     onEmptyResults : function(){
8571         this.collapse();
8572     },
8573
8574     /**
8575      * Returns true if the dropdown list is expanded, else false.
8576      */
8577     isExpanded : function(){
8578         return this.list.isVisible();
8579     },
8580
8581     /**
8582      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8583      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8584      * @param {String} value The data value of the item to select
8585      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8586      * selected item if it is not currently in view (defaults to true)
8587      * @return {Boolean} True if the value matched an item in the list, else false
8588      */
8589     selectByValue : function(v, scrollIntoView){
8590         if(v !== undefined && v !== null){
8591             var r = this.findRecord(this.valueField || this.displayField, v);
8592             if(r){
8593                 this.select(this.store.indexOf(r), scrollIntoView);
8594                 return true;
8595             }
8596         }
8597         return false;
8598     },
8599
8600     /**
8601      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8602      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8603      * @param {Number} index The zero-based index of the list item to select
8604      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8605      * selected item if it is not currently in view (defaults to true)
8606      */
8607     select : function(index, scrollIntoView){
8608         this.selectedIndex = index;
8609         this.view.select(index);
8610         if(scrollIntoView !== false){
8611             var el = this.view.getNode(index);
8612             if(el){
8613                 //this.innerList.scrollChildIntoView(el, false);
8614                 
8615             }
8616         }
8617     },
8618
8619     // private
8620     selectNext : function(){
8621         var ct = this.store.getCount();
8622         if(ct > 0){
8623             if(this.selectedIndex == -1){
8624                 this.select(0);
8625             }else if(this.selectedIndex < ct-1){
8626                 this.select(this.selectedIndex+1);
8627             }
8628         }
8629     },
8630
8631     // private
8632     selectPrev : function(){
8633         var ct = this.store.getCount();
8634         if(ct > 0){
8635             if(this.selectedIndex == -1){
8636                 this.select(0);
8637             }else if(this.selectedIndex != 0){
8638                 this.select(this.selectedIndex-1);
8639             }
8640         }
8641     },
8642
8643     // private
8644     onKeyUp : function(e){
8645         if(this.editable !== false && !e.isSpecialKey()){
8646             this.lastKey = e.getKey();
8647             this.dqTask.delay(this.queryDelay);
8648         }
8649     },
8650
8651     // private
8652     validateBlur : function(){
8653         return !this.list || !this.list.isVisible();   
8654     },
8655
8656     // private
8657     initQuery : function(){
8658         this.doQuery(this.getRawValue());
8659     },
8660
8661     // private
8662     doForce : function(){
8663         if(this.el.dom.value.length > 0){
8664             this.el.dom.value =
8665                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8666              
8667         }
8668     },
8669
8670     /**
8671      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8672      * query allowing the query action to be canceled if needed.
8673      * @param {String} query The SQL query to execute
8674      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8675      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8676      * saved in the current store (defaults to false)
8677      */
8678     doQuery : function(q, forceAll){
8679         
8680         if(q === undefined || q === null){
8681             q = '';
8682         }
8683         var qe = {
8684             query: q,
8685             forceAll: forceAll,
8686             combo: this,
8687             cancel:false
8688         };
8689         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8690             return false;
8691         }
8692         q = qe.query;
8693         
8694         forceAll = qe.forceAll;
8695         if(forceAll === true || (q.length >= this.minChars)){
8696             
8697             this.hasQuery = true;
8698             
8699             if(this.lastQuery != q || this.alwaysQuery){
8700                 this.lastQuery = q;
8701                 if(this.mode == 'local'){
8702                     this.selectedIndex = -1;
8703                     if(forceAll){
8704                         this.store.clearFilter();
8705                     }else{
8706                         this.store.filter(this.displayField, q);
8707                     }
8708                     this.onLoad();
8709                 }else{
8710                     this.store.baseParams[this.queryParam] = q;
8711                     
8712                     var options = {params : this.getParams(q)};
8713                     
8714                     if(this.loadNext){
8715                         options.add = true;
8716                         options.params.start = this.page * this.pageSize;
8717                     }
8718                     
8719                     this.store.load(options);
8720                     this.expand();
8721                 }
8722             }else{
8723                 this.selectedIndex = -1;
8724                 this.onLoad();   
8725             }
8726         }
8727         
8728         this.loadNext = false;
8729     },
8730
8731     // private
8732     getParams : function(q){
8733         var p = {};
8734         //p[this.queryParam] = q;
8735         
8736         if(this.pageSize){
8737             p.start = 0;
8738             p.limit = this.pageSize;
8739         }
8740         return p;
8741     },
8742
8743     /**
8744      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8745      */
8746     collapse : function(){
8747         if(!this.isExpanded()){
8748             return;
8749         }
8750         
8751         this.list.hide();
8752         Roo.get(document).un('mousedown', this.collapseIf, this);
8753         Roo.get(document).un('mousewheel', this.collapseIf, this);
8754         if (!this.editable) {
8755             Roo.get(document).un('keydown', this.listKeyPress, this);
8756         }
8757         this.fireEvent('collapse', this);
8758     },
8759
8760     // private
8761     collapseIf : function(e){
8762         var in_combo  = e.within(this.el);
8763         var in_list =  e.within(this.list);
8764         
8765         if (in_combo || in_list) {
8766             //e.stopPropagation();
8767             return;
8768         }
8769
8770         this.collapse();
8771         
8772     },
8773
8774     /**
8775      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8776      */
8777     expand : function(){
8778        
8779         if(this.isExpanded() || !this.hasFocus){
8780             return;
8781         }
8782          Roo.log('expand');
8783         this.list.alignTo(this.inputEl(), this.listAlign);
8784         this.list.show();
8785         Roo.get(document).on('mousedown', this.collapseIf, this);
8786         Roo.get(document).on('mousewheel', this.collapseIf, this);
8787         if (!this.editable) {
8788             Roo.get(document).on('keydown', this.listKeyPress, this);
8789         }
8790         
8791         this.fireEvent('expand', this);
8792     },
8793
8794     // private
8795     // Implements the default empty TriggerField.onTriggerClick function
8796     onTriggerClick : function()
8797     {
8798         Roo.log('trigger click');
8799         
8800         if(this.disabled){
8801             return;
8802         }
8803         
8804         this.page = 0;
8805         this.loadNext = false;
8806         
8807         if(this.isExpanded()){
8808             this.collapse();
8809             if (!this.blockFocus) {
8810                 this.inputEl().focus();
8811             }
8812             
8813         }else {
8814             this.hasFocus = true;
8815             if(this.triggerAction == 'all') {
8816                 this.doQuery(this.allQuery, true);
8817             } else {
8818                 this.doQuery(this.getRawValue());
8819             }
8820             if (!this.blockFocus) {
8821                 this.inputEl().focus();
8822             }
8823         }
8824     },
8825     listKeyPress : function(e)
8826     {
8827         //Roo.log('listkeypress');
8828         // scroll to first matching element based on key pres..
8829         if (e.isSpecialKey()) {
8830             return false;
8831         }
8832         var k = String.fromCharCode(e.getKey()).toUpperCase();
8833         //Roo.log(k);
8834         var match  = false;
8835         var csel = this.view.getSelectedNodes();
8836         var cselitem = false;
8837         if (csel.length) {
8838             var ix = this.view.indexOf(csel[0]);
8839             cselitem  = this.store.getAt(ix);
8840             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8841                 cselitem = false;
8842             }
8843             
8844         }
8845         
8846         this.store.each(function(v) { 
8847             if (cselitem) {
8848                 // start at existing selection.
8849                 if (cselitem.id == v.id) {
8850                     cselitem = false;
8851                 }
8852                 return true;
8853             }
8854                 
8855             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8856                 match = this.store.indexOf(v);
8857                 return false;
8858             }
8859             return true;
8860         }, this);
8861         
8862         if (match === false) {
8863             return true; // no more action?
8864         }
8865         // scroll to?
8866         this.view.select(match);
8867         var sn = Roo.get(this.view.getSelectedNodes()[0])
8868         //sn.scrollIntoView(sn.dom.parentNode, false);
8869     },
8870     
8871     onViewScroll : function(e, t){
8872         
8873         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8874             return;
8875         }
8876         
8877         this.hasQuery = true;
8878         
8879         this.loading = this.list.select('.loading', true).first();
8880         
8881         if(this.loading === null){
8882             this.list.createChild({
8883                 tag: 'div',
8884                 cls: 'loading select2-more-results select2-active',
8885                 html: 'Loading more results...'
8886             })
8887             
8888             this.loading = this.list.select('.loading', true).first();
8889             
8890             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8891             
8892             this.loading.hide();
8893         }
8894         
8895         this.loading.show();
8896         
8897         var _combo = this;
8898         
8899         this.page++;
8900         this.loadNext = true;
8901         
8902         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8903         
8904         return;
8905     },
8906     
8907     addItem : function(o)
8908     {   
8909         var dv = ''; // display value
8910         
8911         if (this.displayField) {
8912             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8913         } else {
8914             // this is an error condition!!!
8915             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8916         }
8917         
8918         if(!dv.length){
8919             return;
8920         }
8921         
8922         var choice = this.choices.createChild({
8923             tag: 'li',
8924             cls: 'select2-search-choice',
8925             cn: [
8926                 {
8927                     tag: 'div',
8928                     html: dv
8929                 },
8930                 {
8931                     tag: 'a',
8932                     href: '#',
8933                     cls: 'select2-search-choice-close',
8934                     tabindex: '-1'
8935                 }
8936             ]
8937             
8938         }, this.searchField);
8939         
8940         var close = choice.select('a.select2-search-choice-close', true).first()
8941         
8942         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8943         
8944         this.item.push(o);
8945         this.lastData = o;
8946         
8947         this.syncValue();
8948         
8949         this.inputEl().dom.value = '';
8950         
8951     },
8952     
8953     onRemoveItem : function(e, _self, o)
8954     {
8955         Roo.log('remove item');
8956         var index = this.item.indexOf(o.data) * 1;
8957         
8958         if( index < 0){
8959             Roo.log('not this item?!');
8960             return;
8961         }
8962         
8963         this.item.splice(index, 1);
8964         o.item.remove();
8965         
8966         this.syncValue();
8967         
8968         this.fireEvent('remove', this);
8969         
8970     },
8971     
8972     syncValue : function()
8973     {
8974         if(!this.item.length){
8975             this.clearValue();
8976             return;
8977         }
8978             
8979         var value = [];
8980         var _this = this;
8981         Roo.each(this.item, function(i){
8982             if(_this.valueField){
8983                 value.push(i[_this.valueField]);
8984                 return;
8985             }
8986
8987             value.push(i);
8988         });
8989
8990         this.value = value.join(',');
8991
8992         if(this.hiddenField){
8993             this.hiddenField.dom.value = this.value;
8994         }
8995     },
8996     
8997     clearItem : function()
8998     {
8999         if(!this.multiple){
9000             return;
9001         }
9002         
9003         this.item = [];
9004         
9005         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9006            c.remove();
9007         });
9008         
9009         this.syncValue();
9010     }
9011     
9012     
9013
9014     /** 
9015     * @cfg {Boolean} grow 
9016     * @hide 
9017     */
9018     /** 
9019     * @cfg {Number} growMin 
9020     * @hide 
9021     */
9022     /** 
9023     * @cfg {Number} growMax 
9024     * @hide 
9025     */
9026     /**
9027      * @hide
9028      * @method autoSize
9029      */
9030 });
9031 /*
9032  * Based on:
9033  * Ext JS Library 1.1.1
9034  * Copyright(c) 2006-2007, Ext JS, LLC.
9035  *
9036  * Originally Released Under LGPL - original licence link has changed is not relivant.
9037  *
9038  * Fork - LGPL
9039  * <script type="text/javascript">
9040  */
9041
9042 /**
9043  * @class Roo.View
9044  * @extends Roo.util.Observable
9045  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9046  * This class also supports single and multi selection modes. <br>
9047  * Create a data model bound view:
9048  <pre><code>
9049  var store = new Roo.data.Store(...);
9050
9051  var view = new Roo.View({
9052     el : "my-element",
9053     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9054  
9055     singleSelect: true,
9056     selectedClass: "ydataview-selected",
9057     store: store
9058  });
9059
9060  // listen for node click?
9061  view.on("click", function(vw, index, node, e){
9062  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9063  });
9064
9065  // load XML data
9066  dataModel.load("foobar.xml");
9067  </code></pre>
9068  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9069  * <br><br>
9070  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9071  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9072  * 
9073  * Note: old style constructor is still suported (container, template, config)
9074  * 
9075  * @constructor
9076  * Create a new View
9077  * @param {Object} config The config object
9078  * 
9079  */
9080 Roo.View = function(config, depreciated_tpl, depreciated_config){
9081     
9082     if (typeof(depreciated_tpl) == 'undefined') {
9083         // new way.. - universal constructor.
9084         Roo.apply(this, config);
9085         this.el  = Roo.get(this.el);
9086     } else {
9087         // old format..
9088         this.el  = Roo.get(config);
9089         this.tpl = depreciated_tpl;
9090         Roo.apply(this, depreciated_config);
9091     }
9092     this.wrapEl  = this.el.wrap().wrap();
9093     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9094     
9095     
9096     if(typeof(this.tpl) == "string"){
9097         this.tpl = new Roo.Template(this.tpl);
9098     } else {
9099         // support xtype ctors..
9100         this.tpl = new Roo.factory(this.tpl, Roo);
9101     }
9102     
9103     
9104     this.tpl.compile();
9105    
9106   
9107     
9108      
9109     /** @private */
9110     this.addEvents({
9111         /**
9112          * @event beforeclick
9113          * Fires before a click is processed. Returns false to cancel the default action.
9114          * @param {Roo.View} this
9115          * @param {Number} index The index of the target node
9116          * @param {HTMLElement} node The target node
9117          * @param {Roo.EventObject} e The raw event object
9118          */
9119             "beforeclick" : true,
9120         /**
9121          * @event click
9122          * Fires when a template node is clicked.
9123          * @param {Roo.View} this
9124          * @param {Number} index The index of the target node
9125          * @param {HTMLElement} node The target node
9126          * @param {Roo.EventObject} e The raw event object
9127          */
9128             "click" : true,
9129         /**
9130          * @event dblclick
9131          * Fires when a template node is double clicked.
9132          * @param {Roo.View} this
9133          * @param {Number} index The index of the target node
9134          * @param {HTMLElement} node The target node
9135          * @param {Roo.EventObject} e The raw event object
9136          */
9137             "dblclick" : true,
9138         /**
9139          * @event contextmenu
9140          * Fires when a template node is right clicked.
9141          * @param {Roo.View} this
9142          * @param {Number} index The index of the target node
9143          * @param {HTMLElement} node The target node
9144          * @param {Roo.EventObject} e The raw event object
9145          */
9146             "contextmenu" : true,
9147         /**
9148          * @event selectionchange
9149          * Fires when the selected nodes change.
9150          * @param {Roo.View} this
9151          * @param {Array} selections Array of the selected nodes
9152          */
9153             "selectionchange" : true,
9154     
9155         /**
9156          * @event beforeselect
9157          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9158          * @param {Roo.View} this
9159          * @param {HTMLElement} node The node to be selected
9160          * @param {Array} selections Array of currently selected nodes
9161          */
9162             "beforeselect" : true,
9163         /**
9164          * @event preparedata
9165          * Fires on every row to render, to allow you to change the data.
9166          * @param {Roo.View} this
9167          * @param {Object} data to be rendered (change this)
9168          */
9169           "preparedata" : true
9170           
9171           
9172         });
9173
9174
9175
9176     this.el.on({
9177         "click": this.onClick,
9178         "dblclick": this.onDblClick,
9179         "contextmenu": this.onContextMenu,
9180         scope:this
9181     });
9182
9183     this.selections = [];
9184     this.nodes = [];
9185     this.cmp = new Roo.CompositeElementLite([]);
9186     if(this.store){
9187         this.store = Roo.factory(this.store, Roo.data);
9188         this.setStore(this.store, true);
9189     }
9190     
9191     if ( this.footer && this.footer.xtype) {
9192            
9193          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9194         
9195         this.footer.dataSource = this.store
9196         this.footer.container = fctr;
9197         this.footer = Roo.factory(this.footer, Roo);
9198         fctr.insertFirst(this.el);
9199         
9200         // this is a bit insane - as the paging toolbar seems to detach the el..
9201 //        dom.parentNode.parentNode.parentNode
9202          // they get detached?
9203     }
9204     
9205     
9206     Roo.View.superclass.constructor.call(this);
9207     
9208     
9209 };
9210
9211 Roo.extend(Roo.View, Roo.util.Observable, {
9212     
9213      /**
9214      * @cfg {Roo.data.Store} store Data store to load data from.
9215      */
9216     store : false,
9217     
9218     /**
9219      * @cfg {String|Roo.Element} el The container element.
9220      */
9221     el : '',
9222     
9223     /**
9224      * @cfg {String|Roo.Template} tpl The template used by this View 
9225      */
9226     tpl : false,
9227     /**
9228      * @cfg {String} dataName the named area of the template to use as the data area
9229      *                          Works with domtemplates roo-name="name"
9230      */
9231     dataName: false,
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     
9241     /**
9242      * @cfg {String} text to display on mask (default Loading)
9243      */
9244     mask : false,
9245     /**
9246      * @cfg {Boolean} multiSelect Allow multiple selection
9247      */
9248     multiSelect : false,
9249     /**
9250      * @cfg {Boolean} singleSelect Allow single selection
9251      */
9252     singleSelect:  false,
9253     
9254     /**
9255      * @cfg {Boolean} toggleSelect - selecting 
9256      */
9257     toggleSelect : false,
9258     
9259     /**
9260      * Returns the element this view is bound to.
9261      * @return {Roo.Element}
9262      */
9263     getEl : function(){
9264         return this.wrapEl;
9265     },
9266     
9267     
9268
9269     /**
9270      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9271      */
9272     refresh : function(){
9273         Roo.log('refresh');
9274         var t = this.tpl;
9275         
9276         // if we are using something like 'domtemplate', then
9277         // the what gets used is:
9278         // t.applySubtemplate(NAME, data, wrapping data..)
9279         // the outer template then get' applied with
9280         //     the store 'extra data'
9281         // and the body get's added to the
9282         //      roo-name="data" node?
9283         //      <span class='roo-tpl-{name}'></span> ?????
9284         
9285         
9286         
9287         this.clearSelections();
9288         this.el.update("");
9289         var html = [];
9290         var records = this.store.getRange();
9291         if(records.length < 1) {
9292             
9293             // is this valid??  = should it render a template??
9294             
9295             this.el.update(this.emptyText);
9296             return;
9297         }
9298         var el = this.el;
9299         if (this.dataName) {
9300             this.el.update(t.apply(this.store.meta)); //????
9301             el = this.el.child('.roo-tpl-' + this.dataName);
9302         }
9303         
9304         for(var i = 0, len = records.length; i < len; i++){
9305             var data = this.prepareData(records[i].data, i, records[i]);
9306             this.fireEvent("preparedata", this, data, i, records[i]);
9307             html[html.length] = Roo.util.Format.trim(
9308                 this.dataName ?
9309                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9310                     t.apply(data)
9311             );
9312         }
9313         
9314         
9315         
9316         el.update(html.join(""));
9317         this.nodes = el.dom.childNodes;
9318         this.updateIndexes(0);
9319     },
9320     
9321
9322     /**
9323      * Function to override to reformat the data that is sent to
9324      * the template for each node.
9325      * DEPRICATED - use the preparedata event handler.
9326      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9327      * a JSON object for an UpdateManager bound view).
9328      */
9329     prepareData : function(data, index, record)
9330     {
9331         this.fireEvent("preparedata", this, data, index, record);
9332         return data;
9333     },
9334
9335     onUpdate : function(ds, record){
9336          Roo.log('on update');   
9337         this.clearSelections();
9338         var index = this.store.indexOf(record);
9339         var n = this.nodes[index];
9340         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9341         n.parentNode.removeChild(n);
9342         this.updateIndexes(index, index);
9343     },
9344
9345     
9346     
9347 // --------- FIXME     
9348     onAdd : function(ds, records, index)
9349     {
9350         Roo.log(['on Add', ds, records, index] );        
9351         this.clearSelections();
9352         if(this.nodes.length == 0){
9353             this.refresh();
9354             return;
9355         }
9356         var n = this.nodes[index];
9357         for(var i = 0, len = records.length; i < len; i++){
9358             var d = this.prepareData(records[i].data, i, records[i]);
9359             if(n){
9360                 this.tpl.insertBefore(n, d);
9361             }else{
9362                 
9363                 this.tpl.append(this.el, d);
9364             }
9365         }
9366         this.updateIndexes(index);
9367     },
9368
9369     onRemove : function(ds, record, index){
9370         Roo.log('onRemove');
9371         this.clearSelections();
9372         var el = this.dataName  ?
9373             this.el.child('.roo-tpl-' + this.dataName) :
9374             this.el; 
9375         
9376         el.dom.removeChild(this.nodes[index]);
9377         this.updateIndexes(index);
9378     },
9379
9380     /**
9381      * Refresh an individual node.
9382      * @param {Number} index
9383      */
9384     refreshNode : function(index){
9385         this.onUpdate(this.store, this.store.getAt(index));
9386     },
9387
9388     updateIndexes : function(startIndex, endIndex){
9389         var ns = this.nodes;
9390         startIndex = startIndex || 0;
9391         endIndex = endIndex || ns.length - 1;
9392         for(var i = startIndex; i <= endIndex; i++){
9393             ns[i].nodeIndex = i;
9394         }
9395     },
9396
9397     /**
9398      * Changes the data store this view uses and refresh the view.
9399      * @param {Store} store
9400      */
9401     setStore : function(store, initial){
9402         if(!initial && this.store){
9403             this.store.un("datachanged", this.refresh);
9404             this.store.un("add", this.onAdd);
9405             this.store.un("remove", this.onRemove);
9406             this.store.un("update", this.onUpdate);
9407             this.store.un("clear", this.refresh);
9408             this.store.un("beforeload", this.onBeforeLoad);
9409             this.store.un("load", this.onLoad);
9410             this.store.un("loadexception", this.onLoad);
9411         }
9412         if(store){
9413           
9414             store.on("datachanged", this.refresh, this);
9415             store.on("add", this.onAdd, this);
9416             store.on("remove", this.onRemove, this);
9417             store.on("update", this.onUpdate, this);
9418             store.on("clear", this.refresh, this);
9419             store.on("beforeload", this.onBeforeLoad, this);
9420             store.on("load", this.onLoad, this);
9421             store.on("loadexception", this.onLoad, this);
9422         }
9423         
9424         if(store){
9425             this.refresh();
9426         }
9427     },
9428     /**
9429      * onbeforeLoad - masks the loading area.
9430      *
9431      */
9432     onBeforeLoad : function(store,opts)
9433     {
9434          Roo.log('onBeforeLoad');   
9435         if (!opts.add) {
9436             this.el.update("");
9437         }
9438         this.el.mask(this.mask ? this.mask : "Loading" ); 
9439     },
9440     onLoad : function ()
9441     {
9442         this.el.unmask();
9443     },
9444     
9445
9446     /**
9447      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9448      * @param {HTMLElement} node
9449      * @return {HTMLElement} The template node
9450      */
9451     findItemFromChild : function(node){
9452         var el = this.dataName  ?
9453             this.el.child('.roo-tpl-' + this.dataName,true) :
9454             this.el.dom; 
9455         
9456         if(!node || node.parentNode == el){
9457                     return node;
9458             }
9459             var p = node.parentNode;
9460             while(p && p != el){
9461             if(p.parentNode == el){
9462                 return p;
9463             }
9464             p = p.parentNode;
9465         }
9466             return null;
9467     },
9468
9469     /** @ignore */
9470     onClick : function(e){
9471         var item = this.findItemFromChild(e.getTarget());
9472         if(item){
9473             var index = this.indexOf(item);
9474             if(this.onItemClick(item, index, e) !== false){
9475                 this.fireEvent("click", this, index, item, e);
9476             }
9477         }else{
9478             this.clearSelections();
9479         }
9480     },
9481
9482     /** @ignore */
9483     onContextMenu : function(e){
9484         var item = this.findItemFromChild(e.getTarget());
9485         if(item){
9486             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9487         }
9488     },
9489
9490     /** @ignore */
9491     onDblClick : function(e){
9492         var item = this.findItemFromChild(e.getTarget());
9493         if(item){
9494             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9495         }
9496     },
9497
9498     onItemClick : function(item, index, e)
9499     {
9500         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9501             return false;
9502         }
9503         if (this.toggleSelect) {
9504             var m = this.isSelected(item) ? 'unselect' : 'select';
9505             Roo.log(m);
9506             var _t = this;
9507             _t[m](item, true, false);
9508             return true;
9509         }
9510         if(this.multiSelect || this.singleSelect){
9511             if(this.multiSelect && e.shiftKey && this.lastSelection){
9512                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9513             }else{
9514                 this.select(item, this.multiSelect && e.ctrlKey);
9515                 this.lastSelection = item;
9516             }
9517             e.preventDefault();
9518         }
9519         return true;
9520     },
9521
9522     /**
9523      * Get the number of selected nodes.
9524      * @return {Number}
9525      */
9526     getSelectionCount : function(){
9527         return this.selections.length;
9528     },
9529
9530     /**
9531      * Get the currently selected nodes.
9532      * @return {Array} An array of HTMLElements
9533      */
9534     getSelectedNodes : function(){
9535         return this.selections;
9536     },
9537
9538     /**
9539      * Get the indexes of the selected nodes.
9540      * @return {Array}
9541      */
9542     getSelectedIndexes : function(){
9543         var indexes = [], s = this.selections;
9544         for(var i = 0, len = s.length; i < len; i++){
9545             indexes.push(s[i].nodeIndex);
9546         }
9547         return indexes;
9548     },
9549
9550     /**
9551      * Clear all selections
9552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9553      */
9554     clearSelections : function(suppressEvent){
9555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9556             this.cmp.elements = this.selections;
9557             this.cmp.removeClass(this.selectedClass);
9558             this.selections = [];
9559             if(!suppressEvent){
9560                 this.fireEvent("selectionchange", this, this.selections);
9561             }
9562         }
9563     },
9564
9565     /**
9566      * Returns true if the passed node is selected
9567      * @param {HTMLElement/Number} node The node or node index
9568      * @return {Boolean}
9569      */
9570     isSelected : function(node){
9571         var s = this.selections;
9572         if(s.length < 1){
9573             return false;
9574         }
9575         node = this.getNode(node);
9576         return s.indexOf(node) !== -1;
9577     },
9578
9579     /**
9580      * Selects nodes.
9581      * @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
9582      * @param {Boolean} keepExisting (optional) true to keep existing selections
9583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9584      */
9585     select : function(nodeInfo, keepExisting, suppressEvent){
9586         if(nodeInfo instanceof Array){
9587             if(!keepExisting){
9588                 this.clearSelections(true);
9589             }
9590             for(var i = 0, len = nodeInfo.length; i < len; i++){
9591                 this.select(nodeInfo[i], true, true);
9592             }
9593             return;
9594         } 
9595         var node = this.getNode(nodeInfo);
9596         if(!node || this.isSelected(node)){
9597             return; // already selected.
9598         }
9599         if(!keepExisting){
9600             this.clearSelections(true);
9601         }
9602         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9603             Roo.fly(node).addClass(this.selectedClass);
9604             this.selections.push(node);
9605             if(!suppressEvent){
9606                 this.fireEvent("selectionchange", this, this.selections);
9607             }
9608         }
9609         
9610         
9611     },
9612       /**
9613      * Unselects nodes.
9614      * @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
9615      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9616      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9617      */
9618     unselect : function(nodeInfo, keepExisting, suppressEvent)
9619     {
9620         if(nodeInfo instanceof Array){
9621             Roo.each(this.selections, function(s) {
9622                 this.unselect(s, nodeInfo);
9623             }, this);
9624             return;
9625         }
9626         var node = this.getNode(nodeInfo);
9627         if(!node || !this.isSelected(node)){
9628             Roo.log("not selected");
9629             return; // not selected.
9630         }
9631         // fireevent???
9632         var ns = [];
9633         Roo.each(this.selections, function(s) {
9634             if (s == node ) {
9635                 Roo.fly(node).removeClass(this.selectedClass);
9636
9637                 return;
9638             }
9639             ns.push(s);
9640         },this);
9641         
9642         this.selections= ns;
9643         this.fireEvent("selectionchange", this, this.selections);
9644     },
9645
9646     /**
9647      * Gets a template node.
9648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9649      * @return {HTMLElement} The node or null if it wasn't found
9650      */
9651     getNode : function(nodeInfo){
9652         if(typeof nodeInfo == "string"){
9653             return document.getElementById(nodeInfo);
9654         }else if(typeof nodeInfo == "number"){
9655             return this.nodes[nodeInfo];
9656         }
9657         return nodeInfo;
9658     },
9659
9660     /**
9661      * Gets a range template nodes.
9662      * @param {Number} startIndex
9663      * @param {Number} endIndex
9664      * @return {Array} An array of nodes
9665      */
9666     getNodes : function(start, end){
9667         var ns = this.nodes;
9668         start = start || 0;
9669         end = typeof end == "undefined" ? ns.length - 1 : end;
9670         var nodes = [];
9671         if(start <= end){
9672             for(var i = start; i <= end; i++){
9673                 nodes.push(ns[i]);
9674             }
9675         } else{
9676             for(var i = start; i >= end; i--){
9677                 nodes.push(ns[i]);
9678             }
9679         }
9680         return nodes;
9681     },
9682
9683     /**
9684      * Finds the index of the passed node
9685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9686      * @return {Number} The index of the node or -1
9687      */
9688     indexOf : function(node){
9689         node = this.getNode(node);
9690         if(typeof node.nodeIndex == "number"){
9691             return node.nodeIndex;
9692         }
9693         var ns = this.nodes;
9694         for(var i = 0, len = ns.length; i < len; i++){
9695             if(ns[i] == node){
9696                 return i;
9697             }
9698         }
9699         return -1;
9700     }
9701 });
9702 /*
9703  * - LGPL
9704  *
9705  * based on jquery fullcalendar
9706  * 
9707  */
9708
9709 Roo.bootstrap = Roo.bootstrap || {};
9710 /**
9711  * @class Roo.bootstrap.Calendar
9712  * @extends Roo.bootstrap.Component
9713  * Bootstrap Calendar class
9714  * @cfg {Boolean} loadMask (true|false) default false
9715     
9716  * @constructor
9717  * Create a new Container
9718  * @param {Object} config The config object
9719  */
9720
9721
9722
9723 Roo.bootstrap.Calendar = function(config){
9724     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9725      this.addEvents({
9726         /**
9727              * @event select
9728              * Fires when a date is selected
9729              * @param {DatePicker} this
9730              * @param {Date} date The selected date
9731              */
9732         'select': true,
9733         /**
9734              * @event monthchange
9735              * Fires when the displayed month changes 
9736              * @param {DatePicker} this
9737              * @param {Date} date The selected month
9738              */
9739         'monthchange': true,
9740         /**
9741              * @event evententer
9742              * Fires when mouse over an event
9743              * @param {Calendar} this
9744              * @param {event} Event
9745              */
9746         'evententer': true,
9747         /**
9748              * @event eventleave
9749              * Fires when the mouse leaves an
9750              * @param {Calendar} this
9751              * @param {event}
9752              */
9753         'eventleave': true,
9754         /**
9755              * @event eventclick
9756              * Fires when the mouse click an
9757              * @param {Calendar} this
9758              * @param {event}
9759              */
9760         'eventclick': true
9761         
9762     });
9763
9764 };
9765
9766 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9767     
9768      /**
9769      * @cfg {Number} startDay
9770      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9771      */
9772     startDay : 0,
9773     
9774     loadMask : false,
9775       
9776     getAutoCreate : function(){
9777         
9778         
9779         var fc_button = function(name, corner, style, content ) {
9780             return Roo.apply({},{
9781                 tag : 'span',
9782                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9783                          (corner.length ?
9784                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9785                             ''
9786                         ),
9787                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9788                 unselectable: 'on'
9789             });
9790         };
9791         
9792         var header = {
9793             tag : 'table',
9794             cls : 'fc-header',
9795             style : 'width:100%',
9796             cn : [
9797                 {
9798                     tag: 'tr',
9799                     cn : [
9800                         {
9801                             tag : 'td',
9802                             cls : 'fc-header-left',
9803                             cn : [
9804                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9805                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9806                                 { tag: 'span', cls: 'fc-header-space' },
9807                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9808                                 
9809                                 
9810                             ]
9811                         },
9812                         
9813                         {
9814                             tag : 'td',
9815                             cls : 'fc-header-center',
9816                             cn : [
9817                                 {
9818                                     tag: 'span',
9819                                     cls: 'fc-header-title',
9820                                     cn : {
9821                                         tag: 'H2',
9822                                         html : 'month / year'
9823                                     }
9824                                 }
9825                                 
9826                             ]
9827                         },
9828                         {
9829                             tag : 'td',
9830                             cls : 'fc-header-right',
9831                             cn : [
9832                           /*      fc_button('month', 'left', '', 'month' ),
9833                                 fc_button('week', '', '', 'week' ),
9834                                 fc_button('day', 'right', '', 'day' )
9835                             */    
9836                                 
9837                             ]
9838                         }
9839                         
9840                     ]
9841                 }
9842             ]
9843         };
9844         
9845        
9846         var cal_heads = function() {
9847             var ret = [];
9848             // fixme - handle this.
9849             
9850             for (var i =0; i < Date.dayNames.length; i++) {
9851                 var d = Date.dayNames[i];
9852                 ret.push({
9853                     tag: 'th',
9854                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9855                     html : d.substring(0,3)
9856                 });
9857                 
9858             }
9859             ret[0].cls += ' fc-first';
9860             ret[6].cls += ' fc-last';
9861             return ret;
9862         };
9863         var cal_cell = function(n) {
9864             return  {
9865                 tag: 'td',
9866                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9867                 cn : [
9868                     {
9869                         cn : [
9870                             {
9871                                 cls: 'fc-day-number',
9872                                 html: 'D'
9873                             },
9874                             {
9875                                 cls: 'fc-day-content',
9876                              
9877                                 cn : [
9878                                      {
9879                                         style: 'position: relative;' // height: 17px;
9880                                     }
9881                                 ]
9882                             }
9883                             
9884                             
9885                         ]
9886                     }
9887                 ]
9888                 
9889             }
9890         };
9891         var cal_rows = function() {
9892             
9893             var ret = []
9894             for (var r = 0; r < 6; r++) {
9895                 var row= {
9896                     tag : 'tr',
9897                     cls : 'fc-week',
9898                     cn : []
9899                 };
9900                 
9901                 for (var i =0; i < Date.dayNames.length; i++) {
9902                     var d = Date.dayNames[i];
9903                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9904
9905                 }
9906                 row.cn[0].cls+=' fc-first';
9907                 row.cn[0].cn[0].style = 'min-height:90px';
9908                 row.cn[6].cls+=' fc-last';
9909                 ret.push(row);
9910                 
9911             }
9912             ret[0].cls += ' fc-first';
9913             ret[4].cls += ' fc-prev-last';
9914             ret[5].cls += ' fc-last';
9915             return ret;
9916             
9917         };
9918         
9919         var cal_table = {
9920             tag: 'table',
9921             cls: 'fc-border-separate',
9922             style : 'width:100%',
9923             cellspacing  : 0,
9924             cn : [
9925                 { 
9926                     tag: 'thead',
9927                     cn : [
9928                         { 
9929                             tag: 'tr',
9930                             cls : 'fc-first fc-last',
9931                             cn : cal_heads()
9932                         }
9933                     ]
9934                 },
9935                 { 
9936                     tag: 'tbody',
9937                     cn : cal_rows()
9938                 }
9939                   
9940             ]
9941         };
9942          
9943          var cfg = {
9944             cls : 'fc fc-ltr',
9945             cn : [
9946                 header,
9947                 {
9948                     cls : 'fc-content',
9949                     style : "position: relative;",
9950                     cn : [
9951                         {
9952                             cls : 'fc-view fc-view-month fc-grid',
9953                             style : 'position: relative',
9954                             unselectable : 'on',
9955                             cn : [
9956                                 {
9957                                     cls : 'fc-event-container',
9958                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9959                                 },
9960                                 cal_table
9961                             ]
9962                         }
9963                     ]
9964     
9965                 }
9966            ] 
9967             
9968         };
9969         
9970          
9971         
9972         return cfg;
9973     },
9974     
9975     
9976     initEvents : function()
9977     {
9978         if(!this.store){
9979             throw "can not find store for calendar";
9980         }
9981         
9982         var mark = {
9983             tag: "div",
9984             cls:"x-dlg-mask",
9985             style: "text-align:center",
9986             cn: [
9987                 {
9988                     tag: "div",
9989                     style: "background-color:white;width:50%;margin:250 auto",
9990                     cn: [
9991                         {
9992                             tag: "img",
9993                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9994                         },
9995                         {
9996                             tag: "span",
9997                             html: "Loading"
9998                         }
9999                         
10000                     ]
10001                 }
10002             ]
10003         }
10004         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10005         
10006         var size = this.el.select('.fc-content', true).first().getSize();
10007         this.maskEl.setSize(size.width, size.height);
10008         this.maskEl.enableDisplayMode("block");
10009         if(!this.loadMask){
10010             this.maskEl.hide();
10011         }
10012         
10013         this.store = Roo.factory(this.store, Roo.data);
10014         this.store.on('load', this.onLoad, this);
10015         this.store.on('beforeload', this.onBeforeLoad, this);
10016         
10017         this.resize();
10018         
10019         this.cells = this.el.select('.fc-day',true);
10020         //Roo.log(this.cells);
10021         this.textNodes = this.el.query('.fc-day-number');
10022         this.cells.addClassOnOver('fc-state-hover');
10023         
10024         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10025         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10026         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10027         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10028         
10029         this.on('monthchange', this.onMonthChange, this);
10030         
10031         this.update(new Date().clearTime());
10032     },
10033     
10034     resize : function() {
10035         var sz  = this.el.getSize();
10036         
10037         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10038         this.el.select('.fc-day-content div',true).setHeight(34);
10039     },
10040     
10041     
10042     // private
10043     showPrevMonth : function(e){
10044         this.update(this.activeDate.add("mo", -1));
10045     },
10046     showToday : function(e){
10047         this.update(new Date().clearTime());
10048     },
10049     // private
10050     showNextMonth : function(e){
10051         this.update(this.activeDate.add("mo", 1));
10052     },
10053
10054     // private
10055     showPrevYear : function(){
10056         this.update(this.activeDate.add("y", -1));
10057     },
10058
10059     // private
10060     showNextYear : function(){
10061         this.update(this.activeDate.add("y", 1));
10062     },
10063
10064     
10065    // private
10066     update : function(date)
10067     {
10068         var vd = this.activeDate;
10069         this.activeDate = date;
10070 //        if(vd && this.el){
10071 //            var t = date.getTime();
10072 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10073 //                Roo.log('using add remove');
10074 //                
10075 //                this.fireEvent('monthchange', this, date);
10076 //                
10077 //                this.cells.removeClass("fc-state-highlight");
10078 //                this.cells.each(function(c){
10079 //                   if(c.dateValue == t){
10080 //                       c.addClass("fc-state-highlight");
10081 //                       setTimeout(function(){
10082 //                            try{c.dom.firstChild.focus();}catch(e){}
10083 //                       }, 50);
10084 //                       return false;
10085 //                   }
10086 //                   return true;
10087 //                });
10088 //                return;
10089 //            }
10090 //        }
10091         
10092         var days = date.getDaysInMonth();
10093         
10094         var firstOfMonth = date.getFirstDateOfMonth();
10095         var startingPos = firstOfMonth.getDay()-this.startDay;
10096         
10097         if(startingPos < this.startDay){
10098             startingPos += 7;
10099         }
10100         
10101         var pm = date.add(Date.MONTH, -1);
10102         var prevStart = pm.getDaysInMonth()-startingPos;
10103 //        
10104         this.cells = this.el.select('.fc-day',true);
10105         this.textNodes = this.el.query('.fc-day-number');
10106         this.cells.addClassOnOver('fc-state-hover');
10107         
10108         var cells = this.cells.elements;
10109         var textEls = this.textNodes;
10110         
10111         Roo.each(cells, function(cell){
10112             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10113         });
10114         
10115         days += startingPos;
10116
10117         // convert everything to numbers so it's fast
10118         var day = 86400000;
10119         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10120         //Roo.log(d);
10121         //Roo.log(pm);
10122         //Roo.log(prevStart);
10123         
10124         var today = new Date().clearTime().getTime();
10125         var sel = date.clearTime().getTime();
10126         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10127         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10128         var ddMatch = this.disabledDatesRE;
10129         var ddText = this.disabledDatesText;
10130         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10131         var ddaysText = this.disabledDaysText;
10132         var format = this.format;
10133         
10134         var setCellClass = function(cal, cell){
10135             
10136             //Roo.log('set Cell Class');
10137             cell.title = "";
10138             var t = d.getTime();
10139             
10140             //Roo.log(d);
10141             
10142             cell.dateValue = t;
10143             if(t == today){
10144                 cell.className += " fc-today";
10145                 cell.className += " fc-state-highlight";
10146                 cell.title = cal.todayText;
10147             }
10148             if(t == sel){
10149                 // disable highlight in other month..
10150                 //cell.className += " fc-state-highlight";
10151                 
10152             }
10153             // disabling
10154             if(t < min) {
10155                 cell.className = " fc-state-disabled";
10156                 cell.title = cal.minText;
10157                 return;
10158             }
10159             if(t > max) {
10160                 cell.className = " fc-state-disabled";
10161                 cell.title = cal.maxText;
10162                 return;
10163             }
10164             if(ddays){
10165                 if(ddays.indexOf(d.getDay()) != -1){
10166                     cell.title = ddaysText;
10167                     cell.className = " fc-state-disabled";
10168                 }
10169             }
10170             if(ddMatch && format){
10171                 var fvalue = d.dateFormat(format);
10172                 if(ddMatch.test(fvalue)){
10173                     cell.title = ddText.replace("%0", fvalue);
10174                     cell.className = " fc-state-disabled";
10175                 }
10176             }
10177             
10178             if (!cell.initialClassName) {
10179                 cell.initialClassName = cell.dom.className;
10180             }
10181             
10182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10183         };
10184
10185         var i = 0;
10186         
10187         for(; i < startingPos; i++) {
10188             textEls[i].innerHTML = (++prevStart);
10189             d.setDate(d.getDate()+1);
10190             
10191             cells[i].className = "fc-past fc-other-month";
10192             setCellClass(this, cells[i]);
10193         }
10194         
10195         var intDay = 0;
10196         
10197         for(; i < days; i++){
10198             intDay = i - startingPos + 1;
10199             textEls[i].innerHTML = (intDay);
10200             d.setDate(d.getDate()+1);
10201             
10202             cells[i].className = ''; // "x-date-active";
10203             setCellClass(this, cells[i]);
10204         }
10205         var extraDays = 0;
10206         
10207         for(; i < 42; i++) {
10208             textEls[i].innerHTML = (++extraDays);
10209             d.setDate(d.getDate()+1);
10210             
10211             cells[i].className = "fc-future fc-other-month";
10212             setCellClass(this, cells[i]);
10213         }
10214         
10215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10216         
10217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10218         
10219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10221         
10222         if(totalRows != 6){
10223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10225         }
10226         
10227         this.fireEvent('monthchange', this, date);
10228         
10229         
10230         /*
10231         if(!this.internalRender){
10232             var main = this.el.dom.firstChild;
10233             var w = main.offsetWidth;
10234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10235             Roo.fly(main).setWidth(w);
10236             this.internalRender = true;
10237             // opera does not respect the auto grow header center column
10238             // then, after it gets a width opera refuses to recalculate
10239             // without a second pass
10240             if(Roo.isOpera && !this.secondPass){
10241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10242                 this.secondPass = true;
10243                 this.update.defer(10, this, [date]);
10244             }
10245         }
10246         */
10247         
10248     },
10249     
10250     findCell : function(dt) {
10251         dt = dt.clearTime().getTime();
10252         var ret = false;
10253         this.cells.each(function(c){
10254             //Roo.log("check " +c.dateValue + '?=' + dt);
10255             if(c.dateValue == dt){
10256                 ret = c;
10257                 return false;
10258             }
10259             return true;
10260         });
10261         
10262         return ret;
10263     },
10264     
10265     findCells : function(ev) {
10266         var s = ev.start.clone().clearTime().getTime();
10267        // Roo.log(s);
10268         var e= ev.end.clone().clearTime().getTime();
10269        // Roo.log(e);
10270         var ret = [];
10271         this.cells.each(function(c){
10272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10273             
10274             if(c.dateValue > e){
10275                 return ;
10276             }
10277             if(c.dateValue < s){
10278                 return ;
10279             }
10280             ret.push(c);
10281         });
10282         
10283         return ret;    
10284     },
10285     
10286     findBestRow: function(cells)
10287     {
10288         var ret = 0;
10289         
10290         for (var i =0 ; i < cells.length;i++) {
10291             ret  = Math.max(cells[i].rows || 0,ret);
10292         }
10293         return ret;
10294         
10295     },
10296     
10297     
10298     addItem : function(ev)
10299     {
10300         // look for vertical location slot in
10301         var cells = this.findCells(ev);
10302         
10303         ev.row = this.findBestRow(cells);
10304         
10305         // work out the location.
10306         
10307         var crow = false;
10308         var rows = [];
10309         for(var i =0; i < cells.length; i++) {
10310             if (!crow) {
10311                 crow = {
10312                     start : cells[i],
10313                     end :  cells[i]
10314                 };
10315                 continue;
10316             }
10317             if (crow.start.getY() == cells[i].getY()) {
10318                 // on same row.
10319                 crow.end = cells[i];
10320                 continue;
10321             }
10322             // different row.
10323             rows.push(crow);
10324             crow = {
10325                 start: cells[i],
10326                 end : cells[i]
10327             };
10328             
10329         }
10330         
10331         rows.push(crow);
10332         ev.els = [];
10333         ev.rows = rows;
10334         ev.cells = cells;
10335         for (var i = 0; i < cells.length;i++) {
10336             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10337             
10338         }
10339         
10340         this.calevents.push(ev);
10341     },
10342     
10343     clearEvents: function() {
10344         
10345         if(!this.calevents){
10346             return;
10347         }
10348         
10349         Roo.each(this.cells.elements, function(c){
10350             c.rows = 0;
10351         });
10352         
10353         Roo.each(this.calevents, function(e) {
10354             Roo.each(e.els, function(el) {
10355                 el.un('mouseenter' ,this.onEventEnter, this);
10356                 el.un('mouseleave' ,this.onEventLeave, this);
10357                 el.remove();
10358             },this);
10359         },this);
10360         
10361     },
10362     
10363     renderEvents: function()
10364     {   
10365         // first make sure there is enough space..
10366         
10367         this.cells.each(function(c) {
10368         
10369             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10370         });
10371         
10372         for (var e = 0; e < this.calevents.length; e++) {
10373             var ev = this.calevents[e];
10374             var cells = ev.cells;
10375             var rows = ev.rows;
10376             
10377             for(var i =0; i < rows.length; i++) {
10378                 
10379                  
10380                 // how many rows should it span..
10381                 
10382                 var  cfg = {
10383                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10384                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10385                     
10386                     unselectable : "on",
10387                     cn : [
10388                         {
10389                             cls: 'fc-event-inner',
10390                             cn : [
10391 //                                {
10392 //                                  tag:'span',
10393 //                                  cls: 'fc-event-time',
10394 //                                  html : cells.length > 1 ? '' : ev.time
10395 //                                },
10396                                 {
10397                                   tag:'span',
10398                                   cls: 'fc-event-title',
10399                                   html : String.format('{0}', ev.title)
10400                                 }
10401                                 
10402                                 
10403                             ]
10404                         },
10405                         {
10406                             cls: 'ui-resizable-handle ui-resizable-e',
10407                             html : '&nbsp;&nbsp;&nbsp'
10408                         }
10409                         
10410                     ]
10411                 };
10412                 if (i == 0) {
10413                     cfg.cls += ' fc-event-start';
10414                 }
10415                 if ((i+1) == rows.length) {
10416                     cfg.cls += ' fc-event-end';
10417                 }
10418                 
10419                 var ctr = this.el.select('.fc-event-container',true).first();
10420                 var cg = ctr.createChild(cfg);
10421                 
10422                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10423                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10424                 cg.on('click', this.onEventClick, this, ev);
10425                 
10426                 ev.els.push(cg);
10427                 
10428                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10429                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10430                 //Roo.log(cg);
10431                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10432                 cg.setWidth(ebox.right - sbox.x -2);
10433             }
10434             
10435             
10436         }
10437         
10438     },
10439     
10440     onEventEnter: function (e, el,event,d) {
10441         this.fireEvent('evententer', this, el, event);
10442     },
10443     
10444     onEventLeave: function (e, el,event,d) {
10445         this.fireEvent('eventleave', this, el, event);
10446     },
10447     
10448     onEventClick: function (e, el,event,d) {
10449         this.fireEvent('eventclick', this, el, event);
10450     },
10451     
10452     onMonthChange: function () {
10453         this.store.load();
10454     },
10455     
10456     onLoad: function () 
10457     {   
10458         this.calevents = [];
10459         var cal = this;
10460         
10461         if(this.store.getCount() > 0){
10462             this.store.data.each(function(d){
10463                cal.addItem({
10464                     id : d.data.id,
10465                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10466                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10467                     time : d.data.start_time,
10468                     title : d.data.title,
10469                     description : d.data.description,
10470                     venue : d.data.venue
10471                 });
10472             });
10473         }
10474         
10475         this.renderEvents();
10476         
10477         if(this.loadMask){
10478             this.maskEl.hide();
10479         }
10480     },
10481     
10482     onBeforeLoad: function()
10483     {
10484         this.clearEvents();
10485         
10486         if(this.loadMask){
10487             this.maskEl.show();
10488         }
10489     }
10490 });
10491
10492  
10493  /*
10494  * - LGPL
10495  *
10496  * element
10497  * 
10498  */
10499
10500 /**
10501  * @class Roo.bootstrap.Popover
10502  * @extends Roo.bootstrap.Component
10503  * Bootstrap Popover class
10504  * @cfg {String} html contents of the popover   (or false to use children..)
10505  * @cfg {String} title of popover (or false to hide)
10506  * @cfg {String} placement how it is placed
10507  * @cfg {String} trigger click || hover (or false to trigger manually)
10508  * @cfg {String} over what (parent or false to trigger manually.)
10509  * 
10510  * @constructor
10511  * Create a new Popover
10512  * @param {Object} config The config object
10513  */
10514
10515 Roo.bootstrap.Popover = function(config){
10516     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10517 };
10518
10519 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10520     
10521     title: 'Fill in a title',
10522     html: false,
10523     
10524     placement : 'right',
10525     trigger : 'hover', // hover
10526     
10527     over: 'parent',
10528     
10529     can_build_overlaid : false,
10530     
10531     getChildContainer : function()
10532     {
10533         return this.el.select('.popover-content',true).first();
10534     },
10535     
10536     getAutoCreate : function(){
10537          Roo.log('make popover?');
10538         var cfg = {
10539            cls : 'popover roo-dynamic',
10540            style: 'display:block',
10541            cn : [
10542                 {
10543                     cls : 'arrow'
10544                 },
10545                 {
10546                     cls : 'popover-inner',
10547                     cn : [
10548                         {
10549                             tag: 'h3',
10550                             cls: 'popover-title',
10551                             html : this.title
10552                         },
10553                         {
10554                             cls : 'popover-content',
10555                             html : this.html
10556                         }
10557                     ]
10558                     
10559                 }
10560            ]
10561         };
10562         
10563         return cfg;
10564     },
10565     setTitle: function(str)
10566     {
10567         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10568     },
10569     setContent: function(str)
10570     {
10571         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10572     },
10573     // as it get's added to the bottom of the page.
10574     onRender : function(ct, position)
10575     {
10576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10577         if(!this.el){
10578             var cfg = Roo.apply({},  this.getAutoCreate());
10579             cfg.id = Roo.id();
10580             
10581             if (this.cls) {
10582                 cfg.cls += ' ' + this.cls;
10583             }
10584             if (this.style) {
10585                 cfg.style = this.style;
10586             }
10587             Roo.log("adding to ")
10588             this.el = Roo.get(document.body).createChild(cfg, position);
10589             Roo.log(this.el);
10590         }
10591         this.initEvents();
10592     },
10593     
10594     initEvents : function()
10595     {
10596         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10597         this.el.enableDisplayMode('block');
10598         this.el.hide();
10599         if (this.over === false) {
10600             return; 
10601         }
10602         if (this.triggers === false) {
10603             return;
10604         }
10605         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10606         var triggers = this.trigger ? this.trigger.split(' ') : [];
10607         Roo.each(triggers, function(trigger) {
10608         
10609             if (trigger == 'click') {
10610                 on_el.on('click', this.toggle, this);
10611             } else if (trigger != 'manual') {
10612                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10613                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10614       
10615                 on_el.on(eventIn  ,this.enter, this);
10616                 on_el.on(eventOut, this.leave, this);
10617             }
10618         }, this);
10619         
10620     },
10621     
10622     
10623     // private
10624     timeout : null,
10625     hoverState : null,
10626     
10627     toggle : function () {
10628         this.hoverState == 'in' ? this.leave() : this.enter();
10629     },
10630     
10631     enter : function () {
10632        
10633     
10634         clearTimeout(this.timeout);
10635     
10636         this.hoverState = 'in'
10637     
10638         if (!this.delay || !this.delay.show) {
10639             this.show();
10640             return 
10641         }
10642         var _t = this;
10643         this.timeout = setTimeout(function () {
10644             if (_t.hoverState == 'in') {
10645                 _t.show();
10646             }
10647         }, this.delay.show)
10648     },
10649     leave : function() {
10650         clearTimeout(this.timeout);
10651     
10652         this.hoverState = 'out'
10653     
10654         if (!this.delay || !this.delay.hide) {
10655             this.hide();
10656             return 
10657         }
10658         var _t = this;
10659         this.timeout = setTimeout(function () {
10660             if (_t.hoverState == 'out') {
10661                 _t.hide();
10662             }
10663         }, this.delay.hide)
10664     },
10665     
10666     show : function (on_el)
10667     {
10668         if (!on_el) {
10669             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10670         }
10671         // set content.
10672         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10673         if (this.html !== false) {
10674             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10675         }
10676         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10677         if (!this.title.length) {
10678             this.el.select('.popover-title',true).hide();
10679         }
10680         
10681         var placement = typeof this.placement == 'function' ?
10682             this.placement.call(this, this.el, on_el) :
10683             this.placement;
10684             
10685         var autoToken = /\s?auto?\s?/i;
10686         var autoPlace = autoToken.test(placement);
10687         if (autoPlace) {
10688             placement = placement.replace(autoToken, '') || 'top';
10689         }
10690         
10691         //this.el.detach()
10692         //this.el.setXY([0,0]);
10693         this.el.show();
10694         this.el.dom.style.display='block';
10695         this.el.addClass(placement);
10696         
10697         //this.el.appendTo(on_el);
10698         
10699         var p = this.getPosition();
10700         var box = this.el.getBox();
10701         
10702         if (autoPlace) {
10703             // fixme..
10704         }
10705         var align = Roo.bootstrap.Popover.alignment[placement]
10706         this.el.alignTo(on_el, align[0],align[1]);
10707         //var arrow = this.el.select('.arrow',true).first();
10708         //arrow.set(align[2], 
10709         
10710         this.el.addClass('in');
10711         this.hoverState = null;
10712         
10713         if (this.el.hasClass('fade')) {
10714             // fade it?
10715         }
10716         
10717     },
10718     hide : function()
10719     {
10720         this.el.setXY([0,0]);
10721         this.el.removeClass('in');
10722         this.el.hide();
10723         
10724     }
10725     
10726 });
10727
10728 Roo.bootstrap.Popover.alignment = {
10729     'left' : ['r-l', [-10,0], 'right'],
10730     'right' : ['l-r', [10,0], 'left'],
10731     'bottom' : ['t-b', [0,10], 'top'],
10732     'top' : [ 'b-t', [0,-10], 'bottom']
10733 };
10734
10735  /*
10736  * - LGPL
10737  *
10738  * Progress
10739  * 
10740  */
10741
10742 /**
10743  * @class Roo.bootstrap.Progress
10744  * @extends Roo.bootstrap.Component
10745  * Bootstrap Progress class
10746  * @cfg {Boolean} striped striped of the progress bar
10747  * @cfg {Boolean} active animated of the progress bar
10748  * 
10749  * 
10750  * @constructor
10751  * Create a new Progress
10752  * @param {Object} config The config object
10753  */
10754
10755 Roo.bootstrap.Progress = function(config){
10756     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10757 };
10758
10759 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10760     
10761     striped : false,
10762     active: false,
10763     
10764     getAutoCreate : function(){
10765         var cfg = {
10766             tag: 'div',
10767             cls: 'progress'
10768         };
10769         
10770         
10771         if(this.striped){
10772             cfg.cls += ' progress-striped';
10773         }
10774       
10775         if(this.active){
10776             cfg.cls += ' active';
10777         }
10778         
10779         
10780         return cfg;
10781     }
10782    
10783 });
10784
10785  
10786
10787  /*
10788  * - LGPL
10789  *
10790  * ProgressBar
10791  * 
10792  */
10793
10794 /**
10795  * @class Roo.bootstrap.ProgressBar
10796  * @extends Roo.bootstrap.Component
10797  * Bootstrap ProgressBar class
10798  * @cfg {Number} aria_valuenow aria-value now
10799  * @cfg {Number} aria_valuemin aria-value min
10800  * @cfg {Number} aria_valuemax aria-value max
10801  * @cfg {String} label label for the progress bar
10802  * @cfg {String} panel (success | info | warning | danger )
10803  * @cfg {String} role role of the progress bar
10804  * @cfg {String} sr_only text
10805  * 
10806  * 
10807  * @constructor
10808  * Create a new ProgressBar
10809  * @param {Object} config The config object
10810  */
10811
10812 Roo.bootstrap.ProgressBar = function(config){
10813     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10814 };
10815
10816 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10817     
10818     aria_valuenow : 0,
10819     aria_valuemin : 0,
10820     aria_valuemax : 100,
10821     label : false,
10822     panel : false,
10823     role : false,
10824     sr_only: false,
10825     
10826     getAutoCreate : function()
10827     {
10828         
10829         var cfg = {
10830             tag: 'div',
10831             cls: 'progress-bar',
10832             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10833         };
10834         
10835         if(this.sr_only){
10836             cfg.cn = {
10837                 tag: 'span',
10838                 cls: 'sr-only',
10839                 html: this.sr_only
10840             }
10841         }
10842         
10843         if(this.role){
10844             cfg.role = this.role;
10845         }
10846         
10847         if(this.aria_valuenow){
10848             cfg['aria-valuenow'] = this.aria_valuenow;
10849         }
10850         
10851         if(this.aria_valuemin){
10852             cfg['aria-valuemin'] = this.aria_valuemin;
10853         }
10854         
10855         if(this.aria_valuemax){
10856             cfg['aria-valuemax'] = this.aria_valuemax;
10857         }
10858         
10859         if(this.label && !this.sr_only){
10860             cfg.html = this.label;
10861         }
10862         
10863         if(this.panel){
10864             cfg.cls += ' progress-bar-' + this.panel;
10865         }
10866         
10867         return cfg;
10868     },
10869     
10870     update : function(aria_valuenow)
10871     {
10872         this.aria_valuenow = aria_valuenow;
10873         
10874         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10875     }
10876    
10877 });
10878
10879  
10880
10881  /*
10882  * - LGPL
10883  *
10884  * TabPanel
10885  * 
10886  */
10887
10888 /**
10889  * @class Roo.bootstrap.TabPanel
10890  * @extends Roo.bootstrap.Component
10891  * Bootstrap TabPanel class
10892  * @cfg {Boolean} active panel active
10893  * @cfg {String} html panel content
10894  * @cfg {String} tabId tab relate id
10895  * 
10896  * 
10897  * @constructor
10898  * Create a new TabPanel
10899  * @param {Object} config The config object
10900  */
10901
10902 Roo.bootstrap.TabPanel = function(config){
10903     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10904 };
10905
10906 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10907     
10908     active: false,
10909     html: false,
10910     tabId: false,
10911     
10912     getAutoCreate : function(){
10913         var cfg = {
10914             tag: 'div',
10915             cls: 'tab-pane',
10916             html: this.html || ''
10917         };
10918         
10919         if(this.active){
10920             cfg.cls += ' active';
10921         }
10922         
10923         if(this.tabId){
10924             cfg.tabId = this.tabId;
10925         }
10926         
10927         return cfg;
10928     }
10929    
10930 });
10931
10932  
10933
10934  /*
10935  * - LGPL
10936  *
10937  * DateField
10938  * 
10939  */
10940
10941 /**
10942  * @class Roo.bootstrap.DateField
10943  * @extends Roo.bootstrap.Input
10944  * Bootstrap DateField class
10945  * @cfg {Number} weekStart default 0
10946  * @cfg {Number} weekStart default 0
10947  * @cfg {Number} viewMode default empty, (months|years)
10948  * @cfg {Number} minViewMode default empty, (months|years)
10949  * @cfg {Number} startDate default -Infinity
10950  * @cfg {Number} endDate default Infinity
10951  * @cfg {Boolean} todayHighlight default false
10952  * @cfg {Boolean} todayBtn default false
10953  * @cfg {Boolean} calendarWeeks default false
10954  * @cfg {Object} daysOfWeekDisabled default empty
10955  * 
10956  * @cfg {Boolean} keyboardNavigation default true
10957  * @cfg {String} language default en
10958  * 
10959  * @constructor
10960  * Create a new DateField
10961  * @param {Object} config The config object
10962  */
10963
10964 Roo.bootstrap.DateField = function(config){
10965     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10966      this.addEvents({
10967             /**
10968              * @event show
10969              * Fires when this field show.
10970              * @param {Roo.bootstrap.DateField} this
10971              * @param {Mixed} date The date value
10972              */
10973             show : true,
10974             /**
10975              * @event show
10976              * Fires when this field hide.
10977              * @param {Roo.bootstrap.DateField} this
10978              * @param {Mixed} date The date value
10979              */
10980             hide : true,
10981             /**
10982              * @event select
10983              * Fires when select a date.
10984              * @param {Roo.bootstrap.DateField} this
10985              * @param {Mixed} date The date value
10986              */
10987             select : true
10988         });
10989 };
10990
10991 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10992     
10993     /**
10994      * @cfg {String} format
10995      * The default date format string which can be overriden for localization support.  The format must be
10996      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10997      */
10998     format : "m/d/y",
10999     /**
11000      * @cfg {String} altFormats
11001      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11002      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11003      */
11004     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11005     
11006     weekStart : 0,
11007     
11008     viewMode : '',
11009     
11010     minViewMode : '',
11011     
11012     todayHighlight : false,
11013     
11014     todayBtn: false,
11015     
11016     language: 'en',
11017     
11018     keyboardNavigation: true,
11019     
11020     calendarWeeks: false,
11021     
11022     startDate: -Infinity,
11023     
11024     endDate: Infinity,
11025     
11026     daysOfWeekDisabled: [],
11027     
11028     _events: [],
11029     
11030     UTCDate: function()
11031     {
11032         return new Date(Date.UTC.apply(Date, arguments));
11033     },
11034     
11035     UTCToday: function()
11036     {
11037         var today = new Date();
11038         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11039     },
11040     
11041     getDate: function() {
11042             var d = this.getUTCDate();
11043             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11044     },
11045     
11046     getUTCDate: function() {
11047             return this.date;
11048     },
11049     
11050     setDate: function(d) {
11051             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11052     },
11053     
11054     setUTCDate: function(d) {
11055             this.date = d;
11056             this.setValue(this.formatDate(this.date));
11057     },
11058         
11059     onRender: function(ct, position)
11060     {
11061         
11062         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11063         
11064         this.language = this.language || 'en';
11065         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11066         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11067         
11068         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11069         this.format = this.format || 'm/d/y';
11070         this.isInline = false;
11071         this.isInput = true;
11072         this.component = this.el.select('.add-on', true).first() || false;
11073         this.component = (this.component && this.component.length === 0) ? false : this.component;
11074         this.hasInput = this.component && this.inputEL().length;
11075         
11076         if (typeof(this.minViewMode === 'string')) {
11077             switch (this.minViewMode) {
11078                 case 'months':
11079                     this.minViewMode = 1;
11080                     break;
11081                 case 'years':
11082                     this.minViewMode = 2;
11083                     break;
11084                 default:
11085                     this.minViewMode = 0;
11086                     break;
11087             }
11088         }
11089         
11090         if (typeof(this.viewMode === 'string')) {
11091             switch (this.viewMode) {
11092                 case 'months':
11093                     this.viewMode = 1;
11094                     break;
11095                 case 'years':
11096                     this.viewMode = 2;
11097                     break;
11098                 default:
11099                     this.viewMode = 0;
11100                     break;
11101             }
11102         }
11103                 
11104         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11105         
11106         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11107         
11108         this.picker().on('mousedown', this.onMousedown, this);
11109         this.picker().on('click', this.onClick, this);
11110         
11111         this.picker().addClass('datepicker-dropdown');
11112         
11113         this.startViewMode = this.viewMode;
11114         
11115         
11116         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11117             if(!this.calendarWeeks){
11118                 v.remove();
11119                 return;
11120             };
11121             
11122             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11123             v.attr('colspan', function(i, val){
11124                 return parseInt(val) + 1;
11125             });
11126         })
11127                         
11128         
11129         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11130         
11131         this.setStartDate(this.startDate);
11132         this.setEndDate(this.endDate);
11133         
11134         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11135         
11136         this.fillDow();
11137         this.fillMonths();
11138         this.update();
11139         this.showMode();
11140         
11141         if(this.isInline) {
11142             this.show();
11143         }
11144     },
11145     
11146     picker : function()
11147     {
11148         return this.el.select('.datepicker', true).first();
11149     },
11150     
11151     fillDow: function()
11152     {
11153         var dowCnt = this.weekStart;
11154         
11155         var dow = {
11156             tag: 'tr',
11157             cn: [
11158                 
11159             ]
11160         };
11161         
11162         if(this.calendarWeeks){
11163             dow.cn.push({
11164                 tag: 'th',
11165                 cls: 'cw',
11166                 html: '&nbsp;'
11167             })
11168         }
11169         
11170         while (dowCnt < this.weekStart + 7) {
11171             dow.cn.push({
11172                 tag: 'th',
11173                 cls: 'dow',
11174                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11175             });
11176         }
11177         
11178         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11179     },
11180     
11181     fillMonths: function()
11182     {    
11183         var i = 0
11184         var months = this.picker().select('>.datepicker-months td', true).first();
11185         
11186         months.dom.innerHTML = '';
11187         
11188         while (i < 12) {
11189             var month = {
11190                 tag: 'span',
11191                 cls: 'month',
11192                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11193             }
11194             
11195             months.createChild(month);
11196         }
11197         
11198     },
11199     
11200     update: function(){
11201         
11202         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11203         
11204         if (this.date < this.startDate) {
11205             this.viewDate = new Date(this.startDate);
11206         } else if (this.date > this.endDate) {
11207             this.viewDate = new Date(this.endDate);
11208         } else {
11209             this.viewDate = new Date(this.date);
11210         }
11211         
11212         this.fill();
11213     },
11214     
11215     fill: function() {
11216         var d = new Date(this.viewDate),
11217                 year = d.getUTCFullYear(),
11218                 month = d.getUTCMonth(),
11219                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11220                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11221                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11222                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11223                 currentDate = this.date && this.date.valueOf(),
11224                 today = this.UTCToday();
11225         
11226         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11227         
11228 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11229         
11230 //        this.picker.select('>tfoot th.today').
11231 //                                              .text(dates[this.language].today)
11232 //                                              .toggle(this.todayBtn !== false);
11233     
11234         this.updateNavArrows();
11235         this.fillMonths();
11236                                                 
11237         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11238         
11239         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11240          
11241         prevMonth.setUTCDate(day);
11242         
11243         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11244         
11245         var nextMonth = new Date(prevMonth);
11246         
11247         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11248         
11249         nextMonth = nextMonth.valueOf();
11250         
11251         var fillMonths = false;
11252         
11253         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11254         
11255         while(prevMonth.valueOf() < nextMonth) {
11256             var clsName = '';
11257             
11258             if (prevMonth.getUTCDay() === this.weekStart) {
11259                 if(fillMonths){
11260                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11261                 }
11262                     
11263                 fillMonths = {
11264                     tag: 'tr',
11265                     cn: []
11266                 };
11267                 
11268                 if(this.calendarWeeks){
11269                     // ISO 8601: First week contains first thursday.
11270                     // ISO also states week starts on Monday, but we can be more abstract here.
11271                     var
11272                     // Start of current week: based on weekstart/current date
11273                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11274                     // Thursday of this week
11275                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11276                     // First Thursday of year, year from thursday
11277                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11278                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11279                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11280                     
11281                     fillMonths.cn.push({
11282                         tag: 'td',
11283                         cls: 'cw',
11284                         html: calWeek
11285                     });
11286                 }
11287             }
11288             
11289             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11290                 clsName += ' old';
11291             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11292                 clsName += ' new';
11293             }
11294             if (this.todayHighlight &&
11295                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11296                 prevMonth.getUTCMonth() == today.getMonth() &&
11297                 prevMonth.getUTCDate() == today.getDate()) {
11298                 clsName += ' today';
11299             }
11300             
11301             if (currentDate && prevMonth.valueOf() === currentDate) {
11302                 clsName += ' active';
11303             }
11304             
11305             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11306                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11307                     clsName += ' disabled';
11308             }
11309             
11310             fillMonths.cn.push({
11311                 tag: 'td',
11312                 cls: 'day ' + clsName,
11313                 html: prevMonth.getDate()
11314             })
11315             
11316             prevMonth.setDate(prevMonth.getDate()+1);
11317         }
11318           
11319         var currentYear = this.date && this.date.getUTCFullYear();
11320         var currentMonth = this.date && this.date.getUTCMonth();
11321         
11322         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11323         
11324         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11325             v.removeClass('active');
11326             
11327             if(currentYear === year && k === currentMonth){
11328                 v.addClass('active');
11329             }
11330             
11331             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11332                 v.addClass('disabled');
11333             }
11334             
11335         });
11336         
11337         
11338         year = parseInt(year/10, 10) * 10;
11339         
11340         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11341         
11342         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11343         
11344         year -= 1;
11345         for (var i = -1; i < 11; i++) {
11346             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11347                 tag: 'span',
11348                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11349                 html: year
11350             })
11351             
11352             year += 1;
11353         }
11354     },
11355     
11356     showMode: function(dir) {
11357         if (dir) {
11358             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11359         }
11360         Roo.each(this.picker().select('>div',true).elements, function(v){
11361             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11362             v.hide();
11363         });
11364         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11365     },
11366     
11367     place: function()
11368     {
11369         if(this.isInline) return;
11370         
11371         this.picker().removeClass(['bottom', 'top']);
11372         
11373         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11374             /*
11375              * place to the top of element!
11376              *
11377              */
11378             
11379             this.picker().addClass('top');
11380             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11381             
11382             return;
11383         }
11384         
11385         this.picker().addClass('bottom');
11386         
11387         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11388     },
11389     
11390     parseDate : function(value){
11391         if(!value || value instanceof Date){
11392             return value;
11393         }
11394         var v = Date.parseDate(value, this.format);
11395         if (!v && this.useIso) {
11396             v = Date.parseDate(value, 'Y-m-d');
11397         }
11398         if(!v && this.altFormats){
11399             if(!this.altFormatsArray){
11400                 this.altFormatsArray = this.altFormats.split("|");
11401             }
11402             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11403                 v = Date.parseDate(value, this.altFormatsArray[i]);
11404             }
11405         }
11406         return v;
11407     },
11408     
11409     formatDate : function(date, fmt){
11410         return (!date || !(date instanceof Date)) ?
11411         date : date.dateFormat(fmt || this.format);
11412     },
11413     
11414     onFocus : function()
11415     {
11416         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11417         this.show();
11418     },
11419     
11420     onBlur : function()
11421     {
11422         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11423         this.hide();
11424     },
11425     
11426     show : function()
11427     {
11428         this.picker().show();
11429         this.update();
11430         this.place();
11431         
11432         this.fireEvent('show', this, this.date);
11433     },
11434     
11435     hide : function()
11436     {
11437         if(this.isInline) return;
11438         this.picker().hide();
11439         this.viewMode = this.startViewMode;
11440         this.showMode();
11441         
11442         this.fireEvent('hide', this, this.date);
11443         
11444     },
11445     
11446     onMousedown: function(e){
11447         e.stopPropagation();
11448         e.preventDefault();
11449     },
11450     
11451     keyup: function(e){
11452         Roo.bootstrap.DateField.superclass.keyup.call(this);
11453         this.update();
11454         
11455     },
11456
11457     setValue: function(v){
11458         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11459         
11460         this.fireEvent('select', this, this.date);
11461         
11462     },
11463     
11464     fireKey: function(e){
11465         if (!this.picker().isVisible()){
11466             if (e.keyCode == 27) // allow escape to hide and re-show picker
11467                 this.show();
11468             return;
11469         }
11470         var dateChanged = false,
11471         dir, day, month,
11472         newDate, newViewDate;
11473         switch(e.keyCode){
11474             case 27: // escape
11475                 this.hide();
11476                 e.preventDefault();
11477                 break;
11478             case 37: // left
11479             case 39: // right
11480                 if (!this.keyboardNavigation) break;
11481                 dir = e.keyCode == 37 ? -1 : 1;
11482                 
11483                 if (e.ctrlKey){
11484                     newDate = this.moveYear(this.date, dir);
11485                     newViewDate = this.moveYear(this.viewDate, dir);
11486                 } else if (e.shiftKey){
11487                     newDate = this.moveMonth(this.date, dir);
11488                     newViewDate = this.moveMonth(this.viewDate, dir);
11489                 } else {
11490                     newDate = new Date(this.date);
11491                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11492                     newViewDate = new Date(this.viewDate);
11493                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11494                 }
11495                 if (this.dateWithinRange(newDate)){
11496                     this.date = newDate;
11497                     this.viewDate = newViewDate;
11498                     this.setValue(this.formatDate(this.date));
11499                     this.update();
11500                     e.preventDefault();
11501                     dateChanged = true;
11502                 }
11503                 break;
11504             case 38: // up
11505             case 40: // down
11506                 if (!this.keyboardNavigation) break;
11507                 dir = e.keyCode == 38 ? -1 : 1;
11508                 if (e.ctrlKey){
11509                     newDate = this.moveYear(this.date, dir);
11510                     newViewDate = this.moveYear(this.viewDate, dir);
11511                 } else if (e.shiftKey){
11512                     newDate = this.moveMonth(this.date, dir);
11513                     newViewDate = this.moveMonth(this.viewDate, dir);
11514                 } else {
11515                     newDate = new Date(this.date);
11516                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11517                     newViewDate = new Date(this.viewDate);
11518                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11519                 }
11520                 if (this.dateWithinRange(newDate)){
11521                     this.date = newDate;
11522                     this.viewDate = newViewDate;
11523                     this.setValue(this.formatDate(this.date));
11524                     this.update();
11525                     e.preventDefault();
11526                     dateChanged = true;
11527                 }
11528                 break;
11529             case 13: // enter
11530                 this.setValue(this.formatDate(this.date));
11531                 this.hide();
11532                 e.preventDefault();
11533                 break;
11534             case 9: // tab
11535                 this.setValue(this.formatDate(this.date));
11536                 this.hide();
11537                 break;
11538         }
11539     },
11540     
11541     
11542     onClick: function(e) {
11543         e.stopPropagation();
11544         e.preventDefault();
11545         
11546         var target = e.getTarget();
11547         
11548         if(target.nodeName.toLowerCase() === 'i'){
11549             target = Roo.get(target).dom.parentNode;
11550         }
11551         
11552         var nodeName = target.nodeName;
11553         var className = target.className;
11554         var html = target.innerHTML;
11555         
11556         switch(nodeName.toLowerCase()) {
11557             case 'th':
11558                 switch(className) {
11559                     case 'switch':
11560                         this.showMode(1);
11561                         break;
11562                     case 'prev':
11563                     case 'next':
11564                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11565                         switch(this.viewMode){
11566                                 case 0:
11567                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11568                                         break;
11569                                 case 1:
11570                                 case 2:
11571                                         this.viewDate = this.moveYear(this.viewDate, dir);
11572                                         break;
11573                         }
11574                         this.fill();
11575                         break;
11576                     case 'today':
11577                         var date = new Date();
11578                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11579                         this.fill()
11580                         this.setValue(this.formatDate(this.date));
11581                         this.hide();
11582                         break;
11583                 }
11584                 break;
11585             case 'span':
11586                 if (className.indexOf('disabled') === -1) {
11587                     this.viewDate.setUTCDate(1);
11588                     if (className.indexOf('month') !== -1) {
11589                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11590                     } else {
11591                         var year = parseInt(html, 10) || 0;
11592                         this.viewDate.setUTCFullYear(year);
11593                         
11594                     }
11595                     this.showMode(-1);
11596                     this.fill();
11597                 }
11598                 break;
11599                 
11600             case 'td':
11601                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11602                     var day = parseInt(html, 10) || 1;
11603                     var year = this.viewDate.getUTCFullYear(),
11604                         month = this.viewDate.getUTCMonth();
11605
11606                     if (className.indexOf('old') !== -1) {
11607                         if(month === 0 ){
11608                             month = 11;
11609                             year -= 1;
11610                         }else{
11611                             month -= 1;
11612                         }
11613                     } else if (className.indexOf('new') !== -1) {
11614                         if (month == 11) {
11615                             month = 0;
11616                             year += 1;
11617                         } else {
11618                             month += 1;
11619                         }
11620                     }
11621                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11622                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11623                     this.fill();
11624                     this.setValue(this.formatDate(this.date));
11625                     this.hide();
11626                 }
11627                 break;
11628         }
11629     },
11630     
11631     setStartDate: function(startDate){
11632         this.startDate = startDate || -Infinity;
11633         if (this.startDate !== -Infinity) {
11634             this.startDate = this.parseDate(this.startDate);
11635         }
11636         this.update();
11637         this.updateNavArrows();
11638     },
11639
11640     setEndDate: function(endDate){
11641         this.endDate = endDate || Infinity;
11642         if (this.endDate !== Infinity) {
11643             this.endDate = this.parseDate(this.endDate);
11644         }
11645         this.update();
11646         this.updateNavArrows();
11647     },
11648     
11649     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11650         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11651         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11652             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11653         }
11654         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11655             return parseInt(d, 10);
11656         });
11657         this.update();
11658         this.updateNavArrows();
11659     },
11660     
11661     updateNavArrows: function() {
11662         var d = new Date(this.viewDate),
11663         year = d.getUTCFullYear(),
11664         month = d.getUTCMonth();
11665         
11666         Roo.each(this.picker().select('.prev', true).elements, function(v){
11667             v.show();
11668             switch (this.viewMode) {
11669                 case 0:
11670
11671                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11672                         v.hide();
11673                     }
11674                     break;
11675                 case 1:
11676                 case 2:
11677                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11678                         v.hide();
11679                     }
11680                     break;
11681             }
11682         });
11683         
11684         Roo.each(this.picker().select('.next', true).elements, function(v){
11685             v.show();
11686             switch (this.viewMode) {
11687                 case 0:
11688
11689                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11690                         v.hide();
11691                     }
11692                     break;
11693                 case 1:
11694                 case 2:
11695                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11696                         v.hide();
11697                     }
11698                     break;
11699             }
11700         })
11701     },
11702     
11703     moveMonth: function(date, dir){
11704         if (!dir) return date;
11705         var new_date = new Date(date.valueOf()),
11706         day = new_date.getUTCDate(),
11707         month = new_date.getUTCMonth(),
11708         mag = Math.abs(dir),
11709         new_month, test;
11710         dir = dir > 0 ? 1 : -1;
11711         if (mag == 1){
11712             test = dir == -1
11713             // If going back one month, make sure month is not current month
11714             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11715             ? function(){
11716                 return new_date.getUTCMonth() == month;
11717             }
11718             // If going forward one month, make sure month is as expected
11719             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11720             : function(){
11721                 return new_date.getUTCMonth() != new_month;
11722             };
11723             new_month = month + dir;
11724             new_date.setUTCMonth(new_month);
11725             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11726             if (new_month < 0 || new_month > 11)
11727                 new_month = (new_month + 12) % 12;
11728         } else {
11729             // For magnitudes >1, move one month at a time...
11730             for (var i=0; i<mag; i++)
11731                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11732                 new_date = this.moveMonth(new_date, dir);
11733             // ...then reset the day, keeping it in the new month
11734             new_month = new_date.getUTCMonth();
11735             new_date.setUTCDate(day);
11736             test = function(){
11737                 return new_month != new_date.getUTCMonth();
11738             };
11739         }
11740         // Common date-resetting loop -- if date is beyond end of month, make it
11741         // end of month
11742         while (test()){
11743             new_date.setUTCDate(--day);
11744             new_date.setUTCMonth(new_month);
11745         }
11746         return new_date;
11747     },
11748
11749     moveYear: function(date, dir){
11750         return this.moveMonth(date, dir*12);
11751     },
11752
11753     dateWithinRange: function(date){
11754         return date >= this.startDate && date <= this.endDate;
11755     },
11756
11757     
11758     remove: function() {
11759         this.picker().remove();
11760     }
11761    
11762 });
11763
11764 Roo.apply(Roo.bootstrap.DateField,  {
11765     
11766     head : {
11767         tag: 'thead',
11768         cn: [
11769         {
11770             tag: 'tr',
11771             cn: [
11772             {
11773                 tag: 'th',
11774                 cls: 'prev',
11775                 html: '<i class="icon-arrow-left"/>'
11776             },
11777             {
11778                 tag: 'th',
11779                 cls: 'switch',
11780                 colspan: '5'
11781             },
11782             {
11783                 tag: 'th',
11784                 cls: 'next',
11785                 html: '<i class="icon-arrow-right"/>'
11786             }
11787
11788             ]
11789         }
11790         ]
11791     },
11792     
11793     content : {
11794         tag: 'tbody',
11795         cn: [
11796         {
11797             tag: 'tr',
11798             cn: [
11799             {
11800                 tag: 'td',
11801                 colspan: '7'
11802             }
11803             ]
11804         }
11805         ]
11806     },
11807     
11808     footer : {
11809         tag: 'tfoot',
11810         cn: [
11811         {
11812             tag: 'tr',
11813             cn: [
11814             {
11815                 tag: 'th',
11816                 colspan: '7',
11817                 cls: 'today'
11818             }
11819                     
11820             ]
11821         }
11822         ]
11823     },
11824     
11825     dates:{
11826         en: {
11827             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11828             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11829             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11830             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11831             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11832             today: "Today"
11833         }
11834     },
11835     
11836     modes: [
11837     {
11838         clsName: 'days',
11839         navFnc: 'Month',
11840         navStep: 1
11841     },
11842     {
11843         clsName: 'months',
11844         navFnc: 'FullYear',
11845         navStep: 1
11846     },
11847     {
11848         clsName: 'years',
11849         navFnc: 'FullYear',
11850         navStep: 10
11851     }]
11852 });
11853
11854 Roo.apply(Roo.bootstrap.DateField,  {
11855   
11856     template : {
11857         tag: 'div',
11858         cls: 'datepicker dropdown-menu',
11859         cn: [
11860         {
11861             tag: 'div',
11862             cls: 'datepicker-days',
11863             cn: [
11864             {
11865                 tag: 'table',
11866                 cls: 'table-condensed',
11867                 cn:[
11868                 Roo.bootstrap.DateField.head,
11869                 {
11870                     tag: 'tbody'
11871                 },
11872                 Roo.bootstrap.DateField.footer
11873                 ]
11874             }
11875             ]
11876         },
11877         {
11878             tag: 'div',
11879             cls: 'datepicker-months',
11880             cn: [
11881             {
11882                 tag: 'table',
11883                 cls: 'table-condensed',
11884                 cn:[
11885                 Roo.bootstrap.DateField.head,
11886                 Roo.bootstrap.DateField.content,
11887                 Roo.bootstrap.DateField.footer
11888                 ]
11889             }
11890             ]
11891         },
11892         {
11893             tag: 'div',
11894             cls: 'datepicker-years',
11895             cn: [
11896             {
11897                 tag: 'table',
11898                 cls: 'table-condensed',
11899                 cn:[
11900                 Roo.bootstrap.DateField.head,
11901                 Roo.bootstrap.DateField.content,
11902                 Roo.bootstrap.DateField.footer
11903                 ]
11904             }
11905             ]
11906         }
11907         ]
11908     }
11909 });
11910
11911  
11912
11913  /*
11914  * - LGPL
11915  *
11916  * TimeField
11917  * 
11918  */
11919
11920 /**
11921  * @class Roo.bootstrap.TimeField
11922  * @extends Roo.bootstrap.Input
11923  * Bootstrap DateField class
11924  * 
11925  * 
11926  * @constructor
11927  * Create a new TimeField
11928  * @param {Object} config The config object
11929  */
11930
11931 Roo.bootstrap.TimeField = function(config){
11932     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11933     this.addEvents({
11934             /**
11935              * @event show
11936              * Fires when this field show.
11937              * @param {Roo.bootstrap.DateField} this
11938              * @param {Mixed} date The date value
11939              */
11940             show : true,
11941             /**
11942              * @event show
11943              * Fires when this field hide.
11944              * @param {Roo.bootstrap.DateField} this
11945              * @param {Mixed} date The date value
11946              */
11947             hide : true,
11948             /**
11949              * @event select
11950              * Fires when select a date.
11951              * @param {Roo.bootstrap.DateField} this
11952              * @param {Mixed} date The date value
11953              */
11954             select : true
11955         });
11956 };
11957
11958 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11959     
11960     /**
11961      * @cfg {String} format
11962      * The default time format string which can be overriden for localization support.  The format must be
11963      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11964      */
11965     format : "H:i",
11966        
11967     onRender: function(ct, position)
11968     {
11969         
11970         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11971                 
11972         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11973         
11974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11975         
11976         this.pop = this.picker().select('>.datepicker-time',true).first();
11977         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11978         
11979         this.picker().on('mousedown', this.onMousedown, this);
11980         this.picker().on('click', this.onClick, this);
11981         
11982         this.picker().addClass('datepicker-dropdown');
11983     
11984         this.fillTime();
11985         this.update();
11986             
11987         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11988         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11989         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11990         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11991         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11992         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11993
11994     },
11995     
11996     fireKey: function(e){
11997         if (!this.picker().isVisible()){
11998             if (e.keyCode == 27) // allow escape to hide and re-show picker
11999                 this.show();
12000             return;
12001         }
12002
12003         e.preventDefault();
12004         
12005         switch(e.keyCode){
12006             case 27: // escape
12007                 this.hide();
12008                 break;
12009             case 37: // left
12010             case 39: // right
12011                 this.onTogglePeriod();
12012                 break;
12013             case 38: // up
12014                 this.onIncrementMinutes();
12015                 break;
12016             case 40: // down
12017                 this.onDecrementMinutes();
12018                 break;
12019             case 13: // enter
12020             case 9: // tab
12021                 this.setTime();
12022                 break;
12023         }
12024     },
12025     
12026     onClick: function(e) {
12027         e.stopPropagation();
12028         e.preventDefault();
12029     },
12030     
12031     picker : function()
12032     {
12033         return this.el.select('.datepicker', true).first();
12034     },
12035     
12036     fillTime: function()
12037     {    
12038         var time = this.pop.select('tbody', true).first();
12039         
12040         time.dom.innerHTML = '';
12041         
12042         time.createChild({
12043             tag: 'tr',
12044             cn: [
12045                 {
12046                     tag: 'td',
12047                     cn: [
12048                         {
12049                             tag: 'a',
12050                             href: '#',
12051                             cls: 'btn',
12052                             cn: [
12053                                 {
12054                                     tag: 'span',
12055                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12056                                 }
12057                             ]
12058                         } 
12059                     ]
12060                 },
12061                 {
12062                     tag: 'td',
12063                     cls: 'separator'
12064                 },
12065                 {
12066                     tag: 'td',
12067                     cn: [
12068                         {
12069                             tag: 'a',
12070                             href: '#',
12071                             cls: 'btn',
12072                             cn: [
12073                                 {
12074                                     tag: 'span',
12075                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12076                                 }
12077                             ]
12078                         }
12079                     ]
12080                 },
12081                 {
12082                     tag: 'td',
12083                     cls: 'separator'
12084                 }
12085             ]
12086         });
12087         
12088         time.createChild({
12089             tag: 'tr',
12090             cn: [
12091                 {
12092                     tag: 'td',
12093                     cn: [
12094                         {
12095                             tag: 'span',
12096                             cls: 'timepicker-hour',
12097                             html: '00'
12098                         }  
12099                     ]
12100                 },
12101                 {
12102                     tag: 'td',
12103                     cls: 'separator',
12104                     html: ':'
12105                 },
12106                 {
12107                     tag: 'td',
12108                     cn: [
12109                         {
12110                             tag: 'span',
12111                             cls: 'timepicker-minute',
12112                             html: '00'
12113                         }  
12114                     ]
12115                 },
12116                 {
12117                     tag: 'td',
12118                     cls: 'separator'
12119                 },
12120                 {
12121                     tag: 'td',
12122                     cn: [
12123                         {
12124                             tag: 'button',
12125                             type: 'button',
12126                             cls: 'btn btn-primary period',
12127                             html: 'AM'
12128                             
12129                         }
12130                     ]
12131                 }
12132             ]
12133         });
12134         
12135         time.createChild({
12136             tag: 'tr',
12137             cn: [
12138                 {
12139                     tag: 'td',
12140                     cn: [
12141                         {
12142                             tag: 'a',
12143                             href: '#',
12144                             cls: 'btn',
12145                             cn: [
12146                                 {
12147                                     tag: 'span',
12148                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12149                                 }
12150                             ]
12151                         }
12152                     ]
12153                 },
12154                 {
12155                     tag: 'td',
12156                     cls: 'separator'
12157                 },
12158                 {
12159                     tag: 'td',
12160                     cn: [
12161                         {
12162                             tag: 'a',
12163                             href: '#',
12164                             cls: 'btn',
12165                             cn: [
12166                                 {
12167                                     tag: 'span',
12168                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12169                                 }
12170                             ]
12171                         }
12172                     ]
12173                 },
12174                 {
12175                     tag: 'td',
12176                     cls: 'separator'
12177                 }
12178             ]
12179         });
12180         
12181     },
12182     
12183     update: function()
12184     {
12185         
12186         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12187         
12188         this.fill();
12189     },
12190     
12191     fill: function() 
12192     {
12193         var hours = this.time.getHours();
12194         var minutes = this.time.getMinutes();
12195         var period = 'AM';
12196         
12197         if(hours > 11){
12198             period = 'PM';
12199         }
12200         
12201         if(hours == 0){
12202             hours = 12;
12203         }
12204         
12205         
12206         if(hours > 12){
12207             hours = hours - 12;
12208         }
12209         
12210         if(hours < 10){
12211             hours = '0' + hours;
12212         }
12213         
12214         if(minutes < 10){
12215             minutes = '0' + minutes;
12216         }
12217         
12218         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12219         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12220         this.pop.select('button', true).first().dom.innerHTML = period;
12221         
12222     },
12223     
12224     place: function()
12225     {   
12226         this.picker().removeClass(['bottom', 'top']);
12227         
12228         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12229             /*
12230              * place to the top of element!
12231              *
12232              */
12233             
12234             this.picker().addClass('top');
12235             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12236             
12237             return;
12238         }
12239         
12240         this.picker().addClass('bottom');
12241         
12242         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12243     },
12244   
12245     onFocus : function()
12246     {
12247         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12248         this.show();
12249     },
12250     
12251     onBlur : function()
12252     {
12253         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12254         this.hide();
12255     },
12256     
12257     show : function()
12258     {
12259         this.picker().show();
12260         this.pop.show();
12261         this.update();
12262         this.place();
12263         
12264         this.fireEvent('show', this, this.date);
12265     },
12266     
12267     hide : function()
12268     {
12269         this.picker().hide();
12270         this.pop.hide();
12271         
12272         this.fireEvent('hide', this, this.date);
12273     },
12274     
12275     setTime : function()
12276     {
12277         this.hide();
12278         this.setValue(this.time.format(this.format));
12279         
12280         this.fireEvent('select', this, this.date);
12281         
12282         
12283     },
12284     
12285     onMousedown: function(e){
12286         e.stopPropagation();
12287         e.preventDefault();
12288     },
12289     
12290     onIncrementHours: function()
12291     {
12292         Roo.log('onIncrementHours');
12293         this.time = this.time.add(Date.HOUR, 1);
12294         this.update();
12295         
12296     },
12297     
12298     onDecrementHours: function()
12299     {
12300         Roo.log('onDecrementHours');
12301         this.time = this.time.add(Date.HOUR, -1);
12302         this.update();
12303     },
12304     
12305     onIncrementMinutes: function()
12306     {
12307         Roo.log('onIncrementMinutes');
12308         this.time = this.time.add(Date.MINUTE, 1);
12309         this.update();
12310     },
12311     
12312     onDecrementMinutes: function()
12313     {
12314         Roo.log('onDecrementMinutes');
12315         this.time = this.time.add(Date.MINUTE, -1);
12316         this.update();
12317     },
12318     
12319     onTogglePeriod: function()
12320     {
12321         Roo.log('onTogglePeriod');
12322         this.time = this.time.add(Date.HOUR, 12);
12323         this.update();
12324     }
12325     
12326    
12327 });
12328
12329 Roo.apply(Roo.bootstrap.TimeField,  {
12330     
12331     content : {
12332         tag: 'tbody',
12333         cn: [
12334             {
12335                 tag: 'tr',
12336                 cn: [
12337                 {
12338                     tag: 'td',
12339                     colspan: '7'
12340                 }
12341                 ]
12342             }
12343         ]
12344     },
12345     
12346     footer : {
12347         tag: 'tfoot',
12348         cn: [
12349             {
12350                 tag: 'tr',
12351                 cn: [
12352                 {
12353                     tag: 'th',
12354                     colspan: '7',
12355                     cls: '',
12356                     cn: [
12357                         {
12358                             tag: 'button',
12359                             cls: 'btn btn-info ok',
12360                             html: 'OK'
12361                         }
12362                     ]
12363                 }
12364
12365                 ]
12366             }
12367         ]
12368     }
12369 });
12370
12371 Roo.apply(Roo.bootstrap.TimeField,  {
12372   
12373     template : {
12374         tag: 'div',
12375         cls: 'datepicker dropdown-menu',
12376         cn: [
12377             {
12378                 tag: 'div',
12379                 cls: 'datepicker-time',
12380                 cn: [
12381                 {
12382                     tag: 'table',
12383                     cls: 'table-condensed',
12384                     cn:[
12385                     Roo.bootstrap.TimeField.content,
12386                     Roo.bootstrap.TimeField.footer
12387                     ]
12388                 }
12389                 ]
12390             }
12391         ]
12392     }
12393 });
12394
12395  
12396
12397  /*
12398  * - LGPL
12399  *
12400  * CheckBox
12401  * 
12402  */
12403
12404 /**
12405  * @class Roo.bootstrap.CheckBox
12406  * @extends Roo.bootstrap.Input
12407  * Bootstrap CheckBox class
12408  * 
12409  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12410  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12411  * @cfg {String} boxLabel The text that appears beside the checkbox
12412  * @cfg {Boolean} checked initnal the element
12413  * 
12414  * @constructor
12415  * Create a new CheckBox
12416  * @param {Object} config The config object
12417  */
12418
12419 Roo.bootstrap.CheckBox = function(config){
12420     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12421    
12422         this.addEvents({
12423             /**
12424             * @event check
12425             * Fires when the element is checked or unchecked.
12426             * @param {Roo.bootstrap.CheckBox} this This input
12427             * @param {Boolean} checked The new checked value
12428             */
12429            check : true
12430         });
12431 };
12432
12433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12434     
12435     inputType: 'checkbox',
12436     inputValue: 1,
12437     valueOff: 0,
12438     boxLabel: false,
12439     checked: false,
12440     
12441     getAutoCreate : function()
12442     {
12443         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12444         
12445         var id = Roo.id();
12446         
12447         var cfg = {};
12448         
12449         cfg.cls = 'form-group' //input-group
12450         
12451         var input =  {
12452             tag: 'input',
12453             id : id,
12454             type : this.inputType,
12455             value : (!this.checked) ? this.valueOff : this.inputValue,
12456             cls : 'form-box',
12457             placeholder : this.placeholder || ''
12458             
12459         };
12460         
12461         if (this.disabled) {
12462             input.disabled=true;
12463         }
12464         
12465         if(this.checked){
12466             input.checked = this.checked;
12467         }
12468         
12469         if (this.name) {
12470             input.name = this.name;
12471         }
12472         
12473         if (this.size) {
12474             input.cls += ' input-' + this.size;
12475         }
12476         
12477         var settings=this;
12478         ['xs','sm','md','lg'].map(function(size){
12479             if (settings[size]) {
12480                 cfg.cls += ' col-' + size + '-' + settings[size];
12481             }
12482         });
12483         
12484         var inputblock = input;
12485         
12486         if (this.before || this.after) {
12487             
12488             inputblock = {
12489                 cls : 'input-group',
12490                 cn :  [] 
12491             };
12492             if (this.before) {
12493                 inputblock.cn.push({
12494                     tag :'span',
12495                     cls : 'input-group-addon',
12496                     html : this.before
12497                 });
12498             }
12499             inputblock.cn.push(input);
12500             if (this.after) {
12501                 inputblock.cn.push({
12502                     tag :'span',
12503                     cls : 'input-group-addon',
12504                     html : this.after
12505                 });
12506             }
12507             
12508         };
12509         
12510         if (align ==='left' && this.fieldLabel.length) {
12511                 Roo.log("left and has label");
12512                 cfg.cn = [
12513                     
12514                     {
12515                         tag: 'label',
12516                         'for' :  id,
12517                         cls : 'control-label col-md-' + this.labelWidth,
12518                         html : this.fieldLabel
12519                         
12520                     },
12521                     {
12522                         cls : "col-md-" + (12 - this.labelWidth), 
12523                         cn: [
12524                             inputblock
12525                         ]
12526                     }
12527                     
12528                 ];
12529         } else if ( this.fieldLabel.length) {
12530                 Roo.log(" label");
12531                 cfg.cn = [
12532                    
12533                     {
12534                         tag: this.boxLabel ? 'span' : 'label',
12535                         'for': id,
12536                         cls: 'control-label box-input-label',
12537                         //cls : 'input-group-addon',
12538                         html : this.fieldLabel
12539                         
12540                     },
12541                     
12542                     inputblock
12543                     
12544                 ];
12545
12546         } else {
12547             
12548                    Roo.log(" no label && no align");
12549                 cfg.cn = [
12550                     
12551                         inputblock
12552                     
12553                 ];
12554                 
12555                 
12556         };
12557         
12558         if(this.boxLabel){
12559             cfg.cn.push({
12560                 tag: 'label',
12561                 'for': id,
12562                 cls: 'box-label',
12563                 html: this.boxLabel
12564             })
12565         }
12566         
12567         return cfg;
12568         
12569     },
12570     
12571     /**
12572      * return the real input element.
12573      */
12574     inputEl: function ()
12575     {
12576         return this.el.select('input.form-box',true).first();
12577     },
12578     
12579     initEvents : function()
12580     {
12581 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12582         
12583         this.inputEl().on('click', this.onClick,  this);
12584         
12585     },
12586     
12587     onClick : function()
12588     {   
12589         this.setChecked(!this.checked);
12590     },
12591     
12592     setChecked : function(state,suppressEvent)
12593     {
12594         this.checked = state;
12595         
12596         this.inputEl().dom.checked = state;
12597         
12598         if(suppressEvent !== true){
12599             this.fireEvent('check', this, state);
12600         }
12601         
12602         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12603         
12604     },
12605     
12606     setValue : function(v,suppressEvent)
12607     {
12608         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12609     }
12610     
12611 });
12612
12613  
12614 /*
12615  * - LGPL
12616  *
12617  * Radio
12618  * 
12619  */
12620
12621 /**
12622  * @class Roo.bootstrap.Radio
12623  * @extends Roo.bootstrap.CheckBox
12624  * Bootstrap Radio class
12625
12626  * @constructor
12627  * Create a new Radio
12628  * @param {Object} config The config object
12629  */
12630
12631 Roo.bootstrap.Radio = function(config){
12632     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12633    
12634 };
12635
12636 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12637     
12638     inputType: 'radio',
12639     inputValue: '',
12640     valueOff: '',
12641     
12642     getAutoCreate : function()
12643     {
12644         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12645         
12646         var id = Roo.id();
12647         
12648         var cfg = {};
12649         
12650         cfg.cls = 'form-group' //input-group
12651         
12652         var input =  {
12653             tag: 'input',
12654             id : id,
12655             type : this.inputType,
12656             value : (!this.checked) ? this.valueOff : this.inputValue,
12657             cls : 'form-box',
12658             placeholder : this.placeholder || ''
12659             
12660         };
12661         
12662         if (this.disabled) {
12663             input.disabled=true;
12664         }
12665         
12666         if(this.checked){
12667             input.checked = this.checked;
12668         }
12669         
12670         if (this.name) {
12671             input.name = this.name;
12672         }
12673         
12674         if (this.size) {
12675             input.cls += ' input-' + this.size;
12676         }
12677         
12678         var settings=this;
12679         ['xs','sm','md','lg'].map(function(size){
12680             if (settings[size]) {
12681                 cfg.cls += ' col-' + size + '-' + settings[size];
12682             }
12683         });
12684         
12685         var inputblock = input;
12686         
12687         if (this.before || this.after) {
12688             
12689             inputblock = {
12690                 cls : 'input-group',
12691                 cn :  [] 
12692             };
12693             if (this.before) {
12694                 inputblock.cn.push({
12695                     tag :'span',
12696                     cls : 'input-group-addon',
12697                     html : this.before
12698                 });
12699             }
12700             inputblock.cn.push(input);
12701             if (this.after) {
12702                 inputblock.cn.push({
12703                     tag :'span',
12704                     cls : 'input-group-addon',
12705                     html : this.after
12706                 });
12707             }
12708             
12709         };
12710         
12711         if (align ==='left' && this.fieldLabel.length) {
12712                 Roo.log("left and has label");
12713                 cfg.cn = [
12714                     
12715                     {
12716                         tag: 'label',
12717                         'for' :  id,
12718                         cls : 'control-label col-md-' + this.labelWidth,
12719                         html : this.fieldLabel
12720                         
12721                     },
12722                     {
12723                         cls : "col-md-" + (12 - this.labelWidth), 
12724                         cn: [
12725                             inputblock
12726                         ]
12727                     }
12728                     
12729                 ];
12730         } else if ( this.fieldLabel.length) {
12731                 Roo.log(" label");
12732                  cfg.cn = [
12733                    
12734                     {
12735                         tag: 'label',
12736                         'for': id,
12737                         cls: 'control-label box-input-label',
12738                         //cls : 'input-group-addon',
12739                         html : this.fieldLabel
12740                         
12741                     },
12742                     
12743                     inputblock
12744                     
12745                 ];
12746
12747         } else {
12748             
12749                    Roo.log(" no label && no align");
12750                 cfg.cn = [
12751                     
12752                         inputblock
12753                     
12754                 ];
12755                 
12756                 
12757         };
12758         
12759         if(this.boxLabel){
12760             cfg.cn.push({
12761                 tag: 'label',
12762                 'for': id,
12763                 cls: 'box-label',
12764                 html: this.boxLabel
12765             })
12766         }
12767         
12768         return cfg;
12769         
12770     },
12771    
12772     onClick : function()
12773     {   
12774         this.setChecked(true);
12775     },
12776     
12777     setChecked : function(state,suppressEvent)
12778     {
12779         if(state){
12780             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12781                 v.dom.checked = false;
12782             });
12783         }
12784         
12785         this.checked = state;
12786         this.inputEl().dom.checked = state;
12787         
12788         if(suppressEvent !== true){
12789             this.fireEvent('check', this, state);
12790         }
12791         
12792         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12793         
12794     },
12795     
12796     getGroupValue : function()
12797     {
12798         var value = ''
12799         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12800             if(v.dom.checked == true){
12801                 value = v.dom.value;
12802             }
12803         });
12804         
12805         return value;
12806     },
12807     
12808     /**
12809      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12810      * @return {Mixed} value The field value
12811      */
12812     getValue : function(){
12813         return this.getGroupValue();
12814     }
12815     
12816 });
12817
12818  
12819 //<script type="text/javascript">
12820
12821 /*
12822  * Based  Ext JS Library 1.1.1
12823  * Copyright(c) 2006-2007, Ext JS, LLC.
12824  * LGPL
12825  *
12826  */
12827  
12828 /**
12829  * @class Roo.HtmlEditorCore
12830  * @extends Roo.Component
12831  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12832  *
12833  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12834  */
12835
12836 Roo.HtmlEditorCore = function(config){
12837     
12838     
12839     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12840     this.addEvents({
12841         /**
12842          * @event initialize
12843          * Fires when the editor is fully initialized (including the iframe)
12844          * @param {Roo.HtmlEditorCore} this
12845          */
12846         initialize: true,
12847         /**
12848          * @event activate
12849          * Fires when the editor is first receives the focus. Any insertion must wait
12850          * until after this event.
12851          * @param {Roo.HtmlEditorCore} this
12852          */
12853         activate: true,
12854          /**
12855          * @event beforesync
12856          * Fires before the textarea is updated with content from the editor iframe. Return false
12857          * to cancel the sync.
12858          * @param {Roo.HtmlEditorCore} this
12859          * @param {String} html
12860          */
12861         beforesync: true,
12862          /**
12863          * @event beforepush
12864          * Fires before the iframe editor is updated with content from the textarea. Return false
12865          * to cancel the push.
12866          * @param {Roo.HtmlEditorCore} this
12867          * @param {String} html
12868          */
12869         beforepush: true,
12870          /**
12871          * @event sync
12872          * Fires when the textarea is updated with content from the editor iframe.
12873          * @param {Roo.HtmlEditorCore} this
12874          * @param {String} html
12875          */
12876         sync: true,
12877          /**
12878          * @event push
12879          * Fires when the iframe editor is updated with content from the textarea.
12880          * @param {Roo.HtmlEditorCore} this
12881          * @param {String} html
12882          */
12883         push: true,
12884         
12885         /**
12886          * @event editorevent
12887          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12888          * @param {Roo.HtmlEditorCore} this
12889          */
12890         editorevent: true
12891     });
12892      
12893 };
12894
12895
12896 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12897
12898
12899      /**
12900      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12901      */
12902     
12903     owner : false,
12904     
12905      /**
12906      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12907      *                        Roo.resizable.
12908      */
12909     resizable : false,
12910      /**
12911      * @cfg {Number} height (in pixels)
12912      */   
12913     height: 300,
12914    /**
12915      * @cfg {Number} width (in pixels)
12916      */   
12917     width: 500,
12918     
12919     /**
12920      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12921      * 
12922      */
12923     stylesheets: false,
12924     
12925     // id of frame..
12926     frameId: false,
12927     
12928     // private properties
12929     validationEvent : false,
12930     deferHeight: true,
12931     initialized : false,
12932     activated : false,
12933     sourceEditMode : false,
12934     onFocus : Roo.emptyFn,
12935     iframePad:3,
12936     hideMode:'offsets',
12937     
12938     clearUp: true,
12939     
12940      
12941     
12942
12943     /**
12944      * Protected method that will not generally be called directly. It
12945      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12946      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12947      */
12948     getDocMarkup : function(){
12949         // body styles..
12950         var st = '';
12951         Roo.log(this.stylesheets);
12952         
12953         // inherit styels from page...?? 
12954         if (this.stylesheets === false) {
12955             
12956             Roo.get(document.head).select('style').each(function(node) {
12957                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12958             });
12959             
12960             Roo.get(document.head).select('link').each(function(node) { 
12961                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12962             });
12963             
12964         } else if (!this.stylesheets.length) {
12965                 // simple..
12966                 st = '<style type="text/css">' +
12967                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12968                    '</style>';
12969         } else {
12970             Roo.each(this.stylesheets, function(s) {
12971                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12972             });
12973             
12974         }
12975         
12976         st +=  '<style type="text/css">' +
12977             'IMG { cursor: pointer } ' +
12978         '</style>';
12979
12980         
12981         return '<html><head>' + st  +
12982             //<style type="text/css">' +
12983             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12984             //'</style>' +
12985             ' </head><body class="roo-htmleditor-body"></body></html>';
12986     },
12987
12988     // private
12989     onRender : function(ct, position)
12990     {
12991         var _t = this;
12992         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12993         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12994         
12995         
12996         this.el.dom.style.border = '0 none';
12997         this.el.dom.setAttribute('tabIndex', -1);
12998         this.el.addClass('x-hidden hide');
12999         
13000         
13001         
13002         if(Roo.isIE){ // fix IE 1px bogus margin
13003             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13004         }
13005        
13006         
13007         this.frameId = Roo.id();
13008         
13009          
13010         
13011         var iframe = this.owner.wrap.createChild({
13012             tag: 'iframe',
13013             cls: 'form-control', // bootstrap..
13014             id: this.frameId,
13015             name: this.frameId,
13016             frameBorder : 'no',
13017             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13018         }, this.el
13019         );
13020         
13021         
13022         this.iframe = iframe.dom;
13023
13024          this.assignDocWin();
13025         
13026         this.doc.designMode = 'on';
13027        
13028         this.doc.open();
13029         this.doc.write(this.getDocMarkup());
13030         this.doc.close();
13031
13032         
13033         var task = { // must defer to wait for browser to be ready
13034             run : function(){
13035                 //console.log("run task?" + this.doc.readyState);
13036                 this.assignDocWin();
13037                 if(this.doc.body || this.doc.readyState == 'complete'){
13038                     try {
13039                         this.doc.designMode="on";
13040                     } catch (e) {
13041                         return;
13042                     }
13043                     Roo.TaskMgr.stop(task);
13044                     this.initEditor.defer(10, this);
13045                 }
13046             },
13047             interval : 10,
13048             duration: 10000,
13049             scope: this
13050         };
13051         Roo.TaskMgr.start(task);
13052
13053         
13054          
13055     },
13056
13057     // private
13058     onResize : function(w, h)
13059     {
13060          Roo.log('resize: ' +w + ',' + h );
13061         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13062         if(!this.iframe){
13063             return;
13064         }
13065         if(typeof w == 'number'){
13066             
13067             this.iframe.style.width = w + 'px';
13068         }
13069         if(typeof h == 'number'){
13070             
13071             this.iframe.style.height = h + 'px';
13072             if(this.doc){
13073                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13074             }
13075         }
13076         
13077     },
13078
13079     /**
13080      * Toggles the editor between standard and source edit mode.
13081      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13082      */
13083     toggleSourceEdit : function(sourceEditMode){
13084         
13085         this.sourceEditMode = sourceEditMode === true;
13086         
13087         if(this.sourceEditMode){
13088  
13089             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13090             
13091         }else{
13092             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13093             //this.iframe.className = '';
13094             this.deferFocus();
13095         }
13096         //this.setSize(this.owner.wrap.getSize());
13097         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13098     },
13099
13100     
13101   
13102
13103     /**
13104      * Protected method that will not generally be called directly. If you need/want
13105      * custom HTML cleanup, this is the method you should override.
13106      * @param {String} html The HTML to be cleaned
13107      * return {String} The cleaned HTML
13108      */
13109     cleanHtml : function(html){
13110         html = String(html);
13111         if(html.length > 5){
13112             if(Roo.isSafari){ // strip safari nonsense
13113                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13114             }
13115         }
13116         if(html == '&nbsp;'){
13117             html = '';
13118         }
13119         return html;
13120     },
13121
13122     /**
13123      * HTML Editor -> Textarea
13124      * Protected method that will not generally be called directly. Syncs the contents
13125      * of the editor iframe with the textarea.
13126      */
13127     syncValue : function(){
13128         if(this.initialized){
13129             var bd = (this.doc.body || this.doc.documentElement);
13130             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13131             var html = bd.innerHTML;
13132             if(Roo.isSafari){
13133                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13134                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13135                 if(m && m[1]){
13136                     html = '<div style="'+m[0]+'">' + html + '</div>';
13137                 }
13138             }
13139             html = this.cleanHtml(html);
13140             // fix up the special chars.. normaly like back quotes in word...
13141             // however we do not want to do this with chinese..
13142             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13143                 var cc = b.charCodeAt();
13144                 if (
13145                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13146                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13147                     (cc >= 0xf900 && cc < 0xfb00 )
13148                 ) {
13149                         return b;
13150                 }
13151                 return "&#"+cc+";" 
13152             });
13153             if(this.owner.fireEvent('beforesync', this, html) !== false){
13154                 this.el.dom.value = html;
13155                 this.owner.fireEvent('sync', this, html);
13156             }
13157         }
13158     },
13159
13160     /**
13161      * Protected method that will not generally be called directly. Pushes the value of the textarea
13162      * into the iframe editor.
13163      */
13164     pushValue : function(){
13165         if(this.initialized){
13166             var v = this.el.dom.value.trim();
13167             
13168 //            if(v.length < 1){
13169 //                v = '&#160;';
13170 //            }
13171             
13172             if(this.owner.fireEvent('beforepush', this, v) !== false){
13173                 var d = (this.doc.body || this.doc.documentElement);
13174                 d.innerHTML = v;
13175                 this.cleanUpPaste();
13176                 this.el.dom.value = d.innerHTML;
13177                 this.owner.fireEvent('push', this, v);
13178             }
13179         }
13180     },
13181
13182     // private
13183     deferFocus : function(){
13184         this.focus.defer(10, this);
13185     },
13186
13187     // doc'ed in Field
13188     focus : function(){
13189         if(this.win && !this.sourceEditMode){
13190             this.win.focus();
13191         }else{
13192             this.el.focus();
13193         }
13194     },
13195     
13196     assignDocWin: function()
13197     {
13198         var iframe = this.iframe;
13199         
13200          if(Roo.isIE){
13201             this.doc = iframe.contentWindow.document;
13202             this.win = iframe.contentWindow;
13203         } else {
13204             if (!Roo.get(this.frameId)) {
13205                 return;
13206             }
13207             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13208             this.win = Roo.get(this.frameId).dom.contentWindow;
13209         }
13210     },
13211     
13212     // private
13213     initEditor : function(){
13214         //console.log("INIT EDITOR");
13215         this.assignDocWin();
13216         
13217         
13218         
13219         this.doc.designMode="on";
13220         this.doc.open();
13221         this.doc.write(this.getDocMarkup());
13222         this.doc.close();
13223         
13224         var dbody = (this.doc.body || this.doc.documentElement);
13225         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13226         // this copies styles from the containing element into thsi one..
13227         // not sure why we need all of this..
13228         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13229         ss['background-attachment'] = 'fixed'; // w3c
13230         dbody.bgProperties = 'fixed'; // ie
13231         Roo.DomHelper.applyStyles(dbody, ss);
13232         Roo.EventManager.on(this.doc, {
13233             //'mousedown': this.onEditorEvent,
13234             'mouseup': this.onEditorEvent,
13235             'dblclick': this.onEditorEvent,
13236             'click': this.onEditorEvent,
13237             'keyup': this.onEditorEvent,
13238             buffer:100,
13239             scope: this
13240         });
13241         if(Roo.isGecko){
13242             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13243         }
13244         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13245             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13246         }
13247         this.initialized = true;
13248
13249         this.owner.fireEvent('initialize', this);
13250         this.pushValue();
13251     },
13252
13253     // private
13254     onDestroy : function(){
13255         
13256         
13257         
13258         if(this.rendered){
13259             
13260             //for (var i =0; i < this.toolbars.length;i++) {
13261             //    // fixme - ask toolbars for heights?
13262             //    this.toolbars[i].onDestroy();
13263            // }
13264             
13265             //this.wrap.dom.innerHTML = '';
13266             //this.wrap.remove();
13267         }
13268     },
13269
13270     // private
13271     onFirstFocus : function(){
13272         
13273         this.assignDocWin();
13274         
13275         
13276         this.activated = true;
13277          
13278     
13279         if(Roo.isGecko){ // prevent silly gecko errors
13280             this.win.focus();
13281             var s = this.win.getSelection();
13282             if(!s.focusNode || s.focusNode.nodeType != 3){
13283                 var r = s.getRangeAt(0);
13284                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13285                 r.collapse(true);
13286                 this.deferFocus();
13287             }
13288             try{
13289                 this.execCmd('useCSS', true);
13290                 this.execCmd('styleWithCSS', false);
13291             }catch(e){}
13292         }
13293         this.owner.fireEvent('activate', this);
13294     },
13295
13296     // private
13297     adjustFont: function(btn){
13298         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13299         //if(Roo.isSafari){ // safari
13300         //    adjust *= 2;
13301        // }
13302         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13303         if(Roo.isSafari){ // safari
13304             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13305             v =  (v < 10) ? 10 : v;
13306             v =  (v > 48) ? 48 : v;
13307             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13308             
13309         }
13310         
13311         
13312         v = Math.max(1, v+adjust);
13313         
13314         this.execCmd('FontSize', v  );
13315     },
13316
13317     onEditorEvent : function(e){
13318         this.owner.fireEvent('editorevent', this, e);
13319       //  this.updateToolbar();
13320         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13321     },
13322
13323     insertTag : function(tg)
13324     {
13325         // could be a bit smarter... -> wrap the current selected tRoo..
13326         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13327             
13328             range = this.createRange(this.getSelection());
13329             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13330             wrappingNode.appendChild(range.extractContents());
13331             range.insertNode(wrappingNode);
13332
13333             return;
13334             
13335             
13336             
13337         }
13338         this.execCmd("formatblock",   tg);
13339         
13340     },
13341     
13342     insertText : function(txt)
13343     {
13344         
13345         
13346         var range = this.createRange();
13347         range.deleteContents();
13348                //alert(Sender.getAttribute('label'));
13349                
13350         range.insertNode(this.doc.createTextNode(txt));
13351     } ,
13352     
13353      
13354
13355     /**
13356      * Executes a Midas editor command on the editor document and performs necessary focus and
13357      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13358      * @param {String} cmd The Midas command
13359      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13360      */
13361     relayCmd : function(cmd, value){
13362         this.win.focus();
13363         this.execCmd(cmd, value);
13364         this.owner.fireEvent('editorevent', this);
13365         //this.updateToolbar();
13366         this.owner.deferFocus();
13367     },
13368
13369     /**
13370      * Executes a Midas editor command directly on the editor document.
13371      * For visual commands, you should use {@link #relayCmd} instead.
13372      * <b>This should only be called after the editor is initialized.</b>
13373      * @param {String} cmd The Midas command
13374      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13375      */
13376     execCmd : function(cmd, value){
13377         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13378         this.syncValue();
13379     },
13380  
13381  
13382    
13383     /**
13384      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13385      * to insert tRoo.
13386      * @param {String} text | dom node.. 
13387      */
13388     insertAtCursor : function(text)
13389     {
13390         
13391         
13392         
13393         if(!this.activated){
13394             return;
13395         }
13396         /*
13397         if(Roo.isIE){
13398             this.win.focus();
13399             var r = this.doc.selection.createRange();
13400             if(r){
13401                 r.collapse(true);
13402                 r.pasteHTML(text);
13403                 this.syncValue();
13404                 this.deferFocus();
13405             
13406             }
13407             return;
13408         }
13409         */
13410         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13411             this.win.focus();
13412             
13413             
13414             // from jquery ui (MIT licenced)
13415             var range, node;
13416             var win = this.win;
13417             
13418             if (win.getSelection && win.getSelection().getRangeAt) {
13419                 range = win.getSelection().getRangeAt(0);
13420                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13421                 range.insertNode(node);
13422             } else if (win.document.selection && win.document.selection.createRange) {
13423                 // no firefox support
13424                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13425                 win.document.selection.createRange().pasteHTML(txt);
13426             } else {
13427                 // no firefox support
13428                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13429                 this.execCmd('InsertHTML', txt);
13430             } 
13431             
13432             this.syncValue();
13433             
13434             this.deferFocus();
13435         }
13436     },
13437  // private
13438     mozKeyPress : function(e){
13439         if(e.ctrlKey){
13440             var c = e.getCharCode(), cmd;
13441           
13442             if(c > 0){
13443                 c = String.fromCharCode(c).toLowerCase();
13444                 switch(c){
13445                     case 'b':
13446                         cmd = 'bold';
13447                         break;
13448                     case 'i':
13449                         cmd = 'italic';
13450                         break;
13451                     
13452                     case 'u':
13453                         cmd = 'underline';
13454                         break;
13455                     
13456                     case 'v':
13457                         this.cleanUpPaste.defer(100, this);
13458                         return;
13459                         
13460                 }
13461                 if(cmd){
13462                     this.win.focus();
13463                     this.execCmd(cmd);
13464                     this.deferFocus();
13465                     e.preventDefault();
13466                 }
13467                 
13468             }
13469         }
13470     },
13471
13472     // private
13473     fixKeys : function(){ // load time branching for fastest keydown performance
13474         if(Roo.isIE){
13475             return function(e){
13476                 var k = e.getKey(), r;
13477                 if(k == e.TAB){
13478                     e.stopEvent();
13479                     r = this.doc.selection.createRange();
13480                     if(r){
13481                         r.collapse(true);
13482                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13483                         this.deferFocus();
13484                     }
13485                     return;
13486                 }
13487                 
13488                 if(k == e.ENTER){
13489                     r = this.doc.selection.createRange();
13490                     if(r){
13491                         var target = r.parentElement();
13492                         if(!target || target.tagName.toLowerCase() != 'li'){
13493                             e.stopEvent();
13494                             r.pasteHTML('<br />');
13495                             r.collapse(false);
13496                             r.select();
13497                         }
13498                     }
13499                 }
13500                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13501                     this.cleanUpPaste.defer(100, this);
13502                     return;
13503                 }
13504                 
13505                 
13506             };
13507         }else if(Roo.isOpera){
13508             return function(e){
13509                 var k = e.getKey();
13510                 if(k == e.TAB){
13511                     e.stopEvent();
13512                     this.win.focus();
13513                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13514                     this.deferFocus();
13515                 }
13516                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13517                     this.cleanUpPaste.defer(100, this);
13518                     return;
13519                 }
13520                 
13521             };
13522         }else if(Roo.isSafari){
13523             return function(e){
13524                 var k = e.getKey();
13525                 
13526                 if(k == e.TAB){
13527                     e.stopEvent();
13528                     this.execCmd('InsertText','\t');
13529                     this.deferFocus();
13530                     return;
13531                 }
13532                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13533                     this.cleanUpPaste.defer(100, this);
13534                     return;
13535                 }
13536                 
13537              };
13538         }
13539     }(),
13540     
13541     getAllAncestors: function()
13542     {
13543         var p = this.getSelectedNode();
13544         var a = [];
13545         if (!p) {
13546             a.push(p); // push blank onto stack..
13547             p = this.getParentElement();
13548         }
13549         
13550         
13551         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13552             a.push(p);
13553             p = p.parentNode;
13554         }
13555         a.push(this.doc.body);
13556         return a;
13557     },
13558     lastSel : false,
13559     lastSelNode : false,
13560     
13561     
13562     getSelection : function() 
13563     {
13564         this.assignDocWin();
13565         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13566     },
13567     
13568     getSelectedNode: function() 
13569     {
13570         // this may only work on Gecko!!!
13571         
13572         // should we cache this!!!!
13573         
13574         
13575         
13576          
13577         var range = this.createRange(this.getSelection()).cloneRange();
13578         
13579         if (Roo.isIE) {
13580             var parent = range.parentElement();
13581             while (true) {
13582                 var testRange = range.duplicate();
13583                 testRange.moveToElementText(parent);
13584                 if (testRange.inRange(range)) {
13585                     break;
13586                 }
13587                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13588                     break;
13589                 }
13590                 parent = parent.parentElement;
13591             }
13592             return parent;
13593         }
13594         
13595         // is ancestor a text element.
13596         var ac =  range.commonAncestorContainer;
13597         if (ac.nodeType == 3) {
13598             ac = ac.parentNode;
13599         }
13600         
13601         var ar = ac.childNodes;
13602          
13603         var nodes = [];
13604         var other_nodes = [];
13605         var has_other_nodes = false;
13606         for (var i=0;i<ar.length;i++) {
13607             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13608                 continue;
13609             }
13610             // fullly contained node.
13611             
13612             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13613                 nodes.push(ar[i]);
13614                 continue;
13615             }
13616             
13617             // probably selected..
13618             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13619                 other_nodes.push(ar[i]);
13620                 continue;
13621             }
13622             // outer..
13623             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13624                 continue;
13625             }
13626             
13627             
13628             has_other_nodes = true;
13629         }
13630         if (!nodes.length && other_nodes.length) {
13631             nodes= other_nodes;
13632         }
13633         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13634             return false;
13635         }
13636         
13637         return nodes[0];
13638     },
13639     createRange: function(sel)
13640     {
13641         // this has strange effects when using with 
13642         // top toolbar - not sure if it's a great idea.
13643         //this.editor.contentWindow.focus();
13644         if (typeof sel != "undefined") {
13645             try {
13646                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13647             } catch(e) {
13648                 return this.doc.createRange();
13649             }
13650         } else {
13651             return this.doc.createRange();
13652         }
13653     },
13654     getParentElement: function()
13655     {
13656         
13657         this.assignDocWin();
13658         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13659         
13660         var range = this.createRange(sel);
13661          
13662         try {
13663             var p = range.commonAncestorContainer;
13664             while (p.nodeType == 3) { // text node
13665                 p = p.parentNode;
13666             }
13667             return p;
13668         } catch (e) {
13669             return null;
13670         }
13671     
13672     },
13673     /***
13674      *
13675      * Range intersection.. the hard stuff...
13676      *  '-1' = before
13677      *  '0' = hits..
13678      *  '1' = after.
13679      *         [ -- selected range --- ]
13680      *   [fail]                        [fail]
13681      *
13682      *    basically..
13683      *      if end is before start or  hits it. fail.
13684      *      if start is after end or hits it fail.
13685      *
13686      *   if either hits (but other is outside. - then it's not 
13687      *   
13688      *    
13689      **/
13690     
13691     
13692     // @see http://www.thismuchiknow.co.uk/?p=64.
13693     rangeIntersectsNode : function(range, node)
13694     {
13695         var nodeRange = node.ownerDocument.createRange();
13696         try {
13697             nodeRange.selectNode(node);
13698         } catch (e) {
13699             nodeRange.selectNodeContents(node);
13700         }
13701     
13702         var rangeStartRange = range.cloneRange();
13703         rangeStartRange.collapse(true);
13704     
13705         var rangeEndRange = range.cloneRange();
13706         rangeEndRange.collapse(false);
13707     
13708         var nodeStartRange = nodeRange.cloneRange();
13709         nodeStartRange.collapse(true);
13710     
13711         var nodeEndRange = nodeRange.cloneRange();
13712         nodeEndRange.collapse(false);
13713     
13714         return rangeStartRange.compareBoundaryPoints(
13715                  Range.START_TO_START, nodeEndRange) == -1 &&
13716                rangeEndRange.compareBoundaryPoints(
13717                  Range.START_TO_START, nodeStartRange) == 1;
13718         
13719          
13720     },
13721     rangeCompareNode : function(range, node)
13722     {
13723         var nodeRange = node.ownerDocument.createRange();
13724         try {
13725             nodeRange.selectNode(node);
13726         } catch (e) {
13727             nodeRange.selectNodeContents(node);
13728         }
13729         
13730         
13731         range.collapse(true);
13732     
13733         nodeRange.collapse(true);
13734      
13735         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13736         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13737          
13738         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13739         
13740         var nodeIsBefore   =  ss == 1;
13741         var nodeIsAfter    = ee == -1;
13742         
13743         if (nodeIsBefore && nodeIsAfter)
13744             return 0; // outer
13745         if (!nodeIsBefore && nodeIsAfter)
13746             return 1; //right trailed.
13747         
13748         if (nodeIsBefore && !nodeIsAfter)
13749             return 2;  // left trailed.
13750         // fully contined.
13751         return 3;
13752     },
13753
13754     // private? - in a new class?
13755     cleanUpPaste :  function()
13756     {
13757         // cleans up the whole document..
13758         Roo.log('cleanuppaste');
13759         
13760         this.cleanUpChildren(this.doc.body);
13761         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13762         if (clean != this.doc.body.innerHTML) {
13763             this.doc.body.innerHTML = clean;
13764         }
13765         
13766     },
13767     
13768     cleanWordChars : function(input) {// change the chars to hex code
13769         var he = Roo.HtmlEditorCore;
13770         
13771         var output = input;
13772         Roo.each(he.swapCodes, function(sw) { 
13773             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13774             
13775             output = output.replace(swapper, sw[1]);
13776         });
13777         
13778         return output;
13779     },
13780     
13781     
13782     cleanUpChildren : function (n)
13783     {
13784         if (!n.childNodes.length) {
13785             return;
13786         }
13787         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13788            this.cleanUpChild(n.childNodes[i]);
13789         }
13790     },
13791     
13792     
13793         
13794     
13795     cleanUpChild : function (node)
13796     {
13797         var ed = this;
13798         //console.log(node);
13799         if (node.nodeName == "#text") {
13800             // clean up silly Windows -- stuff?
13801             return; 
13802         }
13803         if (node.nodeName == "#comment") {
13804             node.parentNode.removeChild(node);
13805             // clean up silly Windows -- stuff?
13806             return; 
13807         }
13808         
13809         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13810             // remove node.
13811             node.parentNode.removeChild(node);
13812             return;
13813             
13814         }
13815         
13816         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13817         
13818         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13819         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13820         
13821         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13822         //    remove_keep_children = true;
13823         //}
13824         
13825         if (remove_keep_children) {
13826             this.cleanUpChildren(node);
13827             // inserts everything just before this node...
13828             while (node.childNodes.length) {
13829                 var cn = node.childNodes[0];
13830                 node.removeChild(cn);
13831                 node.parentNode.insertBefore(cn, node);
13832             }
13833             node.parentNode.removeChild(node);
13834             return;
13835         }
13836         
13837         if (!node.attributes || !node.attributes.length) {
13838             this.cleanUpChildren(node);
13839             return;
13840         }
13841         
13842         function cleanAttr(n,v)
13843         {
13844             
13845             if (v.match(/^\./) || v.match(/^\//)) {
13846                 return;
13847             }
13848             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13849                 return;
13850             }
13851             if (v.match(/^#/)) {
13852                 return;
13853             }
13854 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13855             node.removeAttribute(n);
13856             
13857         }
13858         
13859         function cleanStyle(n,v)
13860         {
13861             if (v.match(/expression/)) { //XSS?? should we even bother..
13862                 node.removeAttribute(n);
13863                 return;
13864             }
13865             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13866             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13867             
13868             
13869             var parts = v.split(/;/);
13870             var clean = [];
13871             
13872             Roo.each(parts, function(p) {
13873                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13874                 if (!p.length) {
13875                     return true;
13876                 }
13877                 var l = p.split(':').shift().replace(/\s+/g,'');
13878                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13879                 
13880                 if ( cblack.indexOf(l) > -1) {
13881 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13882                     //node.removeAttribute(n);
13883                     return true;
13884                 }
13885                 //Roo.log()
13886                 // only allow 'c whitelisted system attributes'
13887                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13888 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13889                     //node.removeAttribute(n);
13890                     return true;
13891                 }
13892                 
13893                 
13894                  
13895                 
13896                 clean.push(p);
13897                 return true;
13898             });
13899             if (clean.length) { 
13900                 node.setAttribute(n, clean.join(';'));
13901             } else {
13902                 node.removeAttribute(n);
13903             }
13904             
13905         }
13906         
13907         
13908         for (var i = node.attributes.length-1; i > -1 ; i--) {
13909             var a = node.attributes[i];
13910             //console.log(a);
13911             
13912             if (a.name.toLowerCase().substr(0,2)=='on')  {
13913                 node.removeAttribute(a.name);
13914                 continue;
13915             }
13916             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13917                 node.removeAttribute(a.name);
13918                 continue;
13919             }
13920             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13921                 cleanAttr(a.name,a.value); // fixme..
13922                 continue;
13923             }
13924             if (a.name == 'style') {
13925                 cleanStyle(a.name,a.value);
13926                 continue;
13927             }
13928             /// clean up MS crap..
13929             // tecnically this should be a list of valid class'es..
13930             
13931             
13932             if (a.name == 'class') {
13933                 if (a.value.match(/^Mso/)) {
13934                     node.className = '';
13935                 }
13936                 
13937                 if (a.value.match(/body/)) {
13938                     node.className = '';
13939                 }
13940                 continue;
13941             }
13942             
13943             // style cleanup!?
13944             // class cleanup?
13945             
13946         }
13947         
13948         
13949         this.cleanUpChildren(node);
13950         
13951         
13952     }
13953     
13954     
13955     // hide stuff that is not compatible
13956     /**
13957      * @event blur
13958      * @hide
13959      */
13960     /**
13961      * @event change
13962      * @hide
13963      */
13964     /**
13965      * @event focus
13966      * @hide
13967      */
13968     /**
13969      * @event specialkey
13970      * @hide
13971      */
13972     /**
13973      * @cfg {String} fieldClass @hide
13974      */
13975     /**
13976      * @cfg {String} focusClass @hide
13977      */
13978     /**
13979      * @cfg {String} autoCreate @hide
13980      */
13981     /**
13982      * @cfg {String} inputType @hide
13983      */
13984     /**
13985      * @cfg {String} invalidClass @hide
13986      */
13987     /**
13988      * @cfg {String} invalidText @hide
13989      */
13990     /**
13991      * @cfg {String} msgFx @hide
13992      */
13993     /**
13994      * @cfg {String} validateOnBlur @hide
13995      */
13996 });
13997
13998 Roo.HtmlEditorCore.white = [
13999         'area', 'br', 'img', 'input', 'hr', 'wbr',
14000         
14001        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14002        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14003        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14004        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14005        'table',   'ul',         'xmp', 
14006        
14007        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14008       'thead',   'tr', 
14009      
14010       'dir', 'menu', 'ol', 'ul', 'dl',
14011        
14012       'embed',  'object'
14013 ];
14014
14015
14016 Roo.HtmlEditorCore.black = [
14017     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14018         'applet', // 
14019         'base',   'basefont', 'bgsound', 'blink',  'body', 
14020         'frame',  'frameset', 'head',    'html',   'ilayer', 
14021         'iframe', 'layer',  'link',     'meta',    'object',   
14022         'script', 'style' ,'title',  'xml' // clean later..
14023 ];
14024 Roo.HtmlEditorCore.clean = [
14025     'script', 'style', 'title', 'xml'
14026 ];
14027 Roo.HtmlEditorCore.remove = [
14028     'font'
14029 ];
14030 // attributes..
14031
14032 Roo.HtmlEditorCore.ablack = [
14033     'on'
14034 ];
14035     
14036 Roo.HtmlEditorCore.aclean = [ 
14037     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14038 ];
14039
14040 // protocols..
14041 Roo.HtmlEditorCore.pwhite= [
14042         'http',  'https',  'mailto'
14043 ];
14044
14045 // white listed style attributes.
14046 Roo.HtmlEditorCore.cwhite= [
14047       //  'text-align', /// default is to allow most things..
14048       
14049          
14050 //        'font-size'//??
14051 ];
14052
14053 // black listed style attributes.
14054 Roo.HtmlEditorCore.cblack= [
14055       //  'font-size' -- this can be set by the project 
14056 ];
14057
14058
14059 Roo.HtmlEditorCore.swapCodes   =[ 
14060     [    8211, "--" ], 
14061     [    8212, "--" ], 
14062     [    8216,  "'" ],  
14063     [    8217, "'" ],  
14064     [    8220, '"' ],  
14065     [    8221, '"' ],  
14066     [    8226, "*" ],  
14067     [    8230, "..." ]
14068 ]; 
14069
14070     /*
14071  * - LGPL
14072  *
14073  * HtmlEditor
14074  * 
14075  */
14076
14077 /**
14078  * @class Roo.bootstrap.HtmlEditor
14079  * @extends Roo.bootstrap.TextArea
14080  * Bootstrap HtmlEditor class
14081
14082  * @constructor
14083  * Create a new HtmlEditor
14084  * @param {Object} config The config object
14085  */
14086
14087 Roo.bootstrap.HtmlEditor = function(config){
14088     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14089     if (!this.toolbars) {
14090         this.toolbars = [];
14091     }
14092     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14093     this.addEvents({
14094             /**
14095              * @event initialize
14096              * Fires when the editor is fully initialized (including the iframe)
14097              * @param {HtmlEditor} this
14098              */
14099             initialize: true,
14100             /**
14101              * @event activate
14102              * Fires when the editor is first receives the focus. Any insertion must wait
14103              * until after this event.
14104              * @param {HtmlEditor} this
14105              */
14106             activate: true,
14107              /**
14108              * @event beforesync
14109              * Fires before the textarea is updated with content from the editor iframe. Return false
14110              * to cancel the sync.
14111              * @param {HtmlEditor} this
14112              * @param {String} html
14113              */
14114             beforesync: true,
14115              /**
14116              * @event beforepush
14117              * Fires before the iframe editor is updated with content from the textarea. Return false
14118              * to cancel the push.
14119              * @param {HtmlEditor} this
14120              * @param {String} html
14121              */
14122             beforepush: true,
14123              /**
14124              * @event sync
14125              * Fires when the textarea is updated with content from the editor iframe.
14126              * @param {HtmlEditor} this
14127              * @param {String} html
14128              */
14129             sync: true,
14130              /**
14131              * @event push
14132              * Fires when the iframe editor is updated with content from the textarea.
14133              * @param {HtmlEditor} this
14134              * @param {String} html
14135              */
14136             push: true,
14137              /**
14138              * @event editmodechange
14139              * Fires when the editor switches edit modes
14140              * @param {HtmlEditor} this
14141              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14142              */
14143             editmodechange: true,
14144             /**
14145              * @event editorevent
14146              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14147              * @param {HtmlEditor} this
14148              */
14149             editorevent: true,
14150             /**
14151              * @event firstfocus
14152              * Fires when on first focus - needed by toolbars..
14153              * @param {HtmlEditor} this
14154              */
14155             firstfocus: true,
14156             /**
14157              * @event autosave
14158              * Auto save the htmlEditor value as a file into Events
14159              * @param {HtmlEditor} this
14160              */
14161             autosave: true,
14162             /**
14163              * @event savedpreview
14164              * preview the saved version of htmlEditor
14165              * @param {HtmlEditor} this
14166              */
14167             savedpreview: true
14168         });
14169 };
14170
14171
14172 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14173     
14174     
14175       /**
14176      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14177      */
14178     toolbars : false,
14179    
14180      /**
14181      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14182      *                        Roo.resizable.
14183      */
14184     resizable : false,
14185      /**
14186      * @cfg {Number} height (in pixels)
14187      */   
14188     height: 300,
14189    /**
14190      * @cfg {Number} width (in pixels)
14191      */   
14192     width: false,
14193     
14194     /**
14195      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14196      * 
14197      */
14198     stylesheets: false,
14199     
14200     // id of frame..
14201     frameId: false,
14202     
14203     // private properties
14204     validationEvent : false,
14205     deferHeight: true,
14206     initialized : false,
14207     activated : false,
14208     
14209     onFocus : Roo.emptyFn,
14210     iframePad:3,
14211     hideMode:'offsets',
14212     
14213     
14214     tbContainer : false,
14215     
14216     toolbarContainer :function() {
14217         return this.wrap.select('.x-html-editor-tb',true).first();
14218     },
14219
14220     /**
14221      * Protected method that will not generally be called directly. It
14222      * is called when the editor creates its toolbar. Override this method if you need to
14223      * add custom toolbar buttons.
14224      * @param {HtmlEditor} editor
14225      */
14226     createToolbar : function(){
14227         
14228         Roo.log("create toolbars");
14229         
14230         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14231         this.toolbars[0].render(this.toolbarContainer());
14232         
14233         return;
14234         
14235 //        if (!editor.toolbars || !editor.toolbars.length) {
14236 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14237 //        }
14238 //        
14239 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14240 //            editor.toolbars[i] = Roo.factory(
14241 //                    typeof(editor.toolbars[i]) == 'string' ?
14242 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14243 //                Roo.bootstrap.HtmlEditor);
14244 //            editor.toolbars[i].init(editor);
14245 //        }
14246     },
14247
14248      
14249     // private
14250     onRender : function(ct, position)
14251     {
14252        // Roo.log("Call onRender: " + this.xtype);
14253         var _t = this;
14254         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14255       
14256         this.wrap = this.inputEl().wrap({
14257             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14258         });
14259         
14260         this.editorcore.onRender(ct, position);
14261          
14262         if (this.resizable) {
14263             this.resizeEl = new Roo.Resizable(this.wrap, {
14264                 pinned : true,
14265                 wrap: true,
14266                 dynamic : true,
14267                 minHeight : this.height,
14268                 height: this.height,
14269                 handles : this.resizable,
14270                 width: this.width,
14271                 listeners : {
14272                     resize : function(r, w, h) {
14273                         _t.onResize(w,h); // -something
14274                     }
14275                 }
14276             });
14277             
14278         }
14279         this.createToolbar(this);
14280        
14281         
14282         if(!this.width && this.resizable){
14283             this.setSize(this.wrap.getSize());
14284         }
14285         if (this.resizeEl) {
14286             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14287             // should trigger onReize..
14288         }
14289         
14290     },
14291
14292     // private
14293     onResize : function(w, h)
14294     {
14295         Roo.log('resize: ' +w + ',' + h );
14296         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14297         var ew = false;
14298         var eh = false;
14299         
14300         if(this.inputEl() ){
14301             if(typeof w == 'number'){
14302                 var aw = w - this.wrap.getFrameWidth('lr');
14303                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14304                 ew = aw;
14305             }
14306             if(typeof h == 'number'){
14307                  var tbh = -11;  // fixme it needs to tool bar size!
14308                 for (var i =0; i < this.toolbars.length;i++) {
14309                     // fixme - ask toolbars for heights?
14310                     tbh += this.toolbars[i].el.getHeight();
14311                     //if (this.toolbars[i].footer) {
14312                     //    tbh += this.toolbars[i].footer.el.getHeight();
14313                     //}
14314                 }
14315               
14316                 
14317                 
14318                 
14319                 
14320                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14321                 ah -= 5; // knock a few pixes off for look..
14322                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14323                 var eh = ah;
14324             }
14325         }
14326         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14327         this.editorcore.onResize(ew,eh);
14328         
14329     },
14330
14331     /**
14332      * Toggles the editor between standard and source edit mode.
14333      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14334      */
14335     toggleSourceEdit : function(sourceEditMode)
14336     {
14337         this.editorcore.toggleSourceEdit(sourceEditMode);
14338         
14339         if(this.editorcore.sourceEditMode){
14340             Roo.log('editor - showing textarea');
14341             
14342 //            Roo.log('in');
14343 //            Roo.log(this.syncValue());
14344             this.syncValue();
14345             this.inputEl().removeClass('hide');
14346             this.inputEl().dom.removeAttribute('tabIndex');
14347             this.inputEl().focus();
14348         }else{
14349             Roo.log('editor - hiding textarea');
14350 //            Roo.log('out')
14351 //            Roo.log(this.pushValue()); 
14352             this.pushValue();
14353             
14354             this.inputEl().addClass('hide');
14355             this.inputEl().dom.setAttribute('tabIndex', -1);
14356             //this.deferFocus();
14357         }
14358          
14359         if(this.resizable){
14360             this.setSize(this.wrap.getSize());
14361         }
14362         
14363         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14364     },
14365  
14366     // private (for BoxComponent)
14367     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14368
14369     // private (for BoxComponent)
14370     getResizeEl : function(){
14371         return this.wrap;
14372     },
14373
14374     // private (for BoxComponent)
14375     getPositionEl : function(){
14376         return this.wrap;
14377     },
14378
14379     // private
14380     initEvents : function(){
14381         this.originalValue = this.getValue();
14382     },
14383
14384 //    /**
14385 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14386 //     * @method
14387 //     */
14388 //    markInvalid : Roo.emptyFn,
14389 //    /**
14390 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14391 //     * @method
14392 //     */
14393 //    clearInvalid : Roo.emptyFn,
14394
14395     setValue : function(v){
14396         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14397         this.editorcore.pushValue();
14398     },
14399
14400      
14401     // private
14402     deferFocus : function(){
14403         this.focus.defer(10, this);
14404     },
14405
14406     // doc'ed in Field
14407     focus : function(){
14408         this.editorcore.focus();
14409         
14410     },
14411       
14412
14413     // private
14414     onDestroy : function(){
14415         
14416         
14417         
14418         if(this.rendered){
14419             
14420             for (var i =0; i < this.toolbars.length;i++) {
14421                 // fixme - ask toolbars for heights?
14422                 this.toolbars[i].onDestroy();
14423             }
14424             
14425             this.wrap.dom.innerHTML = '';
14426             this.wrap.remove();
14427         }
14428     },
14429
14430     // private
14431     onFirstFocus : function(){
14432         //Roo.log("onFirstFocus");
14433         this.editorcore.onFirstFocus();
14434          for (var i =0; i < this.toolbars.length;i++) {
14435             this.toolbars[i].onFirstFocus();
14436         }
14437         
14438     },
14439     
14440     // private
14441     syncValue : function()
14442     {   
14443         this.editorcore.syncValue();
14444     },
14445     
14446     pushValue : function()
14447     {   
14448         this.editorcore.pushValue();
14449     }
14450      
14451     
14452     // hide stuff that is not compatible
14453     /**
14454      * @event blur
14455      * @hide
14456      */
14457     /**
14458      * @event change
14459      * @hide
14460      */
14461     /**
14462      * @event focus
14463      * @hide
14464      */
14465     /**
14466      * @event specialkey
14467      * @hide
14468      */
14469     /**
14470      * @cfg {String} fieldClass @hide
14471      */
14472     /**
14473      * @cfg {String} focusClass @hide
14474      */
14475     /**
14476      * @cfg {String} autoCreate @hide
14477      */
14478     /**
14479      * @cfg {String} inputType @hide
14480      */
14481     /**
14482      * @cfg {String} invalidClass @hide
14483      */
14484     /**
14485      * @cfg {String} invalidText @hide
14486      */
14487     /**
14488      * @cfg {String} msgFx @hide
14489      */
14490     /**
14491      * @cfg {String} validateOnBlur @hide
14492      */
14493 });
14494  
14495     
14496    
14497    
14498    
14499       
14500
14501 /**
14502  * @class Roo.bootstrap.HtmlEditorToolbar1
14503  * Basic Toolbar
14504  * 
14505  * Usage:
14506  *
14507  new Roo.bootstrap.HtmlEditor({
14508     ....
14509     toolbars : [
14510         new Roo.bootstrap.HtmlEditorToolbar1({
14511             disable : { fonts: 1 , format: 1, ..., ... , ...],
14512             btns : [ .... ]
14513         })
14514     }
14515      
14516  * 
14517  * @cfg {Object} disable List of elements to disable..
14518  * @cfg {Array} btns List of additional buttons.
14519  * 
14520  * 
14521  * NEEDS Extra CSS? 
14522  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14523  */
14524  
14525 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14526 {
14527     
14528     Roo.apply(this, config);
14529     
14530     // default disabled, based on 'good practice'..
14531     this.disable = this.disable || {};
14532     Roo.applyIf(this.disable, {
14533         fontSize : true,
14534         colors : true,
14535         specialElements : true
14536     });
14537     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14538     
14539     this.editor = config.editor;
14540     this.editorcore = config.editor.editorcore;
14541     
14542     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14543     
14544     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14545     // dont call parent... till later.
14546 }
14547 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14548     
14549     
14550     bar : true,
14551     
14552     editor : false,
14553     editorcore : false,
14554     
14555     
14556     formats : [
14557         "p" ,  
14558         "h1","h2","h3","h4","h5","h6", 
14559         "pre", "code", 
14560         "abbr", "acronym", "address", "cite", "samp", "var",
14561         'div','span'
14562     ],
14563     
14564     onRender : function(ct, position)
14565     {
14566        // Roo.log("Call onRender: " + this.xtype);
14567         
14568        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14569        Roo.log(this.el);
14570        this.el.dom.style.marginBottom = '0';
14571        var _this = this;
14572        var editorcore = this.editorcore;
14573        var editor= this.editor;
14574        
14575        var children = [];
14576        var btn = function(id,cmd , toggle, handler){
14577        
14578             var  event = toggle ? 'toggle' : 'click';
14579        
14580             var a = {
14581                 size : 'sm',
14582                 xtype: 'Button',
14583                 xns: Roo.bootstrap,
14584                 glyphicon : id,
14585                 cmd : id || cmd,
14586                 enableToggle:toggle !== false,
14587                 //html : 'submit'
14588                 pressed : toggle ? false : null,
14589                 listeners : {}
14590             }
14591             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14592                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14593             }
14594             children.push(a);
14595             return a;
14596        }
14597         
14598         var style = {
14599                 xtype: 'Button',
14600                 size : 'sm',
14601                 xns: Roo.bootstrap,
14602                 glyphicon : 'font',
14603                 //html : 'submit'
14604                 menu : {
14605                     xtype: 'Menu',
14606                     xns: Roo.bootstrap,
14607                     items:  []
14608                 }
14609         };
14610         Roo.each(this.formats, function(f) {
14611             style.menu.items.push({
14612                 xtype :'MenuItem',
14613                 xns: Roo.bootstrap,
14614                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14615                 tagname : f,
14616                 listeners : {
14617                     click : function()
14618                     {
14619                         editorcore.insertTag(this.tagname);
14620                         editor.focus();
14621                     }
14622                 }
14623                 
14624             });
14625         });
14626          children.push(style);   
14627             
14628             
14629         btn('bold',false,true);
14630         btn('italic',false,true);
14631         btn('align-left', 'justifyleft',true);
14632         btn('align-center', 'justifycenter',true);
14633         btn('align-right' , 'justifyright',true);
14634         btn('link', false, false, function(btn) {
14635             //Roo.log("create link?");
14636             var url = prompt(this.createLinkText, this.defaultLinkValue);
14637             if(url && url != 'http:/'+'/'){
14638                 this.editorcore.relayCmd('createlink', url);
14639             }
14640         }),
14641         btn('list','insertunorderedlist',true);
14642         btn('pencil', false,true, function(btn){
14643                 Roo.log(this);
14644                 
14645                 this.toggleSourceEdit(btn.pressed);
14646         });
14647         /*
14648         var cog = {
14649                 xtype: 'Button',
14650                 size : 'sm',
14651                 xns: Roo.bootstrap,
14652                 glyphicon : 'cog',
14653                 //html : 'submit'
14654                 menu : {
14655                     xtype: 'Menu',
14656                     xns: Roo.bootstrap,
14657                     items:  []
14658                 }
14659         };
14660         
14661         cog.menu.items.push({
14662             xtype :'MenuItem',
14663             xns: Roo.bootstrap,
14664             html : Clean styles,
14665             tagname : f,
14666             listeners : {
14667                 click : function()
14668                 {
14669                     editorcore.insertTag(this.tagname);
14670                     editor.focus();
14671                 }
14672             }
14673             
14674         });
14675        */
14676         
14677          
14678        this.xtype = 'Navbar';
14679         
14680         for(var i=0;i< children.length;i++) {
14681             
14682             this.buttons.add(this.addxtypeChild(children[i]));
14683             
14684         }
14685         
14686         editor.on('editorevent', this.updateToolbar, this);
14687     },
14688     onBtnClick : function(id)
14689     {
14690        this.editorcore.relayCmd(id);
14691        this.editorcore.focus();
14692     },
14693     
14694     /**
14695      * Protected method that will not generally be called directly. It triggers
14696      * a toolbar update by reading the markup state of the current selection in the editor.
14697      */
14698     updateToolbar: function(){
14699
14700         if(!this.editorcore.activated){
14701             this.editor.onFirstFocus(); // is this neeed?
14702             return;
14703         }
14704
14705         var btns = this.buttons; 
14706         var doc = this.editorcore.doc;
14707         btns.get('bold').setActive(doc.queryCommandState('bold'));
14708         btns.get('italic').setActive(doc.queryCommandState('italic'));
14709         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14710         
14711         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14712         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14713         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14714         
14715         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14716         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14717          /*
14718         
14719         var ans = this.editorcore.getAllAncestors();
14720         if (this.formatCombo) {
14721             
14722             
14723             var store = this.formatCombo.store;
14724             this.formatCombo.setValue("");
14725             for (var i =0; i < ans.length;i++) {
14726                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14727                     // select it..
14728                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14729                     break;
14730                 }
14731             }
14732         }
14733         
14734         
14735         
14736         // hides menus... - so this cant be on a menu...
14737         Roo.bootstrap.MenuMgr.hideAll();
14738         */
14739         Roo.bootstrap.MenuMgr.hideAll();
14740         //this.editorsyncValue();
14741     },
14742     onFirstFocus: function() {
14743         this.buttons.each(function(item){
14744            item.enable();
14745         });
14746     },
14747     toggleSourceEdit : function(sourceEditMode){
14748         
14749           
14750         if(sourceEditMode){
14751             Roo.log("disabling buttons");
14752            this.buttons.each( function(item){
14753                 if(item.cmd != 'pencil'){
14754                     item.disable();
14755                 }
14756             });
14757           
14758         }else{
14759             Roo.log("enabling buttons");
14760             if(this.editorcore.initialized){
14761                 this.buttons.each( function(item){
14762                     item.enable();
14763                 });
14764             }
14765             
14766         }
14767         Roo.log("calling toggole on editor");
14768         // tell the editor that it's been pressed..
14769         this.editor.toggleSourceEdit(sourceEditMode);
14770        
14771     }
14772 });
14773
14774
14775
14776
14777
14778 /**
14779  * @class Roo.bootstrap.Table.AbstractSelectionModel
14780  * @extends Roo.util.Observable
14781  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14782  * implemented by descendant classes.  This class should not be directly instantiated.
14783  * @constructor
14784  */
14785 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14786     this.locked = false;
14787     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14788 };
14789
14790
14791 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14792     /** @ignore Called by the grid automatically. Do not call directly. */
14793     init : function(grid){
14794         this.grid = grid;
14795         this.initEvents();
14796     },
14797
14798     /**
14799      * Locks the selections.
14800      */
14801     lock : function(){
14802         this.locked = true;
14803     },
14804
14805     /**
14806      * Unlocks the selections.
14807      */
14808     unlock : function(){
14809         this.locked = false;
14810     },
14811
14812     /**
14813      * Returns true if the selections are locked.
14814      * @return {Boolean}
14815      */
14816     isLocked : function(){
14817         return this.locked;
14818     }
14819 });
14820 /**
14821  * @class Roo.bootstrap.Table.ColumnModel
14822  * @extends Roo.util.Observable
14823  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14824  * the columns in the table.
14825  
14826  * @constructor
14827  * @param {Object} config An Array of column config objects. See this class's
14828  * config objects for details.
14829 */
14830 Roo.bootstrap.Table.ColumnModel = function(config){
14831         /**
14832      * The config passed into the constructor
14833      */
14834     this.config = config;
14835     this.lookup = {};
14836
14837     // if no id, create one
14838     // if the column does not have a dataIndex mapping,
14839     // map it to the order it is in the config
14840     for(var i = 0, len = config.length; i < len; i++){
14841         var c = config[i];
14842         if(typeof c.dataIndex == "undefined"){
14843             c.dataIndex = i;
14844         }
14845         if(typeof c.renderer == "string"){
14846             c.renderer = Roo.util.Format[c.renderer];
14847         }
14848         if(typeof c.id == "undefined"){
14849             c.id = Roo.id();
14850         }
14851 //        if(c.editor && c.editor.xtype){
14852 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14853 //        }
14854 //        if(c.editor && c.editor.isFormField){
14855 //            c.editor = new Roo.grid.GridEditor(c.editor);
14856 //        }
14857
14858         this.lookup[c.id] = c;
14859     }
14860
14861     /**
14862      * The width of columns which have no width specified (defaults to 100)
14863      * @type Number
14864      */
14865     this.defaultWidth = 100;
14866
14867     /**
14868      * Default sortable of columns which have no sortable specified (defaults to false)
14869      * @type Boolean
14870      */
14871     this.defaultSortable = false;
14872
14873     this.addEvents({
14874         /**
14875              * @event widthchange
14876              * Fires when the width of a column changes.
14877              * @param {ColumnModel} this
14878              * @param {Number} columnIndex The column index
14879              * @param {Number} newWidth The new width
14880              */
14881             "widthchange": true,
14882         /**
14883              * @event headerchange
14884              * Fires when the text of a header changes.
14885              * @param {ColumnModel} this
14886              * @param {Number} columnIndex The column index
14887              * @param {Number} newText The new header text
14888              */
14889             "headerchange": true,
14890         /**
14891              * @event hiddenchange
14892              * Fires when a column is hidden or "unhidden".
14893              * @param {ColumnModel} this
14894              * @param {Number} columnIndex The column index
14895              * @param {Boolean} hidden true if hidden, false otherwise
14896              */
14897             "hiddenchange": true,
14898             /**
14899          * @event columnmoved
14900          * Fires when a column is moved.
14901          * @param {ColumnModel} this
14902          * @param {Number} oldIndex
14903          * @param {Number} newIndex
14904          */
14905         "columnmoved" : true,
14906         /**
14907          * @event columlockchange
14908          * Fires when a column's locked state is changed
14909          * @param {ColumnModel} this
14910          * @param {Number} colIndex
14911          * @param {Boolean} locked true if locked
14912          */
14913         "columnlockchange" : true
14914     });
14915     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14916 };
14917 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14918     /**
14919      * @cfg {String} header The header text to display in the Grid view.
14920      */
14921     /**
14922      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14923      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14924      * specified, the column's index is used as an index into the Record's data Array.
14925      */
14926     /**
14927      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14928      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14929      */
14930     /**
14931      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14932      * Defaults to the value of the {@link #defaultSortable} property.
14933      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14934      */
14935     /**
14936      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14937      */
14938     /**
14939      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14940      */
14941     /**
14942      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14943      */
14944     /**
14945      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14946      */
14947     /**
14948      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14949      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14950      * default renderer uses the raw data value.
14951      */
14952     /**
14953      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14954      */
14955
14956     /**
14957      * Returns the id of the column at the specified index.
14958      * @param {Number} index The column index
14959      * @return {String} the id
14960      */
14961     getColumnId : function(index){
14962         return this.config[index].id;
14963     },
14964
14965     /**
14966      * Returns the column for a specified id.
14967      * @param {String} id The column id
14968      * @return {Object} the column
14969      */
14970     getColumnById : function(id){
14971         return this.lookup[id];
14972     },
14973
14974     
14975     /**
14976      * Returns the column for a specified dataIndex.
14977      * @param {String} dataIndex The column dataIndex
14978      * @return {Object|Boolean} the column or false if not found
14979      */
14980     getColumnByDataIndex: function(dataIndex){
14981         var index = this.findColumnIndex(dataIndex);
14982         return index > -1 ? this.config[index] : false;
14983     },
14984     
14985     /**
14986      * Returns the index for a specified column id.
14987      * @param {String} id The column id
14988      * @return {Number} the index, or -1 if not found
14989      */
14990     getIndexById : function(id){
14991         for(var i = 0, len = this.config.length; i < len; i++){
14992             if(this.config[i].id == id){
14993                 return i;
14994             }
14995         }
14996         return -1;
14997     },
14998     
14999     /**
15000      * Returns the index for a specified column dataIndex.
15001      * @param {String} dataIndex The column dataIndex
15002      * @return {Number} the index, or -1 if not found
15003      */
15004     
15005     findColumnIndex : function(dataIndex){
15006         for(var i = 0, len = this.config.length; i < len; i++){
15007             if(this.config[i].dataIndex == dataIndex){
15008                 return i;
15009             }
15010         }
15011         return -1;
15012     },
15013     
15014     
15015     moveColumn : function(oldIndex, newIndex){
15016         var c = this.config[oldIndex];
15017         this.config.splice(oldIndex, 1);
15018         this.config.splice(newIndex, 0, c);
15019         this.dataMap = null;
15020         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15021     },
15022
15023     isLocked : function(colIndex){
15024         return this.config[colIndex].locked === true;
15025     },
15026
15027     setLocked : function(colIndex, value, suppressEvent){
15028         if(this.isLocked(colIndex) == value){
15029             return;
15030         }
15031         this.config[colIndex].locked = value;
15032         if(!suppressEvent){
15033             this.fireEvent("columnlockchange", this, colIndex, value);
15034         }
15035     },
15036
15037     getTotalLockedWidth : function(){
15038         var totalWidth = 0;
15039         for(var i = 0; i < this.config.length; i++){
15040             if(this.isLocked(i) && !this.isHidden(i)){
15041                 this.totalWidth += this.getColumnWidth(i);
15042             }
15043         }
15044         return totalWidth;
15045     },
15046
15047     getLockedCount : function(){
15048         for(var i = 0, len = this.config.length; i < len; i++){
15049             if(!this.isLocked(i)){
15050                 return i;
15051             }
15052         }
15053     },
15054
15055     /**
15056      * Returns the number of columns.
15057      * @return {Number}
15058      */
15059     getColumnCount : function(visibleOnly){
15060         if(visibleOnly === true){
15061             var c = 0;
15062             for(var i = 0, len = this.config.length; i < len; i++){
15063                 if(!this.isHidden(i)){
15064                     c++;
15065                 }
15066             }
15067             return c;
15068         }
15069         return this.config.length;
15070     },
15071
15072     /**
15073      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15074      * @param {Function} fn
15075      * @param {Object} scope (optional)
15076      * @return {Array} result
15077      */
15078     getColumnsBy : function(fn, scope){
15079         var r = [];
15080         for(var i = 0, len = this.config.length; i < len; i++){
15081             var c = this.config[i];
15082             if(fn.call(scope||this, c, i) === true){
15083                 r[r.length] = c;
15084             }
15085         }
15086         return r;
15087     },
15088
15089     /**
15090      * Returns true if the specified column is sortable.
15091      * @param {Number} col The column index
15092      * @return {Boolean}
15093      */
15094     isSortable : function(col){
15095         if(typeof this.config[col].sortable == "undefined"){
15096             return this.defaultSortable;
15097         }
15098         return this.config[col].sortable;
15099     },
15100
15101     /**
15102      * Returns the rendering (formatting) function defined for the column.
15103      * @param {Number} col The column index.
15104      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15105      */
15106     getRenderer : function(col){
15107         if(!this.config[col].renderer){
15108             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15109         }
15110         return this.config[col].renderer;
15111     },
15112
15113     /**
15114      * Sets the rendering (formatting) function for a column.
15115      * @param {Number} col The column index
15116      * @param {Function} fn The function to use to process the cell's raw data
15117      * to return HTML markup for the grid view. The render function is called with
15118      * the following parameters:<ul>
15119      * <li>Data value.</li>
15120      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15121      * <li>css A CSS style string to apply to the table cell.</li>
15122      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15123      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15124      * <li>Row index</li>
15125      * <li>Column index</li>
15126      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15127      */
15128     setRenderer : function(col, fn){
15129         this.config[col].renderer = fn;
15130     },
15131
15132     /**
15133      * Returns the width for the specified column.
15134      * @param {Number} col The column index
15135      * @return {Number}
15136      */
15137     getColumnWidth : function(col){
15138         return this.config[col].width * 1 || this.defaultWidth;
15139     },
15140
15141     /**
15142      * Sets the width for a column.
15143      * @param {Number} col The column index
15144      * @param {Number} width The new width
15145      */
15146     setColumnWidth : function(col, width, suppressEvent){
15147         this.config[col].width = width;
15148         this.totalWidth = null;
15149         if(!suppressEvent){
15150              this.fireEvent("widthchange", this, col, width);
15151         }
15152     },
15153
15154     /**
15155      * Returns the total width of all columns.
15156      * @param {Boolean} includeHidden True to include hidden column widths
15157      * @return {Number}
15158      */
15159     getTotalWidth : function(includeHidden){
15160         if(!this.totalWidth){
15161             this.totalWidth = 0;
15162             for(var i = 0, len = this.config.length; i < len; i++){
15163                 if(includeHidden || !this.isHidden(i)){
15164                     this.totalWidth += this.getColumnWidth(i);
15165                 }
15166             }
15167         }
15168         return this.totalWidth;
15169     },
15170
15171     /**
15172      * Returns the header for the specified column.
15173      * @param {Number} col The column index
15174      * @return {String}
15175      */
15176     getColumnHeader : function(col){
15177         return this.config[col].header;
15178     },
15179
15180     /**
15181      * Sets the header for a column.
15182      * @param {Number} col The column index
15183      * @param {String} header The new header
15184      */
15185     setColumnHeader : function(col, header){
15186         this.config[col].header = header;
15187         this.fireEvent("headerchange", this, col, header);
15188     },
15189
15190     /**
15191      * Returns the tooltip for the specified column.
15192      * @param {Number} col The column index
15193      * @return {String}
15194      */
15195     getColumnTooltip : function(col){
15196             return this.config[col].tooltip;
15197     },
15198     /**
15199      * Sets the tooltip for a column.
15200      * @param {Number} col The column index
15201      * @param {String} tooltip The new tooltip
15202      */
15203     setColumnTooltip : function(col, tooltip){
15204             this.config[col].tooltip = tooltip;
15205     },
15206
15207     /**
15208      * Returns the dataIndex for the specified column.
15209      * @param {Number} col The column index
15210      * @return {Number}
15211      */
15212     getDataIndex : function(col){
15213         return this.config[col].dataIndex;
15214     },
15215
15216     /**
15217      * Sets the dataIndex for a column.
15218      * @param {Number} col The column index
15219      * @param {Number} dataIndex The new dataIndex
15220      */
15221     setDataIndex : function(col, dataIndex){
15222         this.config[col].dataIndex = dataIndex;
15223     },
15224
15225     
15226     
15227     /**
15228      * Returns true if the cell is editable.
15229      * @param {Number} colIndex The column index
15230      * @param {Number} rowIndex The row index
15231      * @return {Boolean}
15232      */
15233     isCellEditable : function(colIndex, rowIndex){
15234         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15235     },
15236
15237     /**
15238      * Returns the editor defined for the cell/column.
15239      * return false or null to disable editing.
15240      * @param {Number} colIndex The column index
15241      * @param {Number} rowIndex The row index
15242      * @return {Object}
15243      */
15244     getCellEditor : function(colIndex, rowIndex){
15245         return this.config[colIndex].editor;
15246     },
15247
15248     /**
15249      * Sets if a column is editable.
15250      * @param {Number} col The column index
15251      * @param {Boolean} editable True if the column is editable
15252      */
15253     setEditable : function(col, editable){
15254         this.config[col].editable = editable;
15255     },
15256
15257
15258     /**
15259      * Returns true if the column is hidden.
15260      * @param {Number} colIndex The column index
15261      * @return {Boolean}
15262      */
15263     isHidden : function(colIndex){
15264         return this.config[colIndex].hidden;
15265     },
15266
15267
15268     /**
15269      * Returns true if the column width cannot be changed
15270      */
15271     isFixed : function(colIndex){
15272         return this.config[colIndex].fixed;
15273     },
15274
15275     /**
15276      * Returns true if the column can be resized
15277      * @return {Boolean}
15278      */
15279     isResizable : function(colIndex){
15280         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15281     },
15282     /**
15283      * Sets if a column is hidden.
15284      * @param {Number} colIndex The column index
15285      * @param {Boolean} hidden True if the column is hidden
15286      */
15287     setHidden : function(colIndex, hidden){
15288         this.config[colIndex].hidden = hidden;
15289         this.totalWidth = null;
15290         this.fireEvent("hiddenchange", this, colIndex, hidden);
15291     },
15292
15293     /**
15294      * Sets the editor for a column.
15295      * @param {Number} col The column index
15296      * @param {Object} editor The editor object
15297      */
15298     setEditor : function(col, editor){
15299         this.config[col].editor = editor;
15300     }
15301 });
15302
15303 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15304         if(typeof value == "string" && value.length < 1){
15305             return "&#160;";
15306         }
15307         return value;
15308 };
15309
15310 // Alias for backwards compatibility
15311 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15312
15313 /**
15314  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15315  * @class Roo.bootstrap.Table.RowSelectionModel
15316  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15317  * It supports multiple selections and keyboard selection/navigation. 
15318  * @constructor
15319  * @param {Object} config
15320  */
15321
15322 Roo.bootstrap.Table.RowSelectionModel = function(config){
15323     Roo.apply(this, config);
15324     this.selections = new Roo.util.MixedCollection(false, function(o){
15325         return o.id;
15326     });
15327
15328     this.last = false;
15329     this.lastActive = false;
15330
15331     this.addEvents({
15332         /**
15333              * @event selectionchange
15334              * Fires when the selection changes
15335              * @param {SelectionModel} this
15336              */
15337             "selectionchange" : true,
15338         /**
15339              * @event afterselectionchange
15340              * Fires after the selection changes (eg. by key press or clicking)
15341              * @param {SelectionModel} this
15342              */
15343             "afterselectionchange" : true,
15344         /**
15345              * @event beforerowselect
15346              * Fires when a row is selected being selected, return false to cancel.
15347              * @param {SelectionModel} this
15348              * @param {Number} rowIndex The selected index
15349              * @param {Boolean} keepExisting False if other selections will be cleared
15350              */
15351             "beforerowselect" : true,
15352         /**
15353              * @event rowselect
15354              * Fires when a row is selected.
15355              * @param {SelectionModel} this
15356              * @param {Number} rowIndex The selected index
15357              * @param {Roo.data.Record} r The record
15358              */
15359             "rowselect" : true,
15360         /**
15361              * @event rowdeselect
15362              * Fires when a row is deselected.
15363              * @param {SelectionModel} this
15364              * @param {Number} rowIndex The selected index
15365              */
15366         "rowdeselect" : true
15367     });
15368     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15369     this.locked = false;
15370 };
15371
15372 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15373     /**
15374      * @cfg {Boolean} singleSelect
15375      * True to allow selection of only one row at a time (defaults to false)
15376      */
15377     singleSelect : false,
15378
15379     // private
15380     initEvents : function(){
15381
15382         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15383             this.grid.on("mousedown", this.handleMouseDown, this);
15384         }else{ // allow click to work like normal
15385             this.grid.on("rowclick", this.handleDragableRowClick, this);
15386         }
15387
15388         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15389             "up" : function(e){
15390                 if(!e.shiftKey){
15391                     this.selectPrevious(e.shiftKey);
15392                 }else if(this.last !== false && this.lastActive !== false){
15393                     var last = this.last;
15394                     this.selectRange(this.last,  this.lastActive-1);
15395                     this.grid.getView().focusRow(this.lastActive);
15396                     if(last !== false){
15397                         this.last = last;
15398                     }
15399                 }else{
15400                     this.selectFirstRow();
15401                 }
15402                 this.fireEvent("afterselectionchange", this);
15403             },
15404             "down" : function(e){
15405                 if(!e.shiftKey){
15406                     this.selectNext(e.shiftKey);
15407                 }else if(this.last !== false && this.lastActive !== false){
15408                     var last = this.last;
15409                     this.selectRange(this.last,  this.lastActive+1);
15410                     this.grid.getView().focusRow(this.lastActive);
15411                     if(last !== false){
15412                         this.last = last;
15413                     }
15414                 }else{
15415                     this.selectFirstRow();
15416                 }
15417                 this.fireEvent("afterselectionchange", this);
15418             },
15419             scope: this
15420         });
15421
15422         var view = this.grid.view;
15423         view.on("refresh", this.onRefresh, this);
15424         view.on("rowupdated", this.onRowUpdated, this);
15425         view.on("rowremoved", this.onRemove, this);
15426     },
15427
15428     // private
15429     onRefresh : function(){
15430         var ds = this.grid.dataSource, i, v = this.grid.view;
15431         var s = this.selections;
15432         s.each(function(r){
15433             if((i = ds.indexOfId(r.id)) != -1){
15434                 v.onRowSelect(i);
15435             }else{
15436                 s.remove(r);
15437             }
15438         });
15439     },
15440
15441     // private
15442     onRemove : function(v, index, r){
15443         this.selections.remove(r);
15444     },
15445
15446     // private
15447     onRowUpdated : function(v, index, r){
15448         if(this.isSelected(r)){
15449             v.onRowSelect(index);
15450         }
15451     },
15452
15453     /**
15454      * Select records.
15455      * @param {Array} records The records to select
15456      * @param {Boolean} keepExisting (optional) True to keep existing selections
15457      */
15458     selectRecords : function(records, keepExisting){
15459         if(!keepExisting){
15460             this.clearSelections();
15461         }
15462         var ds = this.grid.dataSource;
15463         for(var i = 0, len = records.length; i < len; i++){
15464             this.selectRow(ds.indexOf(records[i]), true);
15465         }
15466     },
15467
15468     /**
15469      * Gets the number of selected rows.
15470      * @return {Number}
15471      */
15472     getCount : function(){
15473         return this.selections.length;
15474     },
15475
15476     /**
15477      * Selects the first row in the grid.
15478      */
15479     selectFirstRow : function(){
15480         this.selectRow(0);
15481     },
15482
15483     /**
15484      * Select the last row.
15485      * @param {Boolean} keepExisting (optional) True to keep existing selections
15486      */
15487     selectLastRow : function(keepExisting){
15488         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15489     },
15490
15491     /**
15492      * Selects the row immediately following the last selected row.
15493      * @param {Boolean} keepExisting (optional) True to keep existing selections
15494      */
15495     selectNext : function(keepExisting){
15496         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15497             this.selectRow(this.last+1, keepExisting);
15498             this.grid.getView().focusRow(this.last);
15499         }
15500     },
15501
15502     /**
15503      * Selects the row that precedes the last selected row.
15504      * @param {Boolean} keepExisting (optional) True to keep existing selections
15505      */
15506     selectPrevious : function(keepExisting){
15507         if(this.last){
15508             this.selectRow(this.last-1, keepExisting);
15509             this.grid.getView().focusRow(this.last);
15510         }
15511     },
15512
15513     /**
15514      * Returns the selected records
15515      * @return {Array} Array of selected records
15516      */
15517     getSelections : function(){
15518         return [].concat(this.selections.items);
15519     },
15520
15521     /**
15522      * Returns the first selected record.
15523      * @return {Record}
15524      */
15525     getSelected : function(){
15526         return this.selections.itemAt(0);
15527     },
15528
15529
15530     /**
15531      * Clears all selections.
15532      */
15533     clearSelections : function(fast){
15534         if(this.locked) return;
15535         if(fast !== true){
15536             var ds = this.grid.dataSource;
15537             var s = this.selections;
15538             s.each(function(r){
15539                 this.deselectRow(ds.indexOfId(r.id));
15540             }, this);
15541             s.clear();
15542         }else{
15543             this.selections.clear();
15544         }
15545         this.last = false;
15546     },
15547
15548
15549     /**
15550      * Selects all rows.
15551      */
15552     selectAll : function(){
15553         if(this.locked) return;
15554         this.selections.clear();
15555         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15556             this.selectRow(i, true);
15557         }
15558     },
15559
15560     /**
15561      * Returns True if there is a selection.
15562      * @return {Boolean}
15563      */
15564     hasSelection : function(){
15565         return this.selections.length > 0;
15566     },
15567
15568     /**
15569      * Returns True if the specified row is selected.
15570      * @param {Number/Record} record The record or index of the record to check
15571      * @return {Boolean}
15572      */
15573     isSelected : function(index){
15574         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15575         return (r && this.selections.key(r.id) ? true : false);
15576     },
15577
15578     /**
15579      * Returns True if the specified record id is selected.
15580      * @param {String} id The id of record to check
15581      * @return {Boolean}
15582      */
15583     isIdSelected : function(id){
15584         return (this.selections.key(id) ? true : false);
15585     },
15586
15587     // private
15588     handleMouseDown : function(e, t){
15589         var view = this.grid.getView(), rowIndex;
15590         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15591             return;
15592         };
15593         if(e.shiftKey && this.last !== false){
15594             var last = this.last;
15595             this.selectRange(last, rowIndex, e.ctrlKey);
15596             this.last = last; // reset the last
15597             view.focusRow(rowIndex);
15598         }else{
15599             var isSelected = this.isSelected(rowIndex);
15600             if(e.button !== 0 && isSelected){
15601                 view.focusRow(rowIndex);
15602             }else if(e.ctrlKey && isSelected){
15603                 this.deselectRow(rowIndex);
15604             }else if(!isSelected){
15605                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15606                 view.focusRow(rowIndex);
15607             }
15608         }
15609         this.fireEvent("afterselectionchange", this);
15610     },
15611     // private
15612     handleDragableRowClick :  function(grid, rowIndex, e) 
15613     {
15614         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15615             this.selectRow(rowIndex, false);
15616             grid.view.focusRow(rowIndex);
15617              this.fireEvent("afterselectionchange", this);
15618         }
15619     },
15620     
15621     /**
15622      * Selects multiple rows.
15623      * @param {Array} rows Array of the indexes of the row to select
15624      * @param {Boolean} keepExisting (optional) True to keep existing selections
15625      */
15626     selectRows : function(rows, keepExisting){
15627         if(!keepExisting){
15628             this.clearSelections();
15629         }
15630         for(var i = 0, len = rows.length; i < len; i++){
15631             this.selectRow(rows[i], true);
15632         }
15633     },
15634
15635     /**
15636      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15637      * @param {Number} startRow The index of the first row in the range
15638      * @param {Number} endRow The index of the last row in the range
15639      * @param {Boolean} keepExisting (optional) True to retain existing selections
15640      */
15641     selectRange : function(startRow, endRow, keepExisting){
15642         if(this.locked) return;
15643         if(!keepExisting){
15644             this.clearSelections();
15645         }
15646         if(startRow <= endRow){
15647             for(var i = startRow; i <= endRow; i++){
15648                 this.selectRow(i, true);
15649             }
15650         }else{
15651             for(var i = startRow; i >= endRow; i--){
15652                 this.selectRow(i, true);
15653             }
15654         }
15655     },
15656
15657     /**
15658      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15659      * @param {Number} startRow The index of the first row in the range
15660      * @param {Number} endRow The index of the last row in the range
15661      */
15662     deselectRange : function(startRow, endRow, preventViewNotify){
15663         if(this.locked) return;
15664         for(var i = startRow; i <= endRow; i++){
15665             this.deselectRow(i, preventViewNotify);
15666         }
15667     },
15668
15669     /**
15670      * Selects a row.
15671      * @param {Number} row The index of the row to select
15672      * @param {Boolean} keepExisting (optional) True to keep existing selections
15673      */
15674     selectRow : function(index, keepExisting, preventViewNotify){
15675         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15676         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15677             if(!keepExisting || this.singleSelect){
15678                 this.clearSelections();
15679             }
15680             var r = this.grid.dataSource.getAt(index);
15681             this.selections.add(r);
15682             this.last = this.lastActive = index;
15683             if(!preventViewNotify){
15684                 this.grid.getView().onRowSelect(index);
15685             }
15686             this.fireEvent("rowselect", this, index, r);
15687             this.fireEvent("selectionchange", this);
15688         }
15689     },
15690
15691     /**
15692      * Deselects a row.
15693      * @param {Number} row The index of the row to deselect
15694      */
15695     deselectRow : function(index, preventViewNotify){
15696         if(this.locked) return;
15697         if(this.last == index){
15698             this.last = false;
15699         }
15700         if(this.lastActive == index){
15701             this.lastActive = false;
15702         }
15703         var r = this.grid.dataSource.getAt(index);
15704         this.selections.remove(r);
15705         if(!preventViewNotify){
15706             this.grid.getView().onRowDeselect(index);
15707         }
15708         this.fireEvent("rowdeselect", this, index);
15709         this.fireEvent("selectionchange", this);
15710     },
15711
15712     // private
15713     restoreLast : function(){
15714         if(this._last){
15715             this.last = this._last;
15716         }
15717     },
15718
15719     // private
15720     acceptsNav : function(row, col, cm){
15721         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15722     },
15723
15724     // private
15725     onEditorKey : function(field, e){
15726         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15727         if(k == e.TAB){
15728             e.stopEvent();
15729             ed.completeEdit();
15730             if(e.shiftKey){
15731                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15732             }else{
15733                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15734             }
15735         }else if(k == e.ENTER && !e.ctrlKey){
15736             e.stopEvent();
15737             ed.completeEdit();
15738             if(e.shiftKey){
15739                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15740             }else{
15741                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15742             }
15743         }else if(k == e.ESC){
15744             ed.cancelEdit();
15745         }
15746         if(newCell){
15747             g.startEditing(newCell[0], newCell[1]);
15748         }
15749     }
15750 });/*
15751  * - LGPL
15752  *
15753  * element
15754  * 
15755  */
15756
15757 /**
15758  * @class Roo.bootstrap.MessageBar
15759  * @extends Roo.bootstrap.Component
15760  * Bootstrap MessageBar class
15761  * @cfg {String} html contents of the MessageBar
15762  * @cfg {String} weight (info | success | warning | danger) default info
15763  * @cfg {String} beforeClass insert the bar before the given class
15764  * @cfg {Boolean} closable (true | false) default false
15765  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15766  * 
15767  * @constructor
15768  * Create a new Element
15769  * @param {Object} config The config object
15770  */
15771
15772 Roo.bootstrap.MessageBar = function(config){
15773     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15774 };
15775
15776 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15777     
15778     html: '',
15779     weight: 'info',
15780     closable: false,
15781     fixed: false,
15782     beforeClass: 'bootstrap-sticky-wrap',
15783     
15784     getAutoCreate : function(){
15785         
15786         var cfg = {
15787             tag: 'div',
15788             cls: 'alert alert-dismissable alert-' + this.weight,
15789             cn: [
15790                 {
15791                     tag: 'span',
15792                     cls: 'message',
15793                     html: this.html || ''
15794                 }
15795             ]
15796         }
15797         
15798         if(this.fixed){
15799             cfg.cls += ' alert-messages-fixed';
15800         }
15801         
15802         if(this.closable){
15803             cfg.cn.push({
15804                 tag: 'button',
15805                 cls: 'close',
15806                 html: 'x'
15807             });
15808         }
15809         
15810         return cfg;
15811     },
15812     
15813     onRender : function(ct, position)
15814     {
15815         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15816         
15817         if(!this.el){
15818             var cfg = Roo.apply({},  this.getAutoCreate());
15819             cfg.id = Roo.id();
15820             
15821             if (this.cls) {
15822                 cfg.cls += ' ' + this.cls;
15823             }
15824             if (this.style) {
15825                 cfg.style = this.style;
15826             }
15827             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15828             
15829             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15830         }
15831         
15832         this.el.select('>button.close').on('click', this.hide, this);
15833         
15834     },
15835     
15836     show : function()
15837     {
15838         if (!this.rendered) {
15839             this.render();
15840         }
15841         
15842         this.el.show();
15843         
15844         this.fireEvent('show', this);
15845         
15846     },
15847     
15848     hide : function()
15849     {
15850         if (!this.rendered) {
15851             this.render();
15852         }
15853         
15854         this.el.hide();
15855         
15856         this.fireEvent('hide', this);
15857     },
15858     
15859     update : function()
15860     {
15861 //        var e = this.el.dom.firstChild;
15862 //        
15863 //        if(this.closable){
15864 //            e = e.nextSibling;
15865 //        }
15866 //        
15867 //        e.data = this.html || '';
15868
15869         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15870     }
15871    
15872 });
15873
15874  
15875
15876