485a0a865fe5bb183ded70f068dffb46109a49f6
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         Roo.log(this.el);
2954 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2955 //        this.maskEl.enableDisplayMode("block");
2956 //        this.maskEl.show();
2957         
2958         this.store.on('load', this.onLoad, this);
2959         this.store.on('beforeload', this.onBeforeLoad, this);
2960         
2961         this.store.load();
2962         
2963         
2964         
2965     },
2966     
2967     renderHeader : function()
2968     {
2969         var header = {
2970             tag: 'thead',
2971             cn : []
2972         };
2973         
2974         var cm = this.cm;
2975         
2976         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2977             
2978             var config = cm.config[i];
2979             
2980             var c = {
2981                 tag: 'th',
2982                 html: cm.getColumnHeader(i)
2983             };
2984             
2985             if(typeof(config.dataIndex) != 'undefined'){
2986                 c.sort = config.dataIndex;
2987             }
2988             
2989             if(typeof(config.sortable) != 'undefined' && config.sortable){
2990                 c.cls = 'sortable';
2991             }
2992             
2993             header.cn.push(c)
2994         }
2995         
2996         return header;
2997     },
2998     
2999     renderBody : function()
3000     {
3001         var body = {
3002             tag: 'tbody',
3003             cn : []
3004         };
3005         
3006         return body;
3007     },
3008     
3009     renderFooter : function()
3010     {
3011         var footer = {
3012             tag: 'tfoot',
3013             cn : []
3014         };
3015         
3016         return footer;
3017     },
3018     
3019     onLoad : function()
3020     {
3021         Roo.log('ds onload');
3022         
3023         var cm = this.cm;
3024         
3025         var tbody = this.el.select('tbody', true).first();
3026         
3027         var renders = [];
3028         
3029         if(this.store.getCount() > 0){
3030             this.store.data.each(function(d){
3031                 var row = {
3032                     tag : 'tr',
3033                     cn : []
3034                 };
3035                 
3036                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3037                     var renderer = cm.getRenderer(i);
3038                     var config = cm.config[i];
3039                     var value = '';
3040                     var id = Roo.id();
3041                     
3042                     if(typeof(renderer) !== 'undefined'){
3043                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3044                     }
3045                     
3046                     if(typeof(value) === 'object'){
3047                         renders.push({
3048                             id : id,
3049                             cfg : value 
3050                         })
3051                     }
3052                     
3053                     var td = {
3054                         tag: 'td',
3055                         id: id,
3056                         html: (typeof(value) === 'object') ? '' : value
3057                     };
3058                     
3059                     if(typeof(config.width) != 'undefined'){
3060                         td.width = config.width;
3061                     }
3062                     
3063                     row.cn.push(td);
3064                    
3065                 }
3066                 
3067                 tbody.createChild(row);
3068                 
3069             });
3070         }
3071         
3072         
3073         if(renders.length){
3074             var _this = this;
3075             Roo.each(renders, function(r){
3076                 _this.renderColumn(r);
3077             })
3078         }
3079 //        
3080 //        if(this.loadMask){
3081 //            this.maskEl.hide();
3082 //        }
3083     },
3084     
3085     onBeforeLoad : function()
3086     {
3087         Roo.log('ds onBeforeLoad');
3088         
3089         this.clear();
3090         
3091 //        if(this.loadMask){
3092 //            this.maskEl.show();
3093 //        }
3094     },
3095     
3096     clear : function()
3097     {
3098         this.el.select('tbody', true).first().dom.innerHTML = '';
3099     },
3100     
3101     getSelectionModel : function(){
3102         if(!this.selModel){
3103             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3104         }
3105         return this.selModel;
3106     },
3107     
3108     renderColumn : function(r)
3109     {
3110         var _this = this;
3111         r.cfg.render(Roo.get(r.id));
3112         
3113         if(r.cfg.cn){
3114             Roo.each(r.cfg.cn, function(c){
3115                 var child = {
3116                     id: r.id,
3117                     cfg: c
3118                 }
3119                 _this.renderColumn(child);
3120             })
3121         }
3122     }
3123    
3124 });
3125
3126  
3127
3128  /*
3129  * - LGPL
3130  *
3131  * table cell
3132  * 
3133  */
3134
3135 /**
3136  * @class Roo.bootstrap.TableCell
3137  * @extends Roo.bootstrap.Component
3138  * Bootstrap TableCell class
3139  * @cfg {String} html cell contain text
3140  * @cfg {String} cls cell class
3141  * @cfg {String} tag cell tag (td|th) default td
3142  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3143  * @cfg {String} align Aligns the content in a cell
3144  * @cfg {String} axis Categorizes cells
3145  * @cfg {String} bgcolor Specifies the background color of a cell
3146  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3147  * @cfg {Number} colspan Specifies the number of columns a cell should span
3148  * @cfg {String} headers Specifies one or more header cells a cell is related to
3149  * @cfg {Number} height Sets the height of a cell
3150  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3151  * @cfg {Number} rowspan Sets the number of rows a cell should span
3152  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3153  * @cfg {String} valign Vertical aligns the content in a cell
3154  * @cfg {Number} width Specifies the width of a cell
3155  * 
3156  * @constructor
3157  * Create a new TableCell
3158  * @param {Object} config The config object
3159  */
3160
3161 Roo.bootstrap.TableCell = function(config){
3162     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3163 };
3164
3165 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3166     
3167     html: false,
3168     cls: false,
3169     tag: false,
3170     abbr: false,
3171     align: false,
3172     axis: false,
3173     bgcolor: false,
3174     charoff: false,
3175     colspan: false,
3176     headers: false,
3177     height: false,
3178     nowrap: false,
3179     rowspan: false,
3180     scope: false,
3181     valign: false,
3182     width: false,
3183     
3184     
3185     getAutoCreate : function(){
3186         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3187         
3188         cfg = {
3189             tag: 'td'
3190         }
3191         
3192         if(this.tag){
3193             cfg.tag = this.tag;
3194         }
3195         
3196         if (this.html) {
3197             cfg.html=this.html
3198         }
3199         if (this.cls) {
3200             cfg.cls=this.cls
3201         }
3202         if (this.abbr) {
3203             cfg.abbr=this.abbr
3204         }
3205         if (this.align) {
3206             cfg.align=this.align
3207         }
3208         if (this.axis) {
3209             cfg.axis=this.axis
3210         }
3211         if (this.bgcolor) {
3212             cfg.bgcolor=this.bgcolor
3213         }
3214         if (this.charoff) {
3215             cfg.charoff=this.charoff
3216         }
3217         if (this.colspan) {
3218             cfg.colspan=this.colspan
3219         }
3220         if (this.headers) {
3221             cfg.headers=this.headers
3222         }
3223         if (this.height) {
3224             cfg.height=this.height
3225         }
3226         if (this.nowrap) {
3227             cfg.nowrap=this.nowrap
3228         }
3229         if (this.rowspan) {
3230             cfg.rowspan=this.rowspan
3231         }
3232         if (this.scope) {
3233             cfg.scope=this.scope
3234         }
3235         if (this.valign) {
3236             cfg.valign=this.valign
3237         }
3238         if (this.width) {
3239             cfg.width=this.width
3240         }
3241         
3242         
3243         return cfg;
3244     }
3245    
3246 });
3247
3248  
3249
3250  /*
3251  * - LGPL
3252  *
3253  * table row
3254  * 
3255  */
3256
3257 /**
3258  * @class Roo.bootstrap.TableRow
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap TableRow class
3261  * @cfg {String} cls row class
3262  * @cfg {String} align Aligns the content in a table row
3263  * @cfg {String} bgcolor Specifies a background color for a table row
3264  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3265  * @cfg {String} valign Vertical aligns the content in a table row
3266  * 
3267  * @constructor
3268  * Create a new TableRow
3269  * @param {Object} config The config object
3270  */
3271
3272 Roo.bootstrap.TableRow = function(config){
3273     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3274 };
3275
3276 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3277     
3278     cls: false,
3279     align: false,
3280     bgcolor: false,
3281     charoff: false,
3282     valign: false,
3283     
3284     getAutoCreate : function(){
3285         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3286         
3287         cfg = {
3288             tag: 'tr'
3289         }
3290             
3291         if(this.cls){
3292             cfg.cls = this.cls;
3293         }
3294         if(this.align){
3295             cfg.align = this.align;
3296         }
3297         if(this.bgcolor){
3298             cfg.bgcolor = this.bgcolor;
3299         }
3300         if(this.charoff){
3301             cfg.charoff = this.charoff;
3302         }
3303         if(this.valign){
3304             cfg.valign = this.valign;
3305         }
3306         
3307         return cfg;
3308     }
3309    
3310 });
3311
3312  
3313
3314  /*
3315  * - LGPL
3316  *
3317  * table body
3318  * 
3319  */
3320
3321 /**
3322  * @class Roo.bootstrap.TableBody
3323  * @extends Roo.bootstrap.Component
3324  * Bootstrap TableBody class
3325  * @cfg {String} cls element class
3326  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3327  * @cfg {String} align Aligns the content inside the element
3328  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3329  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3330  * 
3331  * @constructor
3332  * Create a new TableBody
3333  * @param {Object} config The config object
3334  */
3335
3336 Roo.bootstrap.TableBody = function(config){
3337     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3338 };
3339
3340 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3341     
3342     cls: false,
3343     tag: false,
3344     align: false,
3345     charoff: false,
3346     valign: false,
3347     
3348     getAutoCreate : function(){
3349         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3350         
3351         cfg = {
3352             tag: 'tbody'
3353         }
3354             
3355         if (this.cls) {
3356             cfg.cls=this.cls
3357         }
3358         if(this.tag){
3359             cfg.tag = this.tag;
3360         }
3361         
3362         if(this.align){
3363             cfg.align = this.align;
3364         }
3365         if(this.charoff){
3366             cfg.charoff = this.charoff;
3367         }
3368         if(this.valign){
3369             cfg.valign = this.valign;
3370         }
3371         
3372         return cfg;
3373     }
3374     
3375     
3376 //    initEvents : function()
3377 //    {
3378 //        
3379 //        if(!this.store){
3380 //            return;
3381 //        }
3382 //        
3383 //        this.store = Roo.factory(this.store, Roo.data);
3384 //        this.store.on('load', this.onLoad, this);
3385 //        
3386 //        this.store.load();
3387 //        
3388 //    },
3389 //    
3390 //    onLoad: function () 
3391 //    {   
3392 //        this.fireEvent('load', this);
3393 //    }
3394 //    
3395 //   
3396 });
3397
3398  
3399
3400  /*
3401  * Based on:
3402  * Ext JS Library 1.1.1
3403  * Copyright(c) 2006-2007, Ext JS, LLC.
3404  *
3405  * Originally Released Under LGPL - original licence link has changed is not relivant.
3406  *
3407  * Fork - LGPL
3408  * <script type="text/javascript">
3409  */
3410
3411 // as we use this in bootstrap.
3412 Roo.namespace('Roo.form');
3413  /**
3414  * @class Roo.form.Action
3415  * Internal Class used to handle form actions
3416  * @constructor
3417  * @param {Roo.form.BasicForm} el The form element or its id
3418  * @param {Object} config Configuration options
3419  */
3420
3421  
3422  
3423 // define the action interface
3424 Roo.form.Action = function(form, options){
3425     this.form = form;
3426     this.options = options || {};
3427 };
3428 /**
3429  * Client Validation Failed
3430  * @const 
3431  */
3432 Roo.form.Action.CLIENT_INVALID = 'client';
3433 /**
3434  * Server Validation Failed
3435  * @const 
3436  */
3437 Roo.form.Action.SERVER_INVALID = 'server';
3438  /**
3439  * Connect to Server Failed
3440  * @const 
3441  */
3442 Roo.form.Action.CONNECT_FAILURE = 'connect';
3443 /**
3444  * Reading Data from Server Failed
3445  * @const 
3446  */
3447 Roo.form.Action.LOAD_FAILURE = 'load';
3448
3449 Roo.form.Action.prototype = {
3450     type : 'default',
3451     failureType : undefined,
3452     response : undefined,
3453     result : undefined,
3454
3455     // interface method
3456     run : function(options){
3457
3458     },
3459
3460     // interface method
3461     success : function(response){
3462
3463     },
3464
3465     // interface method
3466     handleResponse : function(response){
3467
3468     },
3469
3470     // default connection failure
3471     failure : function(response){
3472         
3473         this.response = response;
3474         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3475         this.form.afterAction(this, false);
3476     },
3477
3478     processResponse : function(response){
3479         this.response = response;
3480         if(!response.responseText){
3481             return true;
3482         }
3483         this.result = this.handleResponse(response);
3484         return this.result;
3485     },
3486
3487     // utility functions used internally
3488     getUrl : function(appendParams){
3489         var url = this.options.url || this.form.url || this.form.el.dom.action;
3490         if(appendParams){
3491             var p = this.getParams();
3492             if(p){
3493                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3494             }
3495         }
3496         return url;
3497     },
3498
3499     getMethod : function(){
3500         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3501     },
3502
3503     getParams : function(){
3504         var bp = this.form.baseParams;
3505         var p = this.options.params;
3506         if(p){
3507             if(typeof p == "object"){
3508                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3509             }else if(typeof p == 'string' && bp){
3510                 p += '&' + Roo.urlEncode(bp);
3511             }
3512         }else if(bp){
3513             p = Roo.urlEncode(bp);
3514         }
3515         return p;
3516     },
3517
3518     createCallback : function(){
3519         return {
3520             success: this.success,
3521             failure: this.failure,
3522             scope: this,
3523             timeout: (this.form.timeout*1000),
3524             upload: this.form.fileUpload ? this.success : undefined
3525         };
3526     }
3527 };
3528
3529 Roo.form.Action.Submit = function(form, options){
3530     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3531 };
3532
3533 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3534     type : 'submit',
3535
3536     haveProgress : false,
3537     uploadComplete : false,
3538     
3539     // uploadProgress indicator.
3540     uploadProgress : function()
3541     {
3542         if (!this.form.progressUrl) {
3543             return;
3544         }
3545         
3546         if (!this.haveProgress) {
3547             Roo.MessageBox.progress("Uploading", "Uploading");
3548         }
3549         if (this.uploadComplete) {
3550            Roo.MessageBox.hide();
3551            return;
3552         }
3553         
3554         this.haveProgress = true;
3555    
3556         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3557         
3558         var c = new Roo.data.Connection();
3559         c.request({
3560             url : this.form.progressUrl,
3561             params: {
3562                 id : uid
3563             },
3564             method: 'GET',
3565             success : function(req){
3566                //console.log(data);
3567                 var rdata = false;
3568                 var edata;
3569                 try  {
3570                    rdata = Roo.decode(req.responseText)
3571                 } catch (e) {
3572                     Roo.log("Invalid data from server..");
3573                     Roo.log(edata);
3574                     return;
3575                 }
3576                 if (!rdata || !rdata.success) {
3577                     Roo.log(rdata);
3578                     Roo.MessageBox.alert(Roo.encode(rdata));
3579                     return;
3580                 }
3581                 var data = rdata.data;
3582                 
3583                 if (this.uploadComplete) {
3584                    Roo.MessageBox.hide();
3585                    return;
3586                 }
3587                    
3588                 if (data){
3589                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3590                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3591                     );
3592                 }
3593                 this.uploadProgress.defer(2000,this);
3594             },
3595        
3596             failure: function(data) {
3597                 Roo.log('progress url failed ');
3598                 Roo.log(data);
3599             },
3600             scope : this
3601         });
3602            
3603     },
3604     
3605     
3606     run : function()
3607     {
3608         // run get Values on the form, so it syncs any secondary forms.
3609         this.form.getValues();
3610         
3611         var o = this.options;
3612         var method = this.getMethod();
3613         var isPost = method == 'POST';
3614         if(o.clientValidation === false || this.form.isValid()){
3615             
3616             if (this.form.progressUrl) {
3617                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3618                     (new Date() * 1) + '' + Math.random());
3619                     
3620             } 
3621             
3622             
3623             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3624                 form:this.form.el.dom,
3625                 url:this.getUrl(!isPost),
3626                 method: method,
3627                 params:isPost ? this.getParams() : null,
3628                 isUpload: this.form.fileUpload
3629             }));
3630             
3631             this.uploadProgress();
3632
3633         }else if (o.clientValidation !== false){ // client validation failed
3634             this.failureType = Roo.form.Action.CLIENT_INVALID;
3635             this.form.afterAction(this, false);
3636         }
3637     },
3638
3639     success : function(response)
3640     {
3641         this.uploadComplete= true;
3642         if (this.haveProgress) {
3643             Roo.MessageBox.hide();
3644         }
3645         
3646         
3647         var result = this.processResponse(response);
3648         if(result === true || result.success){
3649             this.form.afterAction(this, true);
3650             return;
3651         }
3652         if(result.errors){
3653             this.form.markInvalid(result.errors);
3654             this.failureType = Roo.form.Action.SERVER_INVALID;
3655         }
3656         this.form.afterAction(this, false);
3657     },
3658     failure : function(response)
3659     {
3660         this.uploadComplete= true;
3661         if (this.haveProgress) {
3662             Roo.MessageBox.hide();
3663         }
3664         
3665         this.response = response;
3666         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3667         this.form.afterAction(this, false);
3668     },
3669     
3670     handleResponse : function(response){
3671         if(this.form.errorReader){
3672             var rs = this.form.errorReader.read(response);
3673             var errors = [];
3674             if(rs.records){
3675                 for(var i = 0, len = rs.records.length; i < len; i++) {
3676                     var r = rs.records[i];
3677                     errors[i] = r.data;
3678                 }
3679             }
3680             if(errors.length < 1){
3681                 errors = null;
3682             }
3683             return {
3684                 success : rs.success,
3685                 errors : errors
3686             };
3687         }
3688         var ret = false;
3689         try {
3690             ret = Roo.decode(response.responseText);
3691         } catch (e) {
3692             ret = {
3693                 success: false,
3694                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3695                 errors : []
3696             };
3697         }
3698         return ret;
3699         
3700     }
3701 });
3702
3703
3704 Roo.form.Action.Load = function(form, options){
3705     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3706     this.reader = this.form.reader;
3707 };
3708
3709 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3710     type : 'load',
3711
3712     run : function(){
3713         
3714         Roo.Ajax.request(Roo.apply(
3715                 this.createCallback(), {
3716                     method:this.getMethod(),
3717                     url:this.getUrl(false),
3718                     params:this.getParams()
3719         }));
3720     },
3721
3722     success : function(response){
3723         
3724         var result = this.processResponse(response);
3725         if(result === true || !result.success || !result.data){
3726             this.failureType = Roo.form.Action.LOAD_FAILURE;
3727             this.form.afterAction(this, false);
3728             return;
3729         }
3730         this.form.clearInvalid();
3731         this.form.setValues(result.data);
3732         this.form.afterAction(this, true);
3733     },
3734
3735     handleResponse : function(response){
3736         if(this.form.reader){
3737             var rs = this.form.reader.read(response);
3738             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3739             return {
3740                 success : rs.success,
3741                 data : data
3742             };
3743         }
3744         return Roo.decode(response.responseText);
3745     }
3746 });
3747
3748 Roo.form.Action.ACTION_TYPES = {
3749     'load' : Roo.form.Action.Load,
3750     'submit' : Roo.form.Action.Submit
3751 };/*
3752  * - LGPL
3753  *
3754  * form
3755  * 
3756  */
3757
3758 /**
3759  * @class Roo.bootstrap.Form
3760  * @extends Roo.bootstrap.Component
3761  * Bootstrap Form class
3762  * @cfg {String} method  GET | POST (default POST)
3763  * @cfg {String} labelAlign top | left (default top)
3764   * @cfg {String} align left  | right - for navbars
3765
3766  * 
3767  * @constructor
3768  * Create a new Form
3769  * @param {Object} config The config object
3770  */
3771
3772
3773 Roo.bootstrap.Form = function(config){
3774     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3775     this.addEvents({
3776         /**
3777          * @event clientvalidation
3778          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3779          * @param {Form} this
3780          * @param {Boolean} valid true if the form has passed client-side validation
3781          */
3782         clientvalidation: true,
3783         /**
3784          * @event beforeaction
3785          * Fires before any action is performed. Return false to cancel the action.
3786          * @param {Form} this
3787          * @param {Action} action The action to be performed
3788          */
3789         beforeaction: true,
3790         /**
3791          * @event actionfailed
3792          * Fires when an action fails.
3793          * @param {Form} this
3794          * @param {Action} action The action that failed
3795          */
3796         actionfailed : true,
3797         /**
3798          * @event actioncomplete
3799          * Fires when an action is completed.
3800          * @param {Form} this
3801          * @param {Action} action The action that completed
3802          */
3803         actioncomplete : true
3804     });
3805     
3806 };
3807
3808 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3809       
3810      /**
3811      * @cfg {String} method
3812      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3813      */
3814     method : 'POST',
3815     /**
3816      * @cfg {String} url
3817      * The URL to use for form actions if one isn't supplied in the action options.
3818      */
3819     /**
3820      * @cfg {Boolean} fileUpload
3821      * Set to true if this form is a file upload.
3822      */
3823      
3824     /**
3825      * @cfg {Object} baseParams
3826      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3827      */
3828       
3829     /**
3830      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3831      */
3832     timeout: 30,
3833     /**
3834      * @cfg {Sting} align (left|right) for navbar forms
3835      */
3836     align : 'left',
3837
3838     // private
3839     activeAction : null,
3840  
3841     /**
3842      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3843      * element by passing it or its id or mask the form itself by passing in true.
3844      * @type Mixed
3845      */
3846     waitMsgTarget : false,
3847     
3848      
3849     
3850     /**
3851      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3852      * element by passing it or its id or mask the form itself by passing in true.
3853      * @type Mixed
3854      */
3855     
3856     getAutoCreate : function(){
3857         
3858         var cfg = {
3859             tag: 'form',
3860             method : this.method || 'POST',
3861             id : this.id || Roo.id(),
3862             cls : ''
3863         }
3864         if (this.parent().xtype.match(/^Nav/)) {
3865             cfg.cls = 'navbar-form navbar-' + this.align;
3866             
3867         }
3868         
3869         if (this.labelAlign == 'left' ) {
3870             cfg.cls += ' form-horizontal';
3871         }
3872         
3873         
3874         return cfg;
3875     },
3876     initEvents : function()
3877     {
3878         this.el.on('submit', this.onSubmit, this);
3879         
3880         
3881     },
3882     // private
3883     onSubmit : function(e){
3884         e.stopEvent();
3885     },
3886     
3887      /**
3888      * Returns true if client-side validation on the form is successful.
3889      * @return Boolean
3890      */
3891     isValid : function(){
3892         var items = this.getItems();
3893         var valid = true;
3894         items.each(function(f){
3895            if(!f.validate()){
3896                valid = false;
3897                
3898            }
3899         });
3900         return valid;
3901     },
3902     /**
3903      * Returns true if any fields in this form have changed since their original load.
3904      * @return Boolean
3905      */
3906     isDirty : function(){
3907         var dirty = false;
3908         var items = this.getItems();
3909         items.each(function(f){
3910            if(f.isDirty()){
3911                dirty = true;
3912                return false;
3913            }
3914            return true;
3915         });
3916         return dirty;
3917     },
3918      /**
3919      * Performs a predefined action (submit or load) or custom actions you define on this form.
3920      * @param {String} actionName The name of the action type
3921      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3922      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3923      * accept other config options):
3924      * <pre>
3925 Property          Type             Description
3926 ----------------  ---------------  ----------------------------------------------------------------------------------
3927 url               String           The url for the action (defaults to the form's url)
3928 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3929 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3930 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3931                                    validate the form on the client (defaults to false)
3932      * </pre>
3933      * @return {BasicForm} this
3934      */
3935     doAction : function(action, options){
3936         if(typeof action == 'string'){
3937             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3938         }
3939         if(this.fireEvent('beforeaction', this, action) !== false){
3940             this.beforeAction(action);
3941             action.run.defer(100, action);
3942         }
3943         return this;
3944     },
3945     
3946     // private
3947     beforeAction : function(action){
3948         var o = action.options;
3949         
3950         // not really supported yet.. ??
3951         
3952         //if(this.waitMsgTarget === true){
3953             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3954         //}else if(this.waitMsgTarget){
3955         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3956         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3957         //}else {
3958         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3959        // }
3960          
3961     },
3962
3963     // private
3964     afterAction : function(action, success){
3965         this.activeAction = null;
3966         var o = action.options;
3967         
3968         //if(this.waitMsgTarget === true){
3969             this.el.unmask();
3970         //}else if(this.waitMsgTarget){
3971         //    this.waitMsgTarget.unmask();
3972         //}else{
3973         //    Roo.MessageBox.updateProgress(1);
3974         //    Roo.MessageBox.hide();
3975        // }
3976         // 
3977         if(success){
3978             if(o.reset){
3979                 this.reset();
3980             }
3981             Roo.callback(o.success, o.scope, [this, action]);
3982             this.fireEvent('actioncomplete', this, action);
3983             
3984         }else{
3985             
3986             // failure condition..
3987             // we have a scenario where updates need confirming.
3988             // eg. if a locking scenario exists..
3989             // we look for { errors : { needs_confirm : true }} in the response.
3990             if (
3991                 (typeof(action.result) != 'undefined')  &&
3992                 (typeof(action.result.errors) != 'undefined')  &&
3993                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3994            ){
3995                 var _t = this;
3996                 Roo.log("not supported yet");
3997                  /*
3998                 
3999                 Roo.MessageBox.confirm(
4000                     "Change requires confirmation",
4001                     action.result.errorMsg,
4002                     function(r) {
4003                         if (r != 'yes') {
4004                             return;
4005                         }
4006                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4007                     }
4008                     
4009                 );
4010                 */
4011                 
4012                 
4013                 return;
4014             }
4015             
4016             Roo.callback(o.failure, o.scope, [this, action]);
4017             // show an error message if no failed handler is set..
4018             if (!this.hasListener('actionfailed')) {
4019                 Roo.log("need to add dialog support");
4020                 /*
4021                 Roo.MessageBox.alert("Error",
4022                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4023                         action.result.errorMsg :
4024                         "Saving Failed, please check your entries or try again"
4025                 );
4026                 */
4027             }
4028             
4029             this.fireEvent('actionfailed', this, action);
4030         }
4031         
4032     },
4033     /**
4034      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4035      * @param {String} id The value to search for
4036      * @return Field
4037      */
4038     findField : function(id){
4039         var items = this.getItems();
4040         var field = items.get(id);
4041         if(!field){
4042              items.each(function(f){
4043                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4044                     field = f;
4045                     return false;
4046                 }
4047                 return true;
4048             });
4049         }
4050         return field || null;
4051     },
4052      /**
4053      * Mark fields in this form invalid in bulk.
4054      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4055      * @return {BasicForm} this
4056      */
4057     markInvalid : function(errors){
4058         if(errors instanceof Array){
4059             for(var i = 0, len = errors.length; i < len; i++){
4060                 var fieldError = errors[i];
4061                 var f = this.findField(fieldError.id);
4062                 if(f){
4063                     f.markInvalid(fieldError.msg);
4064                 }
4065             }
4066         }else{
4067             var field, id;
4068             for(id in errors){
4069                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4070                     field.markInvalid(errors[id]);
4071                 }
4072             }
4073         }
4074         //Roo.each(this.childForms || [], function (f) {
4075         //    f.markInvalid(errors);
4076         //});
4077         
4078         return this;
4079     },
4080
4081     /**
4082      * Set values for fields in this form in bulk.
4083      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4084      * @return {BasicForm} this
4085      */
4086     setValues : function(values){
4087         if(values instanceof Array){ // array of objects
4088             for(var i = 0, len = values.length; i < len; i++){
4089                 var v = values[i];
4090                 var f = this.findField(v.id);
4091                 if(f){
4092                     f.setValue(v.value);
4093                     if(this.trackResetOnLoad){
4094                         f.originalValue = f.getValue();
4095                     }
4096                 }
4097             }
4098         }else{ // object hash
4099             var field, id;
4100             for(id in values){
4101                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4102                     
4103                     if (field.setFromData && 
4104                         field.valueField && 
4105                         field.displayField &&
4106                         // combos' with local stores can 
4107                         // be queried via setValue()
4108                         // to set their value..
4109                         (field.store && !field.store.isLocal)
4110                         ) {
4111                         // it's a combo
4112                         var sd = { };
4113                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4114                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4115                         field.setFromData(sd);
4116                         
4117                     } else {
4118                         field.setValue(values[id]);
4119                     }
4120                     
4121                     
4122                     if(this.trackResetOnLoad){
4123                         field.originalValue = field.getValue();
4124                     }
4125                 }
4126             }
4127         }
4128          
4129         //Roo.each(this.childForms || [], function (f) {
4130         //    f.setValues(values);
4131         //});
4132                 
4133         return this;
4134     },
4135
4136     /**
4137      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4138      * they are returned as an array.
4139      * @param {Boolean} asString
4140      * @return {Object}
4141      */
4142     getValues : function(asString){
4143         //if (this.childForms) {
4144             // copy values from the child forms
4145         //    Roo.each(this.childForms, function (f) {
4146         //        this.setValues(f.getValues());
4147         //    }, this);
4148         //}
4149         
4150         
4151         
4152         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4153         if(asString === true){
4154             return fs;
4155         }
4156         return Roo.urlDecode(fs);
4157     },
4158     
4159     /**
4160      * Returns the fields in this form as an object with key/value pairs. 
4161      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4162      * @return {Object}
4163      */
4164     getFieldValues : function(with_hidden)
4165     {
4166         var items = this.getItems();
4167         var ret = {};
4168         items.each(function(f){
4169             if (!f.getName()) {
4170                 return;
4171             }
4172             var v = f.getValue();
4173             if (f.inputType =='radio') {
4174                 if (typeof(ret[f.getName()]) == 'undefined') {
4175                     ret[f.getName()] = ''; // empty..
4176                 }
4177                 
4178                 if (!f.el.dom.checked) {
4179                     return;
4180                     
4181                 }
4182                 v = f.el.dom.value;
4183                 
4184             }
4185             
4186             // not sure if this supported any more..
4187             if ((typeof(v) == 'object') && f.getRawValue) {
4188                 v = f.getRawValue() ; // dates..
4189             }
4190             // combo boxes where name != hiddenName...
4191             if (f.name != f.getName()) {
4192                 ret[f.name] = f.getRawValue();
4193             }
4194             ret[f.getName()] = v;
4195         });
4196         
4197         return ret;
4198     },
4199
4200     /**
4201      * Clears all invalid messages in this form.
4202      * @return {BasicForm} this
4203      */
4204     clearInvalid : function(){
4205         var items = this.getItems();
4206         
4207         items.each(function(f){
4208            f.clearInvalid();
4209         });
4210         
4211         
4212         
4213         return this;
4214     },
4215
4216     /**
4217      * Resets this form.
4218      * @return {BasicForm} this
4219      */
4220     reset : function(){
4221         var items = this.getItems();
4222         items.each(function(f){
4223             f.reset();
4224         });
4225         
4226         Roo.each(this.childForms || [], function (f) {
4227             f.reset();
4228         });
4229        
4230         
4231         return this;
4232     },
4233     getItems : function()
4234     {
4235         var r=new Roo.util.MixedCollection(false, function(o){
4236             return o.id || (o.id = Roo.id());
4237         });
4238         var iter = function(el) {
4239             if (el.inputEl) {
4240                 r.add(el);
4241             }
4242             if (!el.items) {
4243                 return;
4244             }
4245             Roo.each(el.items,function(e) {
4246                 iter(e);
4247             });
4248             
4249             
4250         };
4251         iter(this);
4252         return r;
4253         
4254         
4255         
4256         
4257     }
4258     
4259 });
4260
4261  
4262 /*
4263  * Based on:
4264  * Ext JS Library 1.1.1
4265  * Copyright(c) 2006-2007, Ext JS, LLC.
4266  *
4267  * Originally Released Under LGPL - original licence link has changed is not relivant.
4268  *
4269  * Fork - LGPL
4270  * <script type="text/javascript">
4271  */
4272 /**
4273  * @class Roo.form.VTypes
4274  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4275  * @singleton
4276  */
4277 Roo.form.VTypes = function(){
4278     // closure these in so they are only created once.
4279     var alpha = /^[a-zA-Z_]+$/;
4280     var alphanum = /^[a-zA-Z0-9_]+$/;
4281     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4282     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4283
4284     // All these messages and functions are configurable
4285     return {
4286         /**
4287          * The function used to validate email addresses
4288          * @param {String} value The email address
4289          */
4290         'email' : function(v){
4291             return email.test(v);
4292         },
4293         /**
4294          * The error text to display when the email validation function returns false
4295          * @type String
4296          */
4297         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4298         /**
4299          * The keystroke filter mask to be applied on email input
4300          * @type RegExp
4301          */
4302         'emailMask' : /[a-z0-9_\.\-@]/i,
4303
4304         /**
4305          * The function used to validate URLs
4306          * @param {String} value The URL
4307          */
4308         'url' : function(v){
4309             return url.test(v);
4310         },
4311         /**
4312          * The error text to display when the url validation function returns false
4313          * @type String
4314          */
4315         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4316         
4317         /**
4318          * The function used to validate alpha values
4319          * @param {String} value The value
4320          */
4321         'alpha' : function(v){
4322             return alpha.test(v);
4323         },
4324         /**
4325          * The error text to display when the alpha validation function returns false
4326          * @type String
4327          */
4328         'alphaText' : 'This field should only contain letters and _',
4329         /**
4330          * The keystroke filter mask to be applied on alpha input
4331          * @type RegExp
4332          */
4333         'alphaMask' : /[a-z_]/i,
4334
4335         /**
4336          * The function used to validate alphanumeric values
4337          * @param {String} value The value
4338          */
4339         'alphanum' : function(v){
4340             return alphanum.test(v);
4341         },
4342         /**
4343          * The error text to display when the alphanumeric validation function returns false
4344          * @type String
4345          */
4346         'alphanumText' : 'This field should only contain letters, numbers and _',
4347         /**
4348          * The keystroke filter mask to be applied on alphanumeric input
4349          * @type RegExp
4350          */
4351         'alphanumMask' : /[a-z0-9_]/i
4352     };
4353 }();/*
4354  * - LGPL
4355  *
4356  * Input
4357  * 
4358  */
4359
4360 /**
4361  * @class Roo.bootstrap.Input
4362  * @extends Roo.bootstrap.Component
4363  * Bootstrap Input class
4364  * @cfg {Boolean} disabled is it disabled
4365  * @cfg {String} fieldLabel - the label associated
4366  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4367  * @cfg {String} name name of the input
4368  * @cfg {string} fieldLabel - the label associated
4369  * @cfg {string}  inputType - input / file submit ...
4370  * @cfg {string} placeholder - placeholder to put in text.
4371  * @cfg {string}  before - input group add on before
4372  * @cfg {string} after - input group add on after
4373  * @cfg {string} size - (lg|sm) or leave empty..
4374  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4375  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4376  * @cfg {Number} md colspan out of 12 for computer-sized screens
4377  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4378  * @cfg {string} value default value of the input
4379  * @cfg {Number} labelWidth set the width of label (0-12)
4380  * @cfg {String} labelAlign (top|left)
4381  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4382  * 
4383  * 
4384  * @constructor
4385  * Create a new Input
4386  * @param {Object} config The config object
4387  */
4388
4389 Roo.bootstrap.Input = function(config){
4390     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4391    
4392         this.addEvents({
4393             /**
4394              * @event focus
4395              * Fires when this field receives input focus.
4396              * @param {Roo.form.Field} this
4397              */
4398             focus : true,
4399             /**
4400              * @event blur
4401              * Fires when this field loses input focus.
4402              * @param {Roo.form.Field} this
4403              */
4404             blur : true,
4405             /**
4406              * @event specialkey
4407              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4408              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4409              * @param {Roo.form.Field} this
4410              * @param {Roo.EventObject} e The event object
4411              */
4412             specialkey : true,
4413             /**
4414              * @event change
4415              * Fires just before the field blurs if the field value has changed.
4416              * @param {Roo.form.Field} this
4417              * @param {Mixed} newValue The new value
4418              * @param {Mixed} oldValue The original value
4419              */
4420             change : true,
4421             /**
4422              * @event invalid
4423              * Fires after the field has been marked as invalid.
4424              * @param {Roo.form.Field} this
4425              * @param {String} msg The validation message
4426              */
4427             invalid : true,
4428             /**
4429              * @event valid
4430              * Fires after the field has been validated with no errors.
4431              * @param {Roo.form.Field} this
4432              */
4433             valid : true,
4434              /**
4435              * @event keyup
4436              * Fires after the key up
4437              * @param {Roo.form.Field} this
4438              * @param {Roo.EventObject}  e The event Object
4439              */
4440             keyup : true
4441         });
4442 };
4443
4444 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4445      /**
4446      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4447       automatic validation (defaults to "keyup").
4448      */
4449     validationEvent : "keyup",
4450      /**
4451      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4452      */
4453     validateOnBlur : true,
4454     /**
4455      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4456      */
4457     validationDelay : 250,
4458      /**
4459      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4460      */
4461     focusClass : "x-form-focus",  // not needed???
4462     
4463        
4464     /**
4465      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4466      */
4467     invalidClass : "has-error",
4468     
4469     /**
4470      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4471      */
4472     selectOnFocus : false,
4473     
4474      /**
4475      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4476      */
4477     maskRe : null,
4478        /**
4479      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4480      */
4481     vtype : null,
4482     
4483       /**
4484      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4485      */
4486     disableKeyFilter : false,
4487     
4488        /**
4489      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4490      */
4491     disabled : false,
4492      /**
4493      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4494      */
4495     allowBlank : true,
4496     /**
4497      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4498      */
4499     blankText : "This field is required",
4500     
4501      /**
4502      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4503      */
4504     minLength : 0,
4505     /**
4506      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4507      */
4508     maxLength : Number.MAX_VALUE,
4509     /**
4510      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4511      */
4512     minLengthText : "The minimum length for this field is {0}",
4513     /**
4514      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4515      */
4516     maxLengthText : "The maximum length for this field is {0}",
4517   
4518     
4519     /**
4520      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4521      * If available, this function will be called only after the basic validators all return true, and will be passed the
4522      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4523      */
4524     validator : null,
4525     /**
4526      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4527      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4528      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4529      */
4530     regex : null,
4531     /**
4532      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4533      */
4534     regexText : "",
4535     
4536     
4537     
4538     fieldLabel : '',
4539     inputType : 'text',
4540     
4541     name : false,
4542     placeholder: false,
4543     before : false,
4544     after : false,
4545     size : false,
4546     // private
4547     hasFocus : false,
4548     preventMark: false,
4549     isFormField : true,
4550     value : '',
4551     labelWidth : 2,
4552     labelAlign : false,
4553     readOnly : false,
4554     
4555     parentLabelAlign : function()
4556     {
4557         var parent = this;
4558         while (parent.parent()) {
4559             parent = parent.parent();
4560             if (typeof(parent.labelAlign) !='undefined') {
4561                 return parent.labelAlign;
4562             }
4563         }
4564         return 'left';
4565         
4566     },
4567     
4568     getAutoCreate : function(){
4569         
4570         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4571         
4572         var id = Roo.id();
4573         
4574         var cfg = {};
4575         
4576         if(this.inputType != 'hidden'){
4577             cfg.cls = 'form-group' //input-group
4578         }
4579         
4580         var input =  {
4581             tag: 'input',
4582             id : id,
4583             type : this.inputType,
4584             value : this.value,
4585             cls : 'form-control',
4586             placeholder : this.placeholder || ''
4587             
4588         };
4589         
4590         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4591             input.maxLength = this.maxLength;
4592         }
4593         
4594         if (this.disabled) {
4595             input.disabled=true;
4596         }
4597         
4598         if (this.readOnly) {
4599             input.readonly=true;
4600         }
4601         
4602         if (this.name) {
4603             input.name = this.name;
4604         }
4605         if (this.size) {
4606             input.cls += ' input-' + this.size;
4607         }
4608         var settings=this;
4609         ['xs','sm','md','lg'].map(function(size){
4610             if (settings[size]) {
4611                 cfg.cls += ' col-' + size + '-' + settings[size];
4612             }
4613         });
4614         
4615         var inputblock = input;
4616         
4617         if (this.before || this.after) {
4618             
4619             inputblock = {
4620                 cls : 'input-group',
4621                 cn :  [] 
4622             };
4623             if (this.before) {
4624                 inputblock.cn.push({
4625                     tag :'span',
4626                     cls : 'input-group-addon',
4627                     html : this.before
4628                 });
4629             }
4630             inputblock.cn.push(input);
4631             if (this.after) {
4632                 inputblock.cn.push({
4633                     tag :'span',
4634                     cls : 'input-group-addon',
4635                     html : this.after
4636                 });
4637             }
4638             
4639         };
4640         
4641         if (align ==='left' && this.fieldLabel.length) {
4642                 Roo.log("left and has label");
4643                 cfg.cn = [
4644                     
4645                     {
4646                         tag: 'label',
4647                         'for' :  id,
4648                         cls : 'control-label col-sm-' + this.labelWidth,
4649                         html : this.fieldLabel
4650                         
4651                     },
4652                     {
4653                         cls : "col-sm-" + (12 - this.labelWidth), 
4654                         cn: [
4655                             inputblock
4656                         ]
4657                     }
4658                     
4659                 ];
4660         } else if ( this.fieldLabel.length) {
4661                 Roo.log(" label");
4662                  cfg.cn = [
4663                    
4664                     {
4665                         tag: 'label',
4666                         //cls : 'input-group-addon',
4667                         html : this.fieldLabel
4668                         
4669                     },
4670                     
4671                     inputblock
4672                     
4673                 ];
4674
4675         } else {
4676             
4677                 Roo.log(" no label && no align");
4678                 cfg.cn = [
4679                     
4680                         inputblock
4681                     
4682                 ];
4683                 
4684                 
4685         };
4686         Roo.log('input-parentType: ' + this.parentType);
4687         
4688         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4689            cfg.cls += ' navbar-form';
4690            Roo.log(cfg);
4691         }
4692         
4693         return cfg;
4694         
4695     },
4696     /**
4697      * return the real input element.
4698      */
4699     inputEl: function ()
4700     {
4701         return this.el.select('input.form-control',true).first();
4702     },
4703     setDisabled : function(v)
4704     {
4705         var i  = this.inputEl().dom;
4706         if (!v) {
4707             i.removeAttribute('disabled');
4708             return;
4709             
4710         }
4711         i.setAttribute('disabled','true');
4712     },
4713     initEvents : function()
4714     {
4715         
4716         this.inputEl().on("keydown" , this.fireKey,  this);
4717         this.inputEl().on("focus", this.onFocus,  this);
4718         this.inputEl().on("blur", this.onBlur,  this);
4719         
4720         this.inputEl().relayEvent('keyup', this);
4721
4722         // reference to original value for reset
4723         this.originalValue = this.getValue();
4724         //Roo.form.TextField.superclass.initEvents.call(this);
4725         if(this.validationEvent == 'keyup'){
4726             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4727             this.inputEl().on('keyup', this.filterValidation, this);
4728         }
4729         else if(this.validationEvent !== false){
4730             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4731         }
4732         
4733         if(this.selectOnFocus){
4734             this.on("focus", this.preFocus, this);
4735             
4736         }
4737         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4738             this.inputEl().on("keypress", this.filterKeys, this);
4739         }
4740        /* if(this.grow){
4741             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4742             this.el.on("click", this.autoSize,  this);
4743         }
4744         */
4745         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4746             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4747         }
4748         
4749     },
4750     filterValidation : function(e){
4751         if(!e.isNavKeyPress()){
4752             this.validationTask.delay(this.validationDelay);
4753         }
4754     },
4755      /**
4756      * Validates the field value
4757      * @return {Boolean} True if the value is valid, else false
4758      */
4759     validate : function(){
4760         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4761         if(this.disabled || this.validateValue(this.getRawValue())){
4762             this.clearInvalid();
4763             return true;
4764         }
4765         return false;
4766     },
4767     
4768     
4769     /**
4770      * Validates a value according to the field's validation rules and marks the field as invalid
4771      * if the validation fails
4772      * @param {Mixed} value The value to validate
4773      * @return {Boolean} True if the value is valid, else false
4774      */
4775     validateValue : function(value){
4776         if(value.length < 1)  { // if it's blank
4777              if(this.allowBlank){
4778                 this.clearInvalid();
4779                 return true;
4780              }else{
4781                 this.markInvalid(this.blankText);
4782                 return false;
4783              }
4784         }
4785         if(value.length < this.minLength){
4786             this.markInvalid(String.format(this.minLengthText, this.minLength));
4787             return false;
4788         }
4789         if(value.length > this.maxLength){
4790             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4791             return false;
4792         }
4793         if(this.vtype){
4794             var vt = Roo.form.VTypes;
4795             if(!vt[this.vtype](value, this)){
4796                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4797                 return false;
4798             }
4799         }
4800         if(typeof this.validator == "function"){
4801             var msg = this.validator(value);
4802             if(msg !== true){
4803                 this.markInvalid(msg);
4804                 return false;
4805             }
4806         }
4807         if(this.regex && !this.regex.test(value)){
4808             this.markInvalid(this.regexText);
4809             return false;
4810         }
4811         return true;
4812     },
4813
4814     
4815     
4816      // private
4817     fireKey : function(e){
4818         //Roo.log('field ' + e.getKey());
4819         if(e.isNavKeyPress()){
4820             this.fireEvent("specialkey", this, e);
4821         }
4822     },
4823     focus : function (selectText){
4824         if(this.rendered){
4825             this.inputEl().focus();
4826             if(selectText === true){
4827                 this.inputEl().dom.select();
4828             }
4829         }
4830         return this;
4831     } ,
4832     
4833     onFocus : function(){
4834         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4835            // this.el.addClass(this.focusClass);
4836         }
4837         if(!this.hasFocus){
4838             this.hasFocus = true;
4839             this.startValue = this.getValue();
4840             this.fireEvent("focus", this);
4841         }
4842     },
4843     
4844     beforeBlur : Roo.emptyFn,
4845
4846     
4847     // private
4848     onBlur : function(){
4849         this.beforeBlur();
4850         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4851             //this.el.removeClass(this.focusClass);
4852         }
4853         this.hasFocus = false;
4854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4855             this.validate();
4856         }
4857         var v = this.getValue();
4858         if(String(v) !== String(this.startValue)){
4859             this.fireEvent('change', this, v, this.startValue);
4860         }
4861         this.fireEvent("blur", this);
4862     },
4863     
4864     /**
4865      * Resets the current field value to the originally loaded value and clears any validation messages
4866      */
4867     reset : function(){
4868         this.setValue(this.originalValue);
4869         this.clearInvalid();
4870     },
4871      /**
4872      * Returns the name of the field
4873      * @return {Mixed} name The name field
4874      */
4875     getName: function(){
4876         return this.name;
4877     },
4878      /**
4879      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4880      * @return {Mixed} value The field value
4881      */
4882     getValue : function(){
4883         return this.inputEl().getValue();
4884     },
4885     /**
4886      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4887      * @return {Mixed} value The field value
4888      */
4889     getRawValue : function(){
4890         var v = this.inputEl().getValue();
4891         
4892         return v;
4893     },
4894     
4895     /**
4896      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4897      * @param {Mixed} value The value to set
4898      */
4899     setRawValue : function(v){
4900         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4901     },
4902     
4903     selectText : function(start, end){
4904         var v = this.getRawValue();
4905         if(v.length > 0){
4906             start = start === undefined ? 0 : start;
4907             end = end === undefined ? v.length : end;
4908             var d = this.inputEl().dom;
4909             if(d.setSelectionRange){
4910                 d.setSelectionRange(start, end);
4911             }else if(d.createTextRange){
4912                 var range = d.createTextRange();
4913                 range.moveStart("character", start);
4914                 range.moveEnd("character", v.length-end);
4915                 range.select();
4916             }
4917         }
4918     },
4919     
4920     /**
4921      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4922      * @param {Mixed} value The value to set
4923      */
4924     setValue : function(v){
4925         this.value = v;
4926         if(this.rendered){
4927             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4928             this.validate();
4929         }
4930     },
4931     
4932     /*
4933     processValue : function(value){
4934         if(this.stripCharsRe){
4935             var newValue = value.replace(this.stripCharsRe, '');
4936             if(newValue !== value){
4937                 this.setRawValue(newValue);
4938                 return newValue;
4939             }
4940         }
4941         return value;
4942     },
4943   */
4944     preFocus : function(){
4945         
4946         if(this.selectOnFocus){
4947             this.inputEl().dom.select();
4948         }
4949     },
4950     filterKeys : function(e){
4951         var k = e.getKey();
4952         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4953             return;
4954         }
4955         var c = e.getCharCode(), cc = String.fromCharCode(c);
4956         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4957             return;
4958         }
4959         if(!this.maskRe.test(cc)){
4960             e.stopEvent();
4961         }
4962     },
4963      /**
4964      * Clear any invalid styles/messages for this field
4965      */
4966     clearInvalid : function(){
4967         
4968         if(!this.el || this.preventMark){ // not rendered
4969             return;
4970         }
4971         this.el.removeClass(this.invalidClass);
4972         /*
4973         switch(this.msgTarget){
4974             case 'qtip':
4975                 this.el.dom.qtip = '';
4976                 break;
4977             case 'title':
4978                 this.el.dom.title = '';
4979                 break;
4980             case 'under':
4981                 if(this.errorEl){
4982                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4983                 }
4984                 break;
4985             case 'side':
4986                 if(this.errorIcon){
4987                     this.errorIcon.dom.qtip = '';
4988                     this.errorIcon.hide();
4989                     this.un('resize', this.alignErrorIcon, this);
4990                 }
4991                 break;
4992             default:
4993                 var t = Roo.getDom(this.msgTarget);
4994                 t.innerHTML = '';
4995                 t.style.display = 'none';
4996                 break;
4997         }
4998         */
4999         this.fireEvent('valid', this);
5000     },
5001      /**
5002      * Mark this field as invalid
5003      * @param {String} msg The validation message
5004      */
5005     markInvalid : function(msg){
5006         if(!this.el  || this.preventMark){ // not rendered
5007             return;
5008         }
5009         this.el.addClass(this.invalidClass);
5010         /*
5011         msg = msg || this.invalidText;
5012         switch(this.msgTarget){
5013             case 'qtip':
5014                 this.el.dom.qtip = msg;
5015                 this.el.dom.qclass = 'x-form-invalid-tip';
5016                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5017                     Roo.QuickTips.enable();
5018                 }
5019                 break;
5020             case 'title':
5021                 this.el.dom.title = msg;
5022                 break;
5023             case 'under':
5024                 if(!this.errorEl){
5025                     var elp = this.el.findParent('.x-form-element', 5, true);
5026                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5027                     this.errorEl.setWidth(elp.getWidth(true)-20);
5028                 }
5029                 this.errorEl.update(msg);
5030                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5031                 break;
5032             case 'side':
5033                 if(!this.errorIcon){
5034                     var elp = this.el.findParent('.x-form-element', 5, true);
5035                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5036                 }
5037                 this.alignErrorIcon();
5038                 this.errorIcon.dom.qtip = msg;
5039                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5040                 this.errorIcon.show();
5041                 this.on('resize', this.alignErrorIcon, this);
5042                 break;
5043             default:
5044                 var t = Roo.getDom(this.msgTarget);
5045                 t.innerHTML = msg;
5046                 t.style.display = this.msgDisplay;
5047                 break;
5048         }
5049         */
5050         this.fireEvent('invalid', this, msg);
5051     },
5052     // private
5053     SafariOnKeyDown : function(event)
5054     {
5055         // this is a workaround for a password hang bug on chrome/ webkit.
5056         
5057         var isSelectAll = false;
5058         
5059         if(this.inputEl().dom.selectionEnd > 0){
5060             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5061         }
5062         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5063             event.preventDefault();
5064             this.setValue('');
5065             return;
5066         }
5067         
5068         if(isSelectAll){ // backspace and delete key
5069             
5070             event.preventDefault();
5071             // this is very hacky as keydown always get's upper case.
5072             //
5073             var cc = String.fromCharCode(event.getCharCode());
5074             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5075             
5076         }
5077     },
5078     adjustWidth : function(tag, w){
5079         tag = tag.toLowerCase();
5080         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5081             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5082                 if(tag == 'input'){
5083                     return w + 2;
5084                 }
5085                 if(tag == 'textarea'){
5086                     return w-2;
5087                 }
5088             }else if(Roo.isOpera){
5089                 if(tag == 'input'){
5090                     return w + 2;
5091                 }
5092                 if(tag == 'textarea'){
5093                     return w-2;
5094                 }
5095             }
5096         }
5097         return w;
5098     }
5099     
5100 });
5101
5102  
5103 /*
5104  * - LGPL
5105  *
5106  * Input
5107  * 
5108  */
5109
5110 /**
5111  * @class Roo.bootstrap.TextArea
5112  * @extends Roo.bootstrap.Input
5113  * Bootstrap TextArea class
5114  * @cfg {Number} cols Specifies the visible width of a text area
5115  * @cfg {Number} rows Specifies the visible number of lines in a text area
5116  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5117  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5118  * @cfg {string} html text
5119  * 
5120  * @constructor
5121  * Create a new TextArea
5122  * @param {Object} config The config object
5123  */
5124
5125 Roo.bootstrap.TextArea = function(config){
5126     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5127    
5128 };
5129
5130 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5131      
5132     cols : false,
5133     rows : 5,
5134     readOnly : false,
5135     warp : 'soft',
5136     resize : false,
5137     value: false,
5138     html: false,
5139     
5140     getAutoCreate : function(){
5141         
5142         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5143         
5144         var id = Roo.id();
5145         
5146         var cfg = {};
5147         
5148         var input =  {
5149             tag: 'textarea',
5150             id : id,
5151             warp : this.warp,
5152             rows : this.rows,
5153             value : this.value || '',
5154             html: this.html || '',
5155             cls : 'form-control',
5156             placeholder : this.placeholder || '' 
5157             
5158         };
5159         
5160         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5161             input.maxLength = this.maxLength;
5162         }
5163         
5164         if(this.resize){
5165             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5166         }
5167         
5168         if(this.cols){
5169             input.cols = this.cols;
5170         }
5171         
5172         if (this.readOnly) {
5173             input.readonly = true;
5174         }
5175         
5176         if (this.name) {
5177             input.name = this.name;
5178         }
5179         
5180         if (this.size) {
5181             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5182         }
5183         
5184         var settings=this;
5185         ['xs','sm','md','lg'].map(function(size){
5186             if (settings[size]) {
5187                 cfg.cls += ' col-' + size + '-' + settings[size];
5188             }
5189         });
5190         
5191         var inputblock = input;
5192         
5193         if (this.before || this.after) {
5194             
5195             inputblock = {
5196                 cls : 'input-group',
5197                 cn :  [] 
5198             };
5199             if (this.before) {
5200                 inputblock.cn.push({
5201                     tag :'span',
5202                     cls : 'input-group-addon',
5203                     html : this.before
5204                 });
5205             }
5206             inputblock.cn.push(input);
5207             if (this.after) {
5208                 inputblock.cn.push({
5209                     tag :'span',
5210                     cls : 'input-group-addon',
5211                     html : this.after
5212                 });
5213             }
5214             
5215         }
5216         
5217         if (align ==='left' && this.fieldLabel.length) {
5218                 Roo.log("left and has label");
5219                 cfg.cn = [
5220                     
5221                     {
5222                         tag: 'label',
5223                         'for' :  id,
5224                         cls : 'control-label col-sm-' + this.labelWidth,
5225                         html : this.fieldLabel
5226                         
5227                     },
5228                     {
5229                         cls : "col-sm-" + (12 - this.labelWidth), 
5230                         cn: [
5231                             inputblock
5232                         ]
5233                     }
5234                     
5235                 ];
5236         } else if ( this.fieldLabel.length) {
5237                 Roo.log(" label");
5238                  cfg.cn = [
5239                    
5240                     {
5241                         tag: 'label',
5242                         //cls : 'input-group-addon',
5243                         html : this.fieldLabel
5244                         
5245                     },
5246                     
5247                     inputblock
5248                     
5249                 ];
5250
5251         } else {
5252             
5253                    Roo.log(" no label && no align");
5254                 cfg.cn = [
5255                     
5256                         inputblock
5257                     
5258                 ];
5259                 
5260                 
5261         }
5262         
5263         if (this.disabled) {
5264             input.disabled=true;
5265         }
5266         
5267         return cfg;
5268         
5269     },
5270     /**
5271      * return the real textarea element.
5272      */
5273     inputEl: function ()
5274     {
5275         return this.el.select('textarea.form-control',true).first();
5276     }
5277 });
5278
5279  
5280 /*
5281  * - LGPL
5282  *
5283  * trigger field - base class for combo..
5284  * 
5285  */
5286  
5287 /**
5288  * @class Roo.bootstrap.TriggerField
5289  * @extends Roo.bootstrap.Input
5290  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5291  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5292  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5293  * for which you can provide a custom implementation.  For example:
5294  * <pre><code>
5295 var trigger = new Roo.bootstrap.TriggerField();
5296 trigger.onTriggerClick = myTriggerFn;
5297 trigger.applyTo('my-field');
5298 </code></pre>
5299  *
5300  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5301  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5302  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5303  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5304  * @constructor
5305  * Create a new TriggerField.
5306  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5307  * to the base TextField)
5308  */
5309 Roo.bootstrap.TriggerField = function(config){
5310     this.mimicing = false;
5311     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5312 };
5313
5314 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5315     /**
5316      * @cfg {String} triggerClass A CSS class to apply to the trigger
5317      */
5318      /**
5319      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5320      */
5321     hideTrigger:false,
5322
5323     /** @cfg {Boolean} grow @hide */
5324     /** @cfg {Number} growMin @hide */
5325     /** @cfg {Number} growMax @hide */
5326
5327     /**
5328      * @hide 
5329      * @method
5330      */
5331     autoSize: Roo.emptyFn,
5332     // private
5333     monitorTab : true,
5334     // private
5335     deferHeight : true,
5336
5337     
5338     actionMode : 'wrap',
5339     
5340     
5341     
5342     getAutoCreate : function(){
5343        
5344         var parent = this.parent();
5345         
5346         var align = this.parentLabelAlign();
5347         
5348         var id = Roo.id();
5349         
5350         var cfg = {
5351             cls: 'form-group' //input-group
5352         };
5353         
5354         
5355         var input =  {
5356             tag: 'input',
5357             id : id,
5358             type : this.inputType,
5359             cls : 'form-control',
5360             autocomplete: 'off',
5361             placeholder : this.placeholder || '' 
5362             
5363         };
5364         if (this.name) {
5365             input.name = this.name;
5366         }
5367         if (this.size) {
5368             input.cls += ' input-' + this.size;
5369         }
5370         
5371         if (this.disabled) {
5372             input.disabled=true;
5373         }
5374         
5375         var inputblock = input;
5376         
5377         if (this.before || this.after) {
5378             
5379             inputblock = {
5380                 cls : 'input-group',
5381                 cn :  [] 
5382             };
5383             if (this.before) {
5384                 inputblock.cn.push({
5385                     tag :'span',
5386                     cls : 'input-group-addon',
5387                     html : this.before
5388                 });
5389             }
5390             inputblock.cn.push(input);
5391             if (this.after) {
5392                 inputblock.cn.push({
5393                     tag :'span',
5394                     cls : 'input-group-addon',
5395                     html : this.after
5396                 });
5397             }
5398             
5399         };
5400         
5401         var box = {
5402             tag: 'div',
5403             cn: [
5404                 {
5405                     tag: 'input',
5406                     type : 'hidden',
5407                     cls: 'form-hidden-field'
5408                 },
5409                 inputblock
5410             ]
5411             
5412         };
5413         
5414         if(this.multiple){
5415             Roo.log('multiple');
5416             
5417             box = {
5418                 tag: 'div',
5419                 cn: [
5420                     {
5421                         tag: 'input',
5422                         type : 'hidden',
5423                         cls: 'form-hidden-field'
5424                     },
5425                     {
5426                         tag: 'ul',
5427                         cls: 'select2-choices',
5428                         cn:[
5429                             {
5430                                 tag: 'li',
5431                                 cls: 'select2-search-field',
5432                                 cn: [
5433
5434                                     inputblock
5435                                 ]
5436                             }
5437                         ]
5438                     }
5439                 ]
5440             }
5441         };
5442         
5443         var combobox = {
5444             cls: 'select2-container input-group',
5445             cn: [
5446                 box,
5447                 {
5448                     tag: 'ul',
5449                     cls: 'typeahead typeahead-long dropdown-menu',
5450                     style: 'display:none'
5451                 }
5452             ]
5453         };
5454         
5455         if(!this.multiple){
5456             combobox.cn.push({
5457                 tag :'span',
5458                 cls : 'input-group-addon btn dropdown-toggle',
5459                 cn : [
5460                     {
5461                         tag: 'span',
5462                         cls: 'caret'
5463                     },
5464                     {
5465                         tag: 'span',
5466                         cls: 'combobox-clear',
5467                         cn  : [
5468                             {
5469                                 tag : 'i',
5470                                 cls: 'icon-remove'
5471                             }
5472                         ]
5473                     }
5474                 ]
5475
5476             })
5477         }
5478         
5479         if(this.multiple){
5480             combobox.cls += ' select2-container-multi';
5481         }
5482         
5483         if (align ==='left' && this.fieldLabel.length) {
5484             
5485                 Roo.log("left and has label");
5486                 cfg.cn = [
5487                     
5488                     {
5489                         tag: 'label',
5490                         'for' :  id,
5491                         cls : 'control-label col-sm-' + this.labelWidth,
5492                         html : this.fieldLabel
5493                         
5494                     },
5495                     {
5496                         cls : "col-sm-" + (12 - this.labelWidth), 
5497                         cn: [
5498                             combobox
5499                         ]
5500                     }
5501                     
5502                 ];
5503         } else if ( this.fieldLabel.length) {
5504                 Roo.log(" label");
5505                  cfg.cn = [
5506                    
5507                     {
5508                         tag: 'label',
5509                         //cls : 'input-group-addon',
5510                         html : this.fieldLabel
5511                         
5512                     },
5513                     
5514                     combobox
5515                     
5516                 ];
5517
5518         } else {
5519             
5520                 Roo.log(" no label && no align");
5521                 cfg = combobox
5522                      
5523                 
5524         }
5525          
5526         var settings=this;
5527         ['xs','sm','md','lg'].map(function(size){
5528             if (settings[size]) {
5529                 cfg.cls += ' col-' + size + '-' + settings[size];
5530             }
5531         });
5532         
5533         return cfg;
5534         
5535     },
5536     
5537     
5538     
5539     // private
5540     onResize : function(w, h){
5541 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5542 //        if(typeof w == 'number'){
5543 //            var x = w - this.trigger.getWidth();
5544 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5545 //            this.trigger.setStyle('left', x+'px');
5546 //        }
5547     },
5548
5549     // private
5550     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5551
5552     // private
5553     getResizeEl : function(){
5554         return this.inputEl();
5555     },
5556
5557     // private
5558     getPositionEl : function(){
5559         return this.inputEl();
5560     },
5561
5562     // private
5563     alignErrorIcon : function(){
5564         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5565     },
5566
5567     // private
5568     initEvents : function(){
5569         
5570         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5571         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5572         if(!this.multiple){
5573             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5574             if(this.hideTrigger){
5575                 this.trigger.setDisplayed(false);
5576             }
5577             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5578         }
5579         
5580         if(this.multiple){
5581             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5582         }
5583         
5584         //this.trigger.addClassOnOver('x-form-trigger-over');
5585         //this.trigger.addClassOnClick('x-form-trigger-click');
5586         
5587         //if(!this.width){
5588         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5589         //}
5590     },
5591
5592     // private
5593     initTrigger : function(){
5594        
5595     },
5596
5597     // private
5598     onDestroy : function(){
5599         if(this.trigger){
5600             this.trigger.removeAllListeners();
5601           //  this.trigger.remove();
5602         }
5603         //if(this.wrap){
5604         //    this.wrap.remove();
5605         //}
5606         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5607     },
5608
5609     // private
5610     onFocus : function(){
5611         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5612         /*
5613         if(!this.mimicing){
5614             this.wrap.addClass('x-trigger-wrap-focus');
5615             this.mimicing = true;
5616             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5617             if(this.monitorTab){
5618                 this.el.on("keydown", this.checkTab, this);
5619             }
5620         }
5621         */
5622     },
5623
5624     // private
5625     checkTab : function(e){
5626         if(e.getKey() == e.TAB){
5627             this.triggerBlur();
5628         }
5629     },
5630
5631     // private
5632     onBlur : function(){
5633         // do nothing
5634     },
5635
5636     // private
5637     mimicBlur : function(e, t){
5638         /*
5639         if(!this.wrap.contains(t) && this.validateBlur()){
5640             this.triggerBlur();
5641         }
5642         */
5643     },
5644
5645     // private
5646     triggerBlur : function(){
5647         this.mimicing = false;
5648         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5649         if(this.monitorTab){
5650             this.el.un("keydown", this.checkTab, this);
5651         }
5652         //this.wrap.removeClass('x-trigger-wrap-focus');
5653         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5654     },
5655
5656     // private
5657     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5658     validateBlur : function(e, t){
5659         return true;
5660     },
5661
5662     // private
5663     onDisable : function(){
5664         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5665         //if(this.wrap){
5666         //    this.wrap.addClass('x-item-disabled');
5667         //}
5668     },
5669
5670     // private
5671     onEnable : function(){
5672         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5673         //if(this.wrap){
5674         //    this.el.removeClass('x-item-disabled');
5675         //}
5676     },
5677
5678     // private
5679     onShow : function(){
5680         var ae = this.getActionEl();
5681         
5682         if(ae){
5683             ae.dom.style.display = '';
5684             ae.dom.style.visibility = 'visible';
5685         }
5686     },
5687
5688     // private
5689     
5690     onHide : function(){
5691         var ae = this.getActionEl();
5692         ae.dom.style.display = 'none';
5693     },
5694
5695     /**
5696      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5697      * by an implementing function.
5698      * @method
5699      * @param {EventObject} e
5700      */
5701     onTriggerClick : Roo.emptyFn
5702 });
5703  /*
5704  * Based on:
5705  * Ext JS Library 1.1.1
5706  * Copyright(c) 2006-2007, Ext JS, LLC.
5707  *
5708  * Originally Released Under LGPL - original licence link has changed is not relivant.
5709  *
5710  * Fork - LGPL
5711  * <script type="text/javascript">
5712  */
5713
5714
5715 /**
5716  * @class Roo.data.SortTypes
5717  * @singleton
5718  * Defines the default sorting (casting?) comparison functions used when sorting data.
5719  */
5720 Roo.data.SortTypes = {
5721     /**
5722      * Default sort that does nothing
5723      * @param {Mixed} s The value being converted
5724      * @return {Mixed} The comparison value
5725      */
5726     none : function(s){
5727         return s;
5728     },
5729     
5730     /**
5731      * The regular expression used to strip tags
5732      * @type {RegExp}
5733      * @property
5734      */
5735     stripTagsRE : /<\/?[^>]+>/gi,
5736     
5737     /**
5738      * Strips all HTML tags to sort on text only
5739      * @param {Mixed} s The value being converted
5740      * @return {String} The comparison value
5741      */
5742     asText : function(s){
5743         return String(s).replace(this.stripTagsRE, "");
5744     },
5745     
5746     /**
5747      * Strips all HTML tags to sort on text only - Case insensitive
5748      * @param {Mixed} s The value being converted
5749      * @return {String} The comparison value
5750      */
5751     asUCText : function(s){
5752         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5753     },
5754     
5755     /**
5756      * Case insensitive string
5757      * @param {Mixed} s The value being converted
5758      * @return {String} The comparison value
5759      */
5760     asUCString : function(s) {
5761         return String(s).toUpperCase();
5762     },
5763     
5764     /**
5765      * Date sorting
5766      * @param {Mixed} s The value being converted
5767      * @return {Number} The comparison value
5768      */
5769     asDate : function(s) {
5770         if(!s){
5771             return 0;
5772         }
5773         if(s instanceof Date){
5774             return s.getTime();
5775         }
5776         return Date.parse(String(s));
5777     },
5778     
5779     /**
5780      * Float sorting
5781      * @param {Mixed} s The value being converted
5782      * @return {Float} The comparison value
5783      */
5784     asFloat : function(s) {
5785         var val = parseFloat(String(s).replace(/,/g, ""));
5786         if(isNaN(val)) val = 0;
5787         return val;
5788     },
5789     
5790     /**
5791      * Integer sorting
5792      * @param {Mixed} s The value being converted
5793      * @return {Number} The comparison value
5794      */
5795     asInt : function(s) {
5796         var val = parseInt(String(s).replace(/,/g, ""));
5797         if(isNaN(val)) val = 0;
5798         return val;
5799     }
5800 };/*
5801  * Based on:
5802  * Ext JS Library 1.1.1
5803  * Copyright(c) 2006-2007, Ext JS, LLC.
5804  *
5805  * Originally Released Under LGPL - original licence link has changed is not relivant.
5806  *
5807  * Fork - LGPL
5808  * <script type="text/javascript">
5809  */
5810
5811 /**
5812 * @class Roo.data.Record
5813  * Instances of this class encapsulate both record <em>definition</em> information, and record
5814  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5815  * to access Records cached in an {@link Roo.data.Store} object.<br>
5816  * <p>
5817  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5818  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5819  * objects.<br>
5820  * <p>
5821  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5822  * @constructor
5823  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5824  * {@link #create}. The parameters are the same.
5825  * @param {Array} data An associative Array of data values keyed by the field name.
5826  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5827  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5828  * not specified an integer id is generated.
5829  */
5830 Roo.data.Record = function(data, id){
5831     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5832     this.data = data;
5833 };
5834
5835 /**
5836  * Generate a constructor for a specific record layout.
5837  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5838  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5839  * Each field definition object may contain the following properties: <ul>
5840  * <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,
5841  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5842  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5843  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5844  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5845  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5846  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5847  * this may be omitted.</p></li>
5848  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5849  * <ul><li>auto (Default, implies no conversion)</li>
5850  * <li>string</li>
5851  * <li>int</li>
5852  * <li>float</li>
5853  * <li>boolean</li>
5854  * <li>date</li></ul></p></li>
5855  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5856  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5857  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5858  * by the Reader into an object that will be stored in the Record. It is passed the
5859  * following parameters:<ul>
5860  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5861  * </ul></p></li>
5862  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5863  * </ul>
5864  * <br>usage:<br><pre><code>
5865 var TopicRecord = Roo.data.Record.create(
5866     {name: 'title', mapping: 'topic_title'},
5867     {name: 'author', mapping: 'username'},
5868     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5869     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5870     {name: 'lastPoster', mapping: 'user2'},
5871     {name: 'excerpt', mapping: 'post_text'}
5872 );
5873
5874 var myNewRecord = new TopicRecord({
5875     title: 'Do my job please',
5876     author: 'noobie',
5877     totalPosts: 1,
5878     lastPost: new Date(),
5879     lastPoster: 'Animal',
5880     excerpt: 'No way dude!'
5881 });
5882 myStore.add(myNewRecord);
5883 </code></pre>
5884  * @method create
5885  * @static
5886  */
5887 Roo.data.Record.create = function(o){
5888     var f = function(){
5889         f.superclass.constructor.apply(this, arguments);
5890     };
5891     Roo.extend(f, Roo.data.Record);
5892     var p = f.prototype;
5893     p.fields = new Roo.util.MixedCollection(false, function(field){
5894         return field.name;
5895     });
5896     for(var i = 0, len = o.length; i < len; i++){
5897         p.fields.add(new Roo.data.Field(o[i]));
5898     }
5899     f.getField = function(name){
5900         return p.fields.get(name);  
5901     };
5902     return f;
5903 };
5904
5905 Roo.data.Record.AUTO_ID = 1000;
5906 Roo.data.Record.EDIT = 'edit';
5907 Roo.data.Record.REJECT = 'reject';
5908 Roo.data.Record.COMMIT = 'commit';
5909
5910 Roo.data.Record.prototype = {
5911     /**
5912      * Readonly flag - true if this record has been modified.
5913      * @type Boolean
5914      */
5915     dirty : false,
5916     editing : false,
5917     error: null,
5918     modified: null,
5919
5920     // private
5921     join : function(store){
5922         this.store = store;
5923     },
5924
5925     /**
5926      * Set the named field to the specified value.
5927      * @param {String} name The name of the field to set.
5928      * @param {Object} value The value to set the field to.
5929      */
5930     set : function(name, value){
5931         if(this.data[name] == value){
5932             return;
5933         }
5934         this.dirty = true;
5935         if(!this.modified){
5936             this.modified = {};
5937         }
5938         if(typeof this.modified[name] == 'undefined'){
5939             this.modified[name] = this.data[name];
5940         }
5941         this.data[name] = value;
5942         if(!this.editing && this.store){
5943             this.store.afterEdit(this);
5944         }       
5945     },
5946
5947     /**
5948      * Get the value of the named field.
5949      * @param {String} name The name of the field to get the value of.
5950      * @return {Object} The value of the field.
5951      */
5952     get : function(name){
5953         return this.data[name]; 
5954     },
5955
5956     // private
5957     beginEdit : function(){
5958         this.editing = true;
5959         this.modified = {}; 
5960     },
5961
5962     // private
5963     cancelEdit : function(){
5964         this.editing = false;
5965         delete this.modified;
5966     },
5967
5968     // private
5969     endEdit : function(){
5970         this.editing = false;
5971         if(this.dirty && this.store){
5972             this.store.afterEdit(this);
5973         }
5974     },
5975
5976     /**
5977      * Usually called by the {@link Roo.data.Store} which owns the Record.
5978      * Rejects all changes made to the Record since either creation, or the last commit operation.
5979      * Modified fields are reverted to their original values.
5980      * <p>
5981      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5982      * of reject operations.
5983      */
5984     reject : function(){
5985         var m = this.modified;
5986         for(var n in m){
5987             if(typeof m[n] != "function"){
5988                 this.data[n] = m[n];
5989             }
5990         }
5991         this.dirty = false;
5992         delete this.modified;
5993         this.editing = false;
5994         if(this.store){
5995             this.store.afterReject(this);
5996         }
5997     },
5998
5999     /**
6000      * Usually called by the {@link Roo.data.Store} which owns the Record.
6001      * Commits all changes made to the Record since either creation, or the last commit operation.
6002      * <p>
6003      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6004      * of commit operations.
6005      */
6006     commit : function(){
6007         this.dirty = false;
6008         delete this.modified;
6009         this.editing = false;
6010         if(this.store){
6011             this.store.afterCommit(this);
6012         }
6013     },
6014
6015     // private
6016     hasError : function(){
6017         return this.error != null;
6018     },
6019
6020     // private
6021     clearError : function(){
6022         this.error = null;
6023     },
6024
6025     /**
6026      * Creates a copy of this record.
6027      * @param {String} id (optional) A new record id if you don't want to use this record's id
6028      * @return {Record}
6029      */
6030     copy : function(newId) {
6031         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6032     }
6033 };/*
6034  * Based on:
6035  * Ext JS Library 1.1.1
6036  * Copyright(c) 2006-2007, Ext JS, LLC.
6037  *
6038  * Originally Released Under LGPL - original licence link has changed is not relivant.
6039  *
6040  * Fork - LGPL
6041  * <script type="text/javascript">
6042  */
6043
6044
6045
6046 /**
6047  * @class Roo.data.Store
6048  * @extends Roo.util.Observable
6049  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6050  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6051  * <p>
6052  * 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
6053  * has no knowledge of the format of the data returned by the Proxy.<br>
6054  * <p>
6055  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6056  * instances from the data object. These records are cached and made available through accessor functions.
6057  * @constructor
6058  * Creates a new Store.
6059  * @param {Object} config A config object containing the objects needed for the Store to access data,
6060  * and read the data into Records.
6061  */
6062 Roo.data.Store = function(config){
6063     this.data = new Roo.util.MixedCollection(false);
6064     this.data.getKey = function(o){
6065         return o.id;
6066     };
6067     this.baseParams = {};
6068     // private
6069     this.paramNames = {
6070         "start" : "start",
6071         "limit" : "limit",
6072         "sort" : "sort",
6073         "dir" : "dir",
6074         "multisort" : "_multisort"
6075     };
6076
6077     if(config && config.data){
6078         this.inlineData = config.data;
6079         delete config.data;
6080     }
6081
6082     Roo.apply(this, config);
6083     
6084     if(this.reader){ // reader passed
6085         this.reader = Roo.factory(this.reader, Roo.data);
6086         this.reader.xmodule = this.xmodule || false;
6087         if(!this.recordType){
6088             this.recordType = this.reader.recordType;
6089         }
6090         if(this.reader.onMetaChange){
6091             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6092         }
6093     }
6094
6095     if(this.recordType){
6096         this.fields = this.recordType.prototype.fields;
6097     }
6098     this.modified = [];
6099
6100     this.addEvents({
6101         /**
6102          * @event datachanged
6103          * Fires when the data cache has changed, and a widget which is using this Store
6104          * as a Record cache should refresh its view.
6105          * @param {Store} this
6106          */
6107         datachanged : true,
6108         /**
6109          * @event metachange
6110          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6111          * @param {Store} this
6112          * @param {Object} meta The JSON metadata
6113          */
6114         metachange : true,
6115         /**
6116          * @event add
6117          * Fires when Records have been added to the Store
6118          * @param {Store} this
6119          * @param {Roo.data.Record[]} records The array of Records added
6120          * @param {Number} index The index at which the record(s) were added
6121          */
6122         add : true,
6123         /**
6124          * @event remove
6125          * Fires when a Record has been removed from the Store
6126          * @param {Store} this
6127          * @param {Roo.data.Record} record The Record that was removed
6128          * @param {Number} index The index at which the record was removed
6129          */
6130         remove : true,
6131         /**
6132          * @event update
6133          * Fires when a Record has been updated
6134          * @param {Store} this
6135          * @param {Roo.data.Record} record The Record that was updated
6136          * @param {String} operation The update operation being performed.  Value may be one of:
6137          * <pre><code>
6138  Roo.data.Record.EDIT
6139  Roo.data.Record.REJECT
6140  Roo.data.Record.COMMIT
6141          * </code></pre>
6142          */
6143         update : true,
6144         /**
6145          * @event clear
6146          * Fires when the data cache has been cleared.
6147          * @param {Store} this
6148          */
6149         clear : true,
6150         /**
6151          * @event beforeload
6152          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6153          * the load action will be canceled.
6154          * @param {Store} this
6155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6156          */
6157         beforeload : true,
6158         /**
6159          * @event beforeloadadd
6160          * Fires after a new set of Records has been loaded.
6161          * @param {Store} this
6162          * @param {Roo.data.Record[]} records The Records that were loaded
6163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6164          */
6165         beforeloadadd : true,
6166         /**
6167          * @event load
6168          * Fires after a new set of Records has been loaded, before they are added to the store.
6169          * @param {Store} this
6170          * @param {Roo.data.Record[]} records The Records that were loaded
6171          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6172          * @params {Object} return from reader
6173          */
6174         load : true,
6175         /**
6176          * @event loadexception
6177          * Fires if an exception occurs in the Proxy during loading.
6178          * Called with the signature of the Proxy's "loadexception" event.
6179          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6180          * 
6181          * @param {Proxy} 
6182          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6183          * @param {Object} load options 
6184          * @param {Object} jsonData from your request (normally this contains the Exception)
6185          */
6186         loadexception : true
6187     });
6188     
6189     if(this.proxy){
6190         this.proxy = Roo.factory(this.proxy, Roo.data);
6191         this.proxy.xmodule = this.xmodule || false;
6192         this.relayEvents(this.proxy,  ["loadexception"]);
6193     }
6194     this.sortToggle = {};
6195     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6196
6197     Roo.data.Store.superclass.constructor.call(this);
6198
6199     if(this.inlineData){
6200         this.loadData(this.inlineData);
6201         delete this.inlineData;
6202     }
6203 };
6204
6205 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6206      /**
6207     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6208     * without a remote query - used by combo/forms at present.
6209     */
6210     
6211     /**
6212     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6213     */
6214     /**
6215     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6216     */
6217     /**
6218     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6219     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6220     */
6221     /**
6222     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6223     * on any HTTP request
6224     */
6225     /**
6226     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6227     */
6228     /**
6229     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6230     */
6231     multiSort: false,
6232     /**
6233     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6234     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6235     */
6236     remoteSort : false,
6237
6238     /**
6239     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6240      * loaded or when a record is removed. (defaults to false).
6241     */
6242     pruneModifiedRecords : false,
6243
6244     // private
6245     lastOptions : null,
6246
6247     /**
6248      * Add Records to the Store and fires the add event.
6249      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6250      */
6251     add : function(records){
6252         records = [].concat(records);
6253         for(var i = 0, len = records.length; i < len; i++){
6254             records[i].join(this);
6255         }
6256         var index = this.data.length;
6257         this.data.addAll(records);
6258         this.fireEvent("add", this, records, index);
6259     },
6260
6261     /**
6262      * Remove a Record from the Store and fires the remove event.
6263      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6264      */
6265     remove : function(record){
6266         var index = this.data.indexOf(record);
6267         this.data.removeAt(index);
6268         if(this.pruneModifiedRecords){
6269             this.modified.remove(record);
6270         }
6271         this.fireEvent("remove", this, record, index);
6272     },
6273
6274     /**
6275      * Remove all Records from the Store and fires the clear event.
6276      */
6277     removeAll : function(){
6278         this.data.clear();
6279         if(this.pruneModifiedRecords){
6280             this.modified = [];
6281         }
6282         this.fireEvent("clear", this);
6283     },
6284
6285     /**
6286      * Inserts Records to the Store at the given index and fires the add event.
6287      * @param {Number} index The start index at which to insert the passed Records.
6288      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6289      */
6290     insert : function(index, records){
6291         records = [].concat(records);
6292         for(var i = 0, len = records.length; i < len; i++){
6293             this.data.insert(index, records[i]);
6294             records[i].join(this);
6295         }
6296         this.fireEvent("add", this, records, index);
6297     },
6298
6299     /**
6300      * Get the index within the cache of the passed Record.
6301      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6302      * @return {Number} The index of the passed Record. Returns -1 if not found.
6303      */
6304     indexOf : function(record){
6305         return this.data.indexOf(record);
6306     },
6307
6308     /**
6309      * Get the index within the cache of the Record with the passed id.
6310      * @param {String} id The id of the Record to find.
6311      * @return {Number} The index of the Record. Returns -1 if not found.
6312      */
6313     indexOfId : function(id){
6314         return this.data.indexOfKey(id);
6315     },
6316
6317     /**
6318      * Get the Record with the specified id.
6319      * @param {String} id The id of the Record to find.
6320      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6321      */
6322     getById : function(id){
6323         return this.data.key(id);
6324     },
6325
6326     /**
6327      * Get the Record at the specified index.
6328      * @param {Number} index The index of the Record to find.
6329      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6330      */
6331     getAt : function(index){
6332         return this.data.itemAt(index);
6333     },
6334
6335     /**
6336      * Returns a range of Records between specified indices.
6337      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6338      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6339      * @return {Roo.data.Record[]} An array of Records
6340      */
6341     getRange : function(start, end){
6342         return this.data.getRange(start, end);
6343     },
6344
6345     // private
6346     storeOptions : function(o){
6347         o = Roo.apply({}, o);
6348         delete o.callback;
6349         delete o.scope;
6350         this.lastOptions = o;
6351     },
6352
6353     /**
6354      * Loads the Record cache from the configured Proxy using the configured Reader.
6355      * <p>
6356      * If using remote paging, then the first load call must specify the <em>start</em>
6357      * and <em>limit</em> properties in the options.params property to establish the initial
6358      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6359      * <p>
6360      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6361      * and this call will return before the new data has been loaded. Perform any post-processing
6362      * in a callback function, or in a "load" event handler.</strong>
6363      * <p>
6364      * @param {Object} options An object containing properties which control loading options:<ul>
6365      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6366      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6367      * passed the following arguments:<ul>
6368      * <li>r : Roo.data.Record[]</li>
6369      * <li>options: Options object from the load call</li>
6370      * <li>success: Boolean success indicator</li></ul></li>
6371      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6372      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6373      * </ul>
6374      */
6375     load : function(options){
6376         options = options || {};
6377         if(this.fireEvent("beforeload", this, options) !== false){
6378             this.storeOptions(options);
6379             var p = Roo.apply(options.params || {}, this.baseParams);
6380             // if meta was not loaded from remote source.. try requesting it.
6381             if (!this.reader.metaFromRemote) {
6382                 p._requestMeta = 1;
6383             }
6384             if(this.sortInfo && this.remoteSort){
6385                 var pn = this.paramNames;
6386                 p[pn["sort"]] = this.sortInfo.field;
6387                 p[pn["dir"]] = this.sortInfo.direction;
6388             }
6389             if (this.multiSort) {
6390                 var pn = this.paramNames;
6391                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6392             }
6393             
6394             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6395         }
6396     },
6397
6398     /**
6399      * Reloads the Record cache from the configured Proxy using the configured Reader and
6400      * the options from the last load operation performed.
6401      * @param {Object} options (optional) An object containing properties which may override the options
6402      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6403      * the most recently used options are reused).
6404      */
6405     reload : function(options){
6406         this.load(Roo.applyIf(options||{}, this.lastOptions));
6407     },
6408
6409     // private
6410     // Called as a callback by the Reader during a load operation.
6411     loadRecords : function(o, options, success){
6412         if(!o || success === false){
6413             if(success !== false){
6414                 this.fireEvent("load", this, [], options, o);
6415             }
6416             if(options.callback){
6417                 options.callback.call(options.scope || this, [], options, false);
6418             }
6419             return;
6420         }
6421         // if data returned failure - throw an exception.
6422         if (o.success === false) {
6423             // show a message if no listener is registered.
6424             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6425                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6426             }
6427             // loadmask wil be hooked into this..
6428             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6429             return;
6430         }
6431         var r = o.records, t = o.totalRecords || r.length;
6432         
6433         this.fireEvent("beforeloadadd", this, r, options, o);
6434         
6435         if(!options || options.add !== true){
6436             if(this.pruneModifiedRecords){
6437                 this.modified = [];
6438             }
6439             for(var i = 0, len = r.length; i < len; i++){
6440                 r[i].join(this);
6441             }
6442             if(this.snapshot){
6443                 this.data = this.snapshot;
6444                 delete this.snapshot;
6445             }
6446             this.data.clear();
6447             this.data.addAll(r);
6448             this.totalLength = t;
6449             this.applySort();
6450             this.fireEvent("datachanged", this);
6451         }else{
6452             this.totalLength = Math.max(t, this.data.length+r.length);
6453             this.add(r);
6454         }
6455         this.fireEvent("load", this, r, options, o);
6456         if(options.callback){
6457             options.callback.call(options.scope || this, r, options, true);
6458         }
6459     },
6460
6461
6462     /**
6463      * Loads data from a passed data block. A Reader which understands the format of the data
6464      * must have been configured in the constructor.
6465      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6466      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6467      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6468      */
6469     loadData : function(o, append){
6470         var r = this.reader.readRecords(o);
6471         this.loadRecords(r, {add: append}, true);
6472     },
6473
6474     /**
6475      * Gets the number of cached records.
6476      * <p>
6477      * <em>If using paging, this may not be the total size of the dataset. If the data object
6478      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6479      * the data set size</em>
6480      */
6481     getCount : function(){
6482         return this.data.length || 0;
6483     },
6484
6485     /**
6486      * Gets the total number of records in the dataset as returned by the server.
6487      * <p>
6488      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6489      * the dataset size</em>
6490      */
6491     getTotalCount : function(){
6492         return this.totalLength || 0;
6493     },
6494
6495     /**
6496      * Returns the sort state of the Store as an object with two properties:
6497      * <pre><code>
6498  field {String} The name of the field by which the Records are sorted
6499  direction {String} The sort order, "ASC" or "DESC"
6500      * </code></pre>
6501      */
6502     getSortState : function(){
6503         return this.sortInfo;
6504     },
6505
6506     // private
6507     applySort : function(){
6508         if(this.sortInfo && !this.remoteSort){
6509             var s = this.sortInfo, f = s.field;
6510             var st = this.fields.get(f).sortType;
6511             var fn = function(r1, r2){
6512                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6513                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6514             };
6515             this.data.sort(s.direction, fn);
6516             if(this.snapshot && this.snapshot != this.data){
6517                 this.snapshot.sort(s.direction, fn);
6518             }
6519         }
6520     },
6521
6522     /**
6523      * Sets the default sort column and order to be used by the next load operation.
6524      * @param {String} fieldName The name of the field to sort by.
6525      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6526      */
6527     setDefaultSort : function(field, dir){
6528         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6529     },
6530
6531     /**
6532      * Sort the Records.
6533      * If remote sorting is used, the sort is performed on the server, and the cache is
6534      * reloaded. If local sorting is used, the cache is sorted internally.
6535      * @param {String} fieldName The name of the field to sort by.
6536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6537      */
6538     sort : function(fieldName, dir){
6539         var f = this.fields.get(fieldName);
6540         if(!dir){
6541             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6542             
6543             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6544                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6545             }else{
6546                 dir = f.sortDir;
6547             }
6548         }
6549         this.sortToggle[f.name] = dir;
6550         this.sortInfo = {field: f.name, direction: dir};
6551         if(!this.remoteSort){
6552             this.applySort();
6553             this.fireEvent("datachanged", this);
6554         }else{
6555             this.load(this.lastOptions);
6556         }
6557     },
6558
6559     /**
6560      * Calls the specified function for each of the Records in the cache.
6561      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6562      * Returning <em>false</em> aborts and exits the iteration.
6563      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6564      */
6565     each : function(fn, scope){
6566         this.data.each(fn, scope);
6567     },
6568
6569     /**
6570      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6571      * (e.g., during paging).
6572      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6573      */
6574     getModifiedRecords : function(){
6575         return this.modified;
6576     },
6577
6578     // private
6579     createFilterFn : function(property, value, anyMatch){
6580         if(!value.exec){ // not a regex
6581             value = String(value);
6582             if(value.length == 0){
6583                 return false;
6584             }
6585             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6586         }
6587         return function(r){
6588             return value.test(r.data[property]);
6589         };
6590     },
6591
6592     /**
6593      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6594      * @param {String} property A field on your records
6595      * @param {Number} start The record index to start at (defaults to 0)
6596      * @param {Number} end The last record index to include (defaults to length - 1)
6597      * @return {Number} The sum
6598      */
6599     sum : function(property, start, end){
6600         var rs = this.data.items, v = 0;
6601         start = start || 0;
6602         end = (end || end === 0) ? end : rs.length-1;
6603
6604         for(var i = start; i <= end; i++){
6605             v += (rs[i].data[property] || 0);
6606         }
6607         return v;
6608     },
6609
6610     /**
6611      * Filter the records by a specified property.
6612      * @param {String} field A field on your records
6613      * @param {String/RegExp} value Either a string that the field
6614      * should start with or a RegExp to test against the field
6615      * @param {Boolean} anyMatch True to match any part not just the beginning
6616      */
6617     filter : function(property, value, anyMatch){
6618         var fn = this.createFilterFn(property, value, anyMatch);
6619         return fn ? this.filterBy(fn) : this.clearFilter();
6620     },
6621
6622     /**
6623      * Filter by a function. The specified function will be called with each
6624      * record in this data source. If the function returns true the record is included,
6625      * otherwise it is filtered.
6626      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6627      * @param {Object} scope (optional) The scope of the function (defaults to this)
6628      */
6629     filterBy : function(fn, scope){
6630         this.snapshot = this.snapshot || this.data;
6631         this.data = this.queryBy(fn, scope||this);
6632         this.fireEvent("datachanged", this);
6633     },
6634
6635     /**
6636      * Query the records by a specified property.
6637      * @param {String} field A field on your records
6638      * @param {String/RegExp} value Either a string that the field
6639      * should start with or a RegExp to test against the field
6640      * @param {Boolean} anyMatch True to match any part not just the beginning
6641      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6642      */
6643     query : function(property, value, anyMatch){
6644         var fn = this.createFilterFn(property, value, anyMatch);
6645         return fn ? this.queryBy(fn) : this.data.clone();
6646     },
6647
6648     /**
6649      * Query by a function. The specified function will be called with each
6650      * record in this data source. If the function returns true the record is included
6651      * in the results.
6652      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6653      * @param {Object} scope (optional) The scope of the function (defaults to this)
6654       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6655      **/
6656     queryBy : function(fn, scope){
6657         var data = this.snapshot || this.data;
6658         return data.filterBy(fn, scope||this);
6659     },
6660
6661     /**
6662      * Collects unique values for a particular dataIndex from this store.
6663      * @param {String} dataIndex The property to collect
6664      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6665      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6666      * @return {Array} An array of the unique values
6667      **/
6668     collect : function(dataIndex, allowNull, bypassFilter){
6669         var d = (bypassFilter === true && this.snapshot) ?
6670                 this.snapshot.items : this.data.items;
6671         var v, sv, r = [], l = {};
6672         for(var i = 0, len = d.length; i < len; i++){
6673             v = d[i].data[dataIndex];
6674             sv = String(v);
6675             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6676                 l[sv] = true;
6677                 r[r.length] = v;
6678             }
6679         }
6680         return r;
6681     },
6682
6683     /**
6684      * Revert to a view of the Record cache with no filtering applied.
6685      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6686      */
6687     clearFilter : function(suppressEvent){
6688         if(this.snapshot && this.snapshot != this.data){
6689             this.data = this.snapshot;
6690             delete this.snapshot;
6691             if(suppressEvent !== true){
6692                 this.fireEvent("datachanged", this);
6693             }
6694         }
6695     },
6696
6697     // private
6698     afterEdit : function(record){
6699         if(this.modified.indexOf(record) == -1){
6700             this.modified.push(record);
6701         }
6702         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6703     },
6704     
6705     // private
6706     afterReject : function(record){
6707         this.modified.remove(record);
6708         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6709     },
6710
6711     // private
6712     afterCommit : function(record){
6713         this.modified.remove(record);
6714         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6715     },
6716
6717     /**
6718      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6719      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6720      */
6721     commitChanges : function(){
6722         var m = this.modified.slice(0);
6723         this.modified = [];
6724         for(var i = 0, len = m.length; i < len; i++){
6725             m[i].commit();
6726         }
6727     },
6728
6729     /**
6730      * Cancel outstanding changes on all changed records.
6731      */
6732     rejectChanges : function(){
6733         var m = this.modified.slice(0);
6734         this.modified = [];
6735         for(var i = 0, len = m.length; i < len; i++){
6736             m[i].reject();
6737         }
6738     },
6739
6740     onMetaChange : function(meta, rtype, o){
6741         this.recordType = rtype;
6742         this.fields = rtype.prototype.fields;
6743         delete this.snapshot;
6744         this.sortInfo = meta.sortInfo || this.sortInfo;
6745         this.modified = [];
6746         this.fireEvent('metachange', this, this.reader.meta);
6747     },
6748     
6749     moveIndex : function(data, type)
6750     {
6751         var index = this.indexOf(data);
6752         
6753         var newIndex = index + type;
6754         
6755         this.remove(data);
6756         
6757         this.insert(newIndex, data);
6758         
6759     }
6760 });/*
6761  * Based on:
6762  * Ext JS Library 1.1.1
6763  * Copyright(c) 2006-2007, Ext JS, LLC.
6764  *
6765  * Originally Released Under LGPL - original licence link has changed is not relivant.
6766  *
6767  * Fork - LGPL
6768  * <script type="text/javascript">
6769  */
6770
6771 /**
6772  * @class Roo.data.SimpleStore
6773  * @extends Roo.data.Store
6774  * Small helper class to make creating Stores from Array data easier.
6775  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6776  * @cfg {Array} fields An array of field definition objects, or field name strings.
6777  * @cfg {Array} data The multi-dimensional array of data
6778  * @constructor
6779  * @param {Object} config
6780  */
6781 Roo.data.SimpleStore = function(config){
6782     Roo.data.SimpleStore.superclass.constructor.call(this, {
6783         isLocal : true,
6784         reader: new Roo.data.ArrayReader({
6785                 id: config.id
6786             },
6787             Roo.data.Record.create(config.fields)
6788         ),
6789         proxy : new Roo.data.MemoryProxy(config.data)
6790     });
6791     this.load();
6792 };
6793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6794  * Based on:
6795  * Ext JS Library 1.1.1
6796  * Copyright(c) 2006-2007, Ext JS, LLC.
6797  *
6798  * Originally Released Under LGPL - original licence link has changed is not relivant.
6799  *
6800  * Fork - LGPL
6801  * <script type="text/javascript">
6802  */
6803
6804 /**
6805 /**
6806  * @extends Roo.data.Store
6807  * @class Roo.data.JsonStore
6808  * Small helper class to make creating Stores for JSON data easier. <br/>
6809 <pre><code>
6810 var store = new Roo.data.JsonStore({
6811     url: 'get-images.php',
6812     root: 'images',
6813     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6814 });
6815 </code></pre>
6816  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6817  * JsonReader and HttpProxy (unless inline data is provided).</b>
6818  * @cfg {Array} fields An array of field definition objects, or field name strings.
6819  * @constructor
6820  * @param {Object} config
6821  */
6822 Roo.data.JsonStore = function(c){
6823     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6824         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6825         reader: new Roo.data.JsonReader(c, c.fields)
6826     }));
6827 };
6828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6829  * Based on:
6830  * Ext JS Library 1.1.1
6831  * Copyright(c) 2006-2007, Ext JS, LLC.
6832  *
6833  * Originally Released Under LGPL - original licence link has changed is not relivant.
6834  *
6835  * Fork - LGPL
6836  * <script type="text/javascript">
6837  */
6838
6839  
6840 Roo.data.Field = function(config){
6841     if(typeof config == "string"){
6842         config = {name: config};
6843     }
6844     Roo.apply(this, config);
6845     
6846     if(!this.type){
6847         this.type = "auto";
6848     }
6849     
6850     var st = Roo.data.SortTypes;
6851     // named sortTypes are supported, here we look them up
6852     if(typeof this.sortType == "string"){
6853         this.sortType = st[this.sortType];
6854     }
6855     
6856     // set default sortType for strings and dates
6857     if(!this.sortType){
6858         switch(this.type){
6859             case "string":
6860                 this.sortType = st.asUCString;
6861                 break;
6862             case "date":
6863                 this.sortType = st.asDate;
6864                 break;
6865             default:
6866                 this.sortType = st.none;
6867         }
6868     }
6869
6870     // define once
6871     var stripRe = /[\$,%]/g;
6872
6873     // prebuilt conversion function for this field, instead of
6874     // switching every time we're reading a value
6875     if(!this.convert){
6876         var cv, dateFormat = this.dateFormat;
6877         switch(this.type){
6878             case "":
6879             case "auto":
6880             case undefined:
6881                 cv = function(v){ return v; };
6882                 break;
6883             case "string":
6884                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6885                 break;
6886             case "int":
6887                 cv = function(v){
6888                     return v !== undefined && v !== null && v !== '' ?
6889                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6890                     };
6891                 break;
6892             case "float":
6893                 cv = function(v){
6894                     return v !== undefined && v !== null && v !== '' ?
6895                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6896                     };
6897                 break;
6898             case "bool":
6899             case "boolean":
6900                 cv = function(v){ return v === true || v === "true" || v == 1; };
6901                 break;
6902             case "date":
6903                 cv = function(v){
6904                     if(!v){
6905                         return '';
6906                     }
6907                     if(v instanceof Date){
6908                         return v;
6909                     }
6910                     if(dateFormat){
6911                         if(dateFormat == "timestamp"){
6912                             return new Date(v*1000);
6913                         }
6914                         return Date.parseDate(v, dateFormat);
6915                     }
6916                     var parsed = Date.parse(v);
6917                     return parsed ? new Date(parsed) : null;
6918                 };
6919              break;
6920             
6921         }
6922         this.convert = cv;
6923     }
6924 };
6925
6926 Roo.data.Field.prototype = {
6927     dateFormat: null,
6928     defaultValue: "",
6929     mapping: null,
6930     sortType : null,
6931     sortDir : "ASC"
6932 };/*
6933  * Based on:
6934  * Ext JS Library 1.1.1
6935  * Copyright(c) 2006-2007, Ext JS, LLC.
6936  *
6937  * Originally Released Under LGPL - original licence link has changed is not relivant.
6938  *
6939  * Fork - LGPL
6940  * <script type="text/javascript">
6941  */
6942  
6943 // Base class for reading structured data from a data source.  This class is intended to be
6944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6945
6946 /**
6947  * @class Roo.data.DataReader
6948  * Base class for reading structured data from a data source.  This class is intended to be
6949  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6950  */
6951
6952 Roo.data.DataReader = function(meta, recordType){
6953     
6954     this.meta = meta;
6955     
6956     this.recordType = recordType instanceof Array ? 
6957         Roo.data.Record.create(recordType) : recordType;
6958 };
6959
6960 Roo.data.DataReader.prototype = {
6961      /**
6962      * Create an empty record
6963      * @param {Object} data (optional) - overlay some values
6964      * @return {Roo.data.Record} record created.
6965      */
6966     newRow :  function(d) {
6967         var da =  {};
6968         this.recordType.prototype.fields.each(function(c) {
6969             switch( c.type) {
6970                 case 'int' : da[c.name] = 0; break;
6971                 case 'date' : da[c.name] = new Date(); break;
6972                 case 'float' : da[c.name] = 0.0; break;
6973                 case 'boolean' : da[c.name] = false; break;
6974                 default : da[c.name] = ""; break;
6975             }
6976             
6977         });
6978         return new this.recordType(Roo.apply(da, d));
6979     }
6980     
6981 };/*
6982  * Based on:
6983  * Ext JS Library 1.1.1
6984  * Copyright(c) 2006-2007, Ext JS, LLC.
6985  *
6986  * Originally Released Under LGPL - original licence link has changed is not relivant.
6987  *
6988  * Fork - LGPL
6989  * <script type="text/javascript">
6990  */
6991
6992 /**
6993  * @class Roo.data.DataProxy
6994  * @extends Roo.data.Observable
6995  * This class is an abstract base class for implementations which provide retrieval of
6996  * unformatted data objects.<br>
6997  * <p>
6998  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6999  * (of the appropriate type which knows how to parse the data object) to provide a block of
7000  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7001  * <p>
7002  * Custom implementations must implement the load method as described in
7003  * {@link Roo.data.HttpProxy#load}.
7004  */
7005 Roo.data.DataProxy = function(){
7006     this.addEvents({
7007         /**
7008          * @event beforeload
7009          * Fires before a network request is made to retrieve a data object.
7010          * @param {Object} This DataProxy object.
7011          * @param {Object} params The params parameter to the load function.
7012          */
7013         beforeload : true,
7014         /**
7015          * @event load
7016          * Fires before the load method's callback is called.
7017          * @param {Object} This DataProxy object.
7018          * @param {Object} o The data object.
7019          * @param {Object} arg The callback argument object passed to the load function.
7020          */
7021         load : true,
7022         /**
7023          * @event loadexception
7024          * Fires if an Exception occurs during data retrieval.
7025          * @param {Object} This DataProxy object.
7026          * @param {Object} o The data object.
7027          * @param {Object} arg The callback argument object passed to the load function.
7028          * @param {Object} e The Exception.
7029          */
7030         loadexception : true
7031     });
7032     Roo.data.DataProxy.superclass.constructor.call(this);
7033 };
7034
7035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7036
7037     /**
7038      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7039      */
7040 /*
7041  * Based on:
7042  * Ext JS Library 1.1.1
7043  * Copyright(c) 2006-2007, Ext JS, LLC.
7044  *
7045  * Originally Released Under LGPL - original licence link has changed is not relivant.
7046  *
7047  * Fork - LGPL
7048  * <script type="text/javascript">
7049  */
7050 /**
7051  * @class Roo.data.MemoryProxy
7052  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7053  * to the Reader when its load method is called.
7054  * @constructor
7055  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7056  */
7057 Roo.data.MemoryProxy = function(data){
7058     if (data.data) {
7059         data = data.data;
7060     }
7061     Roo.data.MemoryProxy.superclass.constructor.call(this);
7062     this.data = data;
7063 };
7064
7065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7066     /**
7067      * Load data from the requested source (in this case an in-memory
7068      * data object passed to the constructor), read the data object into
7069      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7070      * process that block using the passed callback.
7071      * @param {Object} params This parameter is not used by the MemoryProxy class.
7072      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7073      * object into a block of Roo.data.Records.
7074      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7075      * The function must be passed <ul>
7076      * <li>The Record block object</li>
7077      * <li>The "arg" argument from the load function</li>
7078      * <li>A boolean success indicator</li>
7079      * </ul>
7080      * @param {Object} scope The scope in which to call the callback
7081      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7082      */
7083     load : function(params, reader, callback, scope, arg){
7084         params = params || {};
7085         var result;
7086         try {
7087             result = reader.readRecords(this.data);
7088         }catch(e){
7089             this.fireEvent("loadexception", this, arg, null, e);
7090             callback.call(scope, null, arg, false);
7091             return;
7092         }
7093         callback.call(scope, result, arg, true);
7094     },
7095     
7096     // private
7097     update : function(params, records){
7098         
7099     }
7100 });/*
7101  * Based on:
7102  * Ext JS Library 1.1.1
7103  * Copyright(c) 2006-2007, Ext JS, LLC.
7104  *
7105  * Originally Released Under LGPL - original licence link has changed is not relivant.
7106  *
7107  * Fork - LGPL
7108  * <script type="text/javascript">
7109  */
7110 /**
7111  * @class Roo.data.HttpProxy
7112  * @extends Roo.data.DataProxy
7113  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7114  * configured to reference a certain URL.<br><br>
7115  * <p>
7116  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7117  * from which the running page was served.<br><br>
7118  * <p>
7119  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7120  * <p>
7121  * Be aware that to enable the browser to parse an XML document, the server must set
7122  * the Content-Type header in the HTTP response to "text/xml".
7123  * @constructor
7124  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7125  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7126  * will be used to make the request.
7127  */
7128 Roo.data.HttpProxy = function(conn){
7129     Roo.data.HttpProxy.superclass.constructor.call(this);
7130     // is conn a conn config or a real conn?
7131     this.conn = conn;
7132     this.useAjax = !conn || !conn.events;
7133   
7134 };
7135
7136 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7137     // thse are take from connection...
7138     
7139     /**
7140      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7141      */
7142     /**
7143      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7144      * extra parameters to each request made by this object. (defaults to undefined)
7145      */
7146     /**
7147      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7148      *  to each request made by this object. (defaults to undefined)
7149      */
7150     /**
7151      * @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)
7152      */
7153     /**
7154      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7155      */
7156      /**
7157      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7158      * @type Boolean
7159      */
7160   
7161
7162     /**
7163      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7164      * @type Boolean
7165      */
7166     /**
7167      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7168      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7169      * a finer-grained basis than the DataProxy events.
7170      */
7171     getConnection : function(){
7172         return this.useAjax ? Roo.Ajax : this.conn;
7173     },
7174
7175     /**
7176      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7177      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7178      * process that block using the passed callback.
7179      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7180      * for the request to the remote server.
7181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7182      * object into a block of Roo.data.Records.
7183      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7184      * The function must be passed <ul>
7185      * <li>The Record block object</li>
7186      * <li>The "arg" argument from the load function</li>
7187      * <li>A boolean success indicator</li>
7188      * </ul>
7189      * @param {Object} scope The scope in which to call the callback
7190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7191      */
7192     load : function(params, reader, callback, scope, arg){
7193         if(this.fireEvent("beforeload", this, params) !== false){
7194             var  o = {
7195                 params : params || {},
7196                 request: {
7197                     callback : callback,
7198                     scope : scope,
7199                     arg : arg
7200                 },
7201                 reader: reader,
7202                 callback : this.loadResponse,
7203                 scope: this
7204             };
7205             if(this.useAjax){
7206                 Roo.applyIf(o, this.conn);
7207                 if(this.activeRequest){
7208                     Roo.Ajax.abort(this.activeRequest);
7209                 }
7210                 this.activeRequest = Roo.Ajax.request(o);
7211             }else{
7212                 this.conn.request(o);
7213             }
7214         }else{
7215             callback.call(scope||this, null, arg, false);
7216         }
7217     },
7218
7219     // private
7220     loadResponse : function(o, success, response){
7221         delete this.activeRequest;
7222         if(!success){
7223             this.fireEvent("loadexception", this, o, response);
7224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7225             return;
7226         }
7227         var result;
7228         try {
7229             result = o.reader.read(response);
7230         }catch(e){
7231             this.fireEvent("loadexception", this, o, response, e);
7232             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7233             return;
7234         }
7235         
7236         this.fireEvent("load", this, o, o.request.arg);
7237         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7238     },
7239
7240     // private
7241     update : function(dataSet){
7242
7243     },
7244
7245     // private
7246     updateResponse : function(dataSet){
7247
7248     }
7249 });/*
7250  * Based on:
7251  * Ext JS Library 1.1.1
7252  * Copyright(c) 2006-2007, Ext JS, LLC.
7253  *
7254  * Originally Released Under LGPL - original licence link has changed is not relivant.
7255  *
7256  * Fork - LGPL
7257  * <script type="text/javascript">
7258  */
7259
7260 /**
7261  * @class Roo.data.ScriptTagProxy
7262  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7263  * other than the originating domain of the running page.<br><br>
7264  * <p>
7265  * <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
7266  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7267  * <p>
7268  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7269  * source code that is used as the source inside a &lt;script> tag.<br><br>
7270  * <p>
7271  * In order for the browser to process the returned data, the server must wrap the data object
7272  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7273  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7274  * depending on whether the callback name was passed:
7275  * <p>
7276  * <pre><code>
7277 boolean scriptTag = false;
7278 String cb = request.getParameter("callback");
7279 if (cb != null) {
7280     scriptTag = true;
7281     response.setContentType("text/javascript");
7282 } else {
7283     response.setContentType("application/x-json");
7284 }
7285 Writer out = response.getWriter();
7286 if (scriptTag) {
7287     out.write(cb + "(");
7288 }
7289 out.print(dataBlock.toJsonString());
7290 if (scriptTag) {
7291     out.write(");");
7292 }
7293 </pre></code>
7294  *
7295  * @constructor
7296  * @param {Object} config A configuration object.
7297  */
7298 Roo.data.ScriptTagProxy = function(config){
7299     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7300     Roo.apply(this, config);
7301     this.head = document.getElementsByTagName("head")[0];
7302 };
7303
7304 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7305
7306 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7307     /**
7308      * @cfg {String} url The URL from which to request the data object.
7309      */
7310     /**
7311      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7312      */
7313     timeout : 30000,
7314     /**
7315      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7316      * the server the name of the callback function set up by the load call to process the returned data object.
7317      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7318      * javascript output which calls this named function passing the data object as its only parameter.
7319      */
7320     callbackParam : "callback",
7321     /**
7322      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7323      * name to the request.
7324      */
7325     nocache : true,
7326
7327     /**
7328      * Load data from the configured URL, read the data object into
7329      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7330      * process that block using the passed callback.
7331      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7332      * for the request to the remote server.
7333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7334      * object into a block of Roo.data.Records.
7335      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7336      * The function must be passed <ul>
7337      * <li>The Record block object</li>
7338      * <li>The "arg" argument from the load function</li>
7339      * <li>A boolean success indicator</li>
7340      * </ul>
7341      * @param {Object} scope The scope in which to call the callback
7342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7343      */
7344     load : function(params, reader, callback, scope, arg){
7345         if(this.fireEvent("beforeload", this, params) !== false){
7346
7347             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7348
7349             var url = this.url;
7350             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7351             if(this.nocache){
7352                 url += "&_dc=" + (new Date().getTime());
7353             }
7354             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7355             var trans = {
7356                 id : transId,
7357                 cb : "stcCallback"+transId,
7358                 scriptId : "stcScript"+transId,
7359                 params : params,
7360                 arg : arg,
7361                 url : url,
7362                 callback : callback,
7363                 scope : scope,
7364                 reader : reader
7365             };
7366             var conn = this;
7367
7368             window[trans.cb] = function(o){
7369                 conn.handleResponse(o, trans);
7370             };
7371
7372             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7373
7374             if(this.autoAbort !== false){
7375                 this.abort();
7376             }
7377
7378             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7379
7380             var script = document.createElement("script");
7381             script.setAttribute("src", url);
7382             script.setAttribute("type", "text/javascript");
7383             script.setAttribute("id", trans.scriptId);
7384             this.head.appendChild(script);
7385
7386             this.trans = trans;
7387         }else{
7388             callback.call(scope||this, null, arg, false);
7389         }
7390     },
7391
7392     // private
7393     isLoading : function(){
7394         return this.trans ? true : false;
7395     },
7396
7397     /**
7398      * Abort the current server request.
7399      */
7400     abort : function(){
7401         if(this.isLoading()){
7402             this.destroyTrans(this.trans);
7403         }
7404     },
7405
7406     // private
7407     destroyTrans : function(trans, isLoaded){
7408         this.head.removeChild(document.getElementById(trans.scriptId));
7409         clearTimeout(trans.timeoutId);
7410         if(isLoaded){
7411             window[trans.cb] = undefined;
7412             try{
7413                 delete window[trans.cb];
7414             }catch(e){}
7415         }else{
7416             // if hasn't been loaded, wait for load to remove it to prevent script error
7417             window[trans.cb] = function(){
7418                 window[trans.cb] = undefined;
7419                 try{
7420                     delete window[trans.cb];
7421                 }catch(e){}
7422             };
7423         }
7424     },
7425
7426     // private
7427     handleResponse : function(o, trans){
7428         this.trans = false;
7429         this.destroyTrans(trans, true);
7430         var result;
7431         try {
7432             result = trans.reader.readRecords(o);
7433         }catch(e){
7434             this.fireEvent("loadexception", this, o, trans.arg, e);
7435             trans.callback.call(trans.scope||window, null, trans.arg, false);
7436             return;
7437         }
7438         this.fireEvent("load", this, o, trans.arg);
7439         trans.callback.call(trans.scope||window, result, trans.arg, true);
7440     },
7441
7442     // private
7443     handleFailure : function(trans){
7444         this.trans = false;
7445         this.destroyTrans(trans, false);
7446         this.fireEvent("loadexception", this, null, trans.arg);
7447         trans.callback.call(trans.scope||window, null, trans.arg, false);
7448     }
7449 });/*
7450  * Based on:
7451  * Ext JS Library 1.1.1
7452  * Copyright(c) 2006-2007, Ext JS, LLC.
7453  *
7454  * Originally Released Under LGPL - original licence link has changed is not relivant.
7455  *
7456  * Fork - LGPL
7457  * <script type="text/javascript">
7458  */
7459
7460 /**
7461  * @class Roo.data.JsonReader
7462  * @extends Roo.data.DataReader
7463  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7464  * based on mappings in a provided Roo.data.Record constructor.
7465  * 
7466  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7467  * in the reply previously. 
7468  * 
7469  * <p>
7470  * Example code:
7471  * <pre><code>
7472 var RecordDef = Roo.data.Record.create([
7473     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7474     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7475 ]);
7476 var myReader = new Roo.data.JsonReader({
7477     totalProperty: "results",    // The property which contains the total dataset size (optional)
7478     root: "rows",                // The property which contains an Array of row objects
7479     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7480 }, RecordDef);
7481 </code></pre>
7482  * <p>
7483  * This would consume a JSON file like this:
7484  * <pre><code>
7485 { 'results': 2, 'rows': [
7486     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7487     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7488 }
7489 </code></pre>
7490  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7491  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7492  * paged from the remote server.
7493  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7494  * @cfg {String} root name of the property which contains the Array of row objects.
7495  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7496  * @constructor
7497  * Create a new JsonReader
7498  * @param {Object} meta Metadata configuration options
7499  * @param {Object} recordType Either an Array of field definition objects,
7500  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7501  */
7502 Roo.data.JsonReader = function(meta, recordType){
7503     
7504     meta = meta || {};
7505     // set some defaults:
7506     Roo.applyIf(meta, {
7507         totalProperty: 'total',
7508         successProperty : 'success',
7509         root : 'data',
7510         id : 'id'
7511     });
7512     
7513     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7514 };
7515 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7516     
7517     /**
7518      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7519      * Used by Store query builder to append _requestMeta to params.
7520      * 
7521      */
7522     metaFromRemote : false,
7523     /**
7524      * This method is only used by a DataProxy which has retrieved data from a remote server.
7525      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7526      * @return {Object} data A data block which is used by an Roo.data.Store object as
7527      * a cache of Roo.data.Records.
7528      */
7529     read : function(response){
7530         var json = response.responseText;
7531        
7532         var o = /* eval:var:o */ eval("("+json+")");
7533         if(!o) {
7534             throw {message: "JsonReader.read: Json object not found"};
7535         }
7536         
7537         if(o.metaData){
7538             
7539             delete this.ef;
7540             this.metaFromRemote = true;
7541             this.meta = o.metaData;
7542             this.recordType = Roo.data.Record.create(o.metaData.fields);
7543             this.onMetaChange(this.meta, this.recordType, o);
7544         }
7545         return this.readRecords(o);
7546     },
7547
7548     // private function a store will implement
7549     onMetaChange : function(meta, recordType, o){
7550
7551     },
7552
7553     /**
7554          * @ignore
7555          */
7556     simpleAccess: function(obj, subsc) {
7557         return obj[subsc];
7558     },
7559
7560         /**
7561          * @ignore
7562          */
7563     getJsonAccessor: function(){
7564         var re = /[\[\.]/;
7565         return function(expr) {
7566             try {
7567                 return(re.test(expr))
7568                     ? new Function("obj", "return obj." + expr)
7569                     : function(obj){
7570                         return obj[expr];
7571                     };
7572             } catch(e){}
7573             return Roo.emptyFn;
7574         };
7575     }(),
7576
7577     /**
7578      * Create a data block containing Roo.data.Records from an XML document.
7579      * @param {Object} o An object which contains an Array of row objects in the property specified
7580      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7581      * which contains the total size of the dataset.
7582      * @return {Object} data A data block which is used by an Roo.data.Store object as
7583      * a cache of Roo.data.Records.
7584      */
7585     readRecords : function(o){
7586         /**
7587          * After any data loads, the raw JSON data is available for further custom processing.
7588          * @type Object
7589          */
7590         this.o = o;
7591         var s = this.meta, Record = this.recordType,
7592             f = Record.prototype.fields, fi = f.items, fl = f.length;
7593
7594 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7595         if (!this.ef) {
7596             if(s.totalProperty) {
7597                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7598                 }
7599                 if(s.successProperty) {
7600                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7601                 }
7602                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7603                 if (s.id) {
7604                         var g = this.getJsonAccessor(s.id);
7605                         this.getId = function(rec) {
7606                                 var r = g(rec);
7607                                 return (r === undefined || r === "") ? null : r;
7608                         };
7609                 } else {
7610                         this.getId = function(){return null;};
7611                 }
7612             this.ef = [];
7613             for(var jj = 0; jj < fl; jj++){
7614                 f = fi[jj];
7615                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7616                 this.ef[jj] = this.getJsonAccessor(map);
7617             }
7618         }
7619
7620         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7621         if(s.totalProperty){
7622             var vt = parseInt(this.getTotal(o), 10);
7623             if(!isNaN(vt)){
7624                 totalRecords = vt;
7625             }
7626         }
7627         if(s.successProperty){
7628             var vs = this.getSuccess(o);
7629             if(vs === false || vs === 'false'){
7630                 success = false;
7631             }
7632         }
7633         var records = [];
7634             for(var i = 0; i < c; i++){
7635                     var n = root[i];
7636                 var values = {};
7637                 var id = this.getId(n);
7638                 for(var j = 0; j < fl; j++){
7639                     f = fi[j];
7640                 var v = this.ef[j](n);
7641                 if (!f.convert) {
7642                     Roo.log('missing convert for ' + f.name);
7643                     Roo.log(f);
7644                     continue;
7645                 }
7646                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7647                 }
7648                 var record = new Record(values, id);
7649                 record.json = n;
7650                 records[i] = record;
7651             }
7652             return {
7653             raw : o,
7654                 success : success,
7655                 records : records,
7656                 totalRecords : totalRecords
7657             };
7658     }
7659 });/*
7660  * Based on:
7661  * Ext JS Library 1.1.1
7662  * Copyright(c) 2006-2007, Ext JS, LLC.
7663  *
7664  * Originally Released Under LGPL - original licence link has changed is not relivant.
7665  *
7666  * Fork - LGPL
7667  * <script type="text/javascript">
7668  */
7669
7670 /**
7671  * @class Roo.data.ArrayReader
7672  * @extends Roo.data.DataReader
7673  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7674  * Each element of that Array represents a row of data fields. The
7675  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7676  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7677  * <p>
7678  * Example code:.
7679  * <pre><code>
7680 var RecordDef = Roo.data.Record.create([
7681     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7682     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7683 ]);
7684 var myReader = new Roo.data.ArrayReader({
7685     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7686 }, RecordDef);
7687 </code></pre>
7688  * <p>
7689  * This would consume an Array like this:
7690  * <pre><code>
7691 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7692   </code></pre>
7693  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7694  * @constructor
7695  * Create a new JsonReader
7696  * @param {Object} meta Metadata configuration options.
7697  * @param {Object} recordType Either an Array of field definition objects
7698  * as specified to {@link Roo.data.Record#create},
7699  * or an {@link Roo.data.Record} object
7700  * created using {@link Roo.data.Record#create}.
7701  */
7702 Roo.data.ArrayReader = function(meta, recordType){
7703     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7704 };
7705
7706 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7707     /**
7708      * Create a data block containing Roo.data.Records from an XML document.
7709      * @param {Object} o An Array of row objects which represents the dataset.
7710      * @return {Object} data A data block which is used by an Roo.data.Store object as
7711      * a cache of Roo.data.Records.
7712      */
7713     readRecords : function(o){
7714         var sid = this.meta ? this.meta.id : null;
7715         var recordType = this.recordType, fields = recordType.prototype.fields;
7716         var records = [];
7717         var root = o;
7718             for(var i = 0; i < root.length; i++){
7719                     var n = root[i];
7720                 var values = {};
7721                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7722                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7723                 var f = fields.items[j];
7724                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7725                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7726                 v = f.convert(v);
7727                 values[f.name] = v;
7728             }
7729                 var record = new recordType(values, id);
7730                 record.json = n;
7731                 records[records.length] = record;
7732             }
7733             return {
7734                 records : records,
7735                 totalRecords : records.length
7736             };
7737     }
7738 });/*
7739  * - LGPL
7740  * * 
7741  */
7742
7743 /**
7744  * @class Roo.bootstrap.ComboBox
7745  * @extends Roo.bootstrap.TriggerField
7746  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7747  * @cfg {Boolean} append (true|false) default false
7748  * @constructor
7749  * Create a new ComboBox.
7750  * @param {Object} config Configuration options
7751  */
7752 Roo.bootstrap.ComboBox = function(config){
7753     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7754     this.addEvents({
7755         /**
7756          * @event expand
7757          * Fires when the dropdown list is expanded
7758              * @param {Roo.bootstrap.ComboBox} combo This combo box
7759              */
7760         'expand' : true,
7761         /**
7762          * @event collapse
7763          * Fires when the dropdown list is collapsed
7764              * @param {Roo.bootstrap.ComboBox} combo This combo box
7765              */
7766         'collapse' : true,
7767         /**
7768          * @event beforeselect
7769          * Fires before a list item is selected. Return false to cancel the selection.
7770              * @param {Roo.bootstrap.ComboBox} combo This combo box
7771              * @param {Roo.data.Record} record The data record returned from the underlying store
7772              * @param {Number} index The index of the selected item in the dropdown list
7773              */
7774         'beforeselect' : true,
7775         /**
7776          * @event select
7777          * Fires when a list item is selected
7778              * @param {Roo.bootstrap.ComboBox} combo This combo box
7779              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7780              * @param {Number} index The index of the selected item in the dropdown list
7781              */
7782         'select' : true,
7783         /**
7784          * @event beforequery
7785          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7786          * The event object passed has these properties:
7787              * @param {Roo.bootstrap.ComboBox} combo This combo box
7788              * @param {String} query The query
7789              * @param {Boolean} forceAll true to force "all" query
7790              * @param {Boolean} cancel true to cancel the query
7791              * @param {Object} e The query event object
7792              */
7793         'beforequery': true,
7794          /**
7795          * @event add
7796          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7797              * @param {Roo.bootstrap.ComboBox} combo This combo box
7798              */
7799         'add' : true,
7800         /**
7801          * @event edit
7802          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7803              * @param {Roo.bootstrap.ComboBox} combo This combo box
7804              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7805              */
7806         'edit' : true,
7807         /**
7808          * @event remove
7809          * Fires when the remove value from the combobox array
7810              * @param {Roo.bootstrap.ComboBox} combo This combo box
7811              */
7812         'remove' : true
7813         
7814     });
7815     
7816     
7817     this.selectedIndex = -1;
7818     if(this.mode == 'local'){
7819         if(config.queryDelay === undefined){
7820             this.queryDelay = 10;
7821         }
7822         if(config.minChars === undefined){
7823             this.minChars = 0;
7824         }
7825     }
7826 };
7827
7828 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7829      
7830     /**
7831      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7832      * rendering into an Roo.Editor, defaults to false)
7833      */
7834     /**
7835      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7836      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7837      */
7838     /**
7839      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7840      */
7841     /**
7842      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7843      * the dropdown list (defaults to undefined, with no header element)
7844      */
7845
7846      /**
7847      * @cfg {String/Roo.Template} tpl The template to use to render the output
7848      */
7849      
7850      /**
7851      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7852      */
7853     listWidth: undefined,
7854     /**
7855      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7856      * mode = 'remote' or 'text' if mode = 'local')
7857      */
7858     displayField: undefined,
7859     /**
7860      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7861      * mode = 'remote' or 'value' if mode = 'local'). 
7862      * Note: use of a valueField requires the user make a selection
7863      * in order for a value to be mapped.
7864      */
7865     valueField: undefined,
7866     
7867     
7868     /**
7869      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7870      * field's data value (defaults to the underlying DOM element's name)
7871      */
7872     hiddenName: undefined,
7873     /**
7874      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7875      */
7876     listClass: '',
7877     /**
7878      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7879      */
7880     selectedClass: 'active',
7881     
7882     /**
7883      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7884      */
7885     shadow:'sides',
7886     /**
7887      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7888      * anchor positions (defaults to 'tl-bl')
7889      */
7890     listAlign: 'tl-bl?',
7891     /**
7892      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7893      */
7894     maxHeight: 300,
7895     /**
7896      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7897      * query specified by the allQuery config option (defaults to 'query')
7898      */
7899     triggerAction: 'query',
7900     /**
7901      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7902      * (defaults to 4, does not apply if editable = false)
7903      */
7904     minChars : 4,
7905     /**
7906      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7907      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7908      */
7909     typeAhead: false,
7910     /**
7911      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7912      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7913      */
7914     queryDelay: 500,
7915     /**
7916      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7917      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7918      */
7919     pageSize: 0,
7920     /**
7921      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7922      * when editable = true (defaults to false)
7923      */
7924     selectOnFocus:false,
7925     /**
7926      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7927      */
7928     queryParam: 'query',
7929     /**
7930      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7931      * when mode = 'remote' (defaults to 'Loading...')
7932      */
7933     loadingText: 'Loading...',
7934     /**
7935      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7936      */
7937     resizable: false,
7938     /**
7939      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7940      */
7941     handleHeight : 8,
7942     /**
7943      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7944      * traditional select (defaults to true)
7945      */
7946     editable: true,
7947     /**
7948      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7949      */
7950     allQuery: '',
7951     /**
7952      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7953      */
7954     mode: 'remote',
7955     /**
7956      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7957      * listWidth has a higher value)
7958      */
7959     minListWidth : 70,
7960     /**
7961      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7962      * allow the user to set arbitrary text into the field (defaults to false)
7963      */
7964     forceSelection:false,
7965     /**
7966      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7967      * if typeAhead = true (defaults to 250)
7968      */
7969     typeAheadDelay : 250,
7970     /**
7971      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7972      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7973      */
7974     valueNotFoundText : undefined,
7975     /**
7976      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7977      */
7978     blockFocus : false,
7979     
7980     /**
7981      * @cfg {Boolean} disableClear Disable showing of clear button.
7982      */
7983     disableClear : false,
7984     /**
7985      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7986      */
7987     alwaysQuery : false,
7988     
7989     /**
7990      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7991      */
7992     multiple : false,
7993     
7994     //private
7995     addicon : false,
7996     editicon: false,
7997     
7998     page: 0,
7999     hasQuery: false,
8000     append: false,
8001     loadNext: false,
8002     item: [],
8003     
8004     // element that contains real text value.. (when hidden is used..)
8005      
8006     // private
8007     initEvents: function(){
8008         
8009         if (!this.store) {
8010             throw "can not find store for combo";
8011         }
8012         this.store = Roo.factory(this.store, Roo.data);
8013         
8014         
8015         
8016         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8017         
8018         
8019         if(this.hiddenName){
8020             
8021             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8022             
8023             this.hiddenField.dom.value =
8024                 this.hiddenValue !== undefined ? this.hiddenValue :
8025                 this.value !== undefined ? this.value : '';
8026
8027             // prevent input submission
8028             this.el.dom.removeAttribute('name');
8029             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8030              
8031              
8032         }
8033         //if(Roo.isGecko){
8034         //    this.el.dom.setAttribute('autocomplete', 'off');
8035         //}
8036
8037         var cls = 'x-combo-list';
8038         this.list = this.el.select('ul.dropdown-menu',true).first();
8039
8040         //this.list = new Roo.Layer({
8041         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8042         //});
8043         
8044         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8045         this.list.setWidth(lw);
8046         
8047         this.list.on('mouseover', this.onViewOver, this);
8048         this.list.on('mousemove', this.onViewMove, this);
8049         
8050         this.list.on('scroll', this.onViewScroll, this);
8051         
8052         /*
8053         this.list.swallowEvent('mousewheel');
8054         this.assetHeight = 0;
8055
8056         if(this.title){
8057             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8058             this.assetHeight += this.header.getHeight();
8059         }
8060
8061         this.innerList = this.list.createChild({cls:cls+'-inner'});
8062         this.innerList.on('mouseover', this.onViewOver, this);
8063         this.innerList.on('mousemove', this.onViewMove, this);
8064         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8065         
8066         if(this.allowBlank && !this.pageSize && !this.disableClear){
8067             this.footer = this.list.createChild({cls:cls+'-ft'});
8068             this.pageTb = new Roo.Toolbar(this.footer);
8069            
8070         }
8071         if(this.pageSize){
8072             this.footer = this.list.createChild({cls:cls+'-ft'});
8073             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8074                     {pageSize: this.pageSize});
8075             
8076         }
8077         
8078         if (this.pageTb && this.allowBlank && !this.disableClear) {
8079             var _this = this;
8080             this.pageTb.add(new Roo.Toolbar.Fill(), {
8081                 cls: 'x-btn-icon x-btn-clear',
8082                 text: '&#160;',
8083                 handler: function()
8084                 {
8085                     _this.collapse();
8086                     _this.clearValue();
8087                     _this.onSelect(false, -1);
8088                 }
8089             });
8090         }
8091         if (this.footer) {
8092             this.assetHeight += this.footer.getHeight();
8093         }
8094         */
8095             
8096         if(!this.tpl){
8097             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8098         }
8099
8100         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8101             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8102         });
8103         //this.view.wrapEl.setDisplayed(false);
8104         this.view.on('click', this.onViewClick, this);
8105         
8106         
8107         
8108         this.store.on('beforeload', this.onBeforeLoad, this);
8109         this.store.on('load', this.onLoad, this);
8110         this.store.on('loadexception', this.onLoadException, this);
8111         /*
8112         if(this.resizable){
8113             this.resizer = new Roo.Resizable(this.list,  {
8114                pinned:true, handles:'se'
8115             });
8116             this.resizer.on('resize', function(r, w, h){
8117                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8118                 this.listWidth = w;
8119                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8120                 this.restrictHeight();
8121             }, this);
8122             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8123         }
8124         */
8125         if(!this.editable){
8126             this.editable = true;
8127             this.setEditable(false);
8128         }
8129         
8130         /*
8131         
8132         if (typeof(this.events.add.listeners) != 'undefined') {
8133             
8134             this.addicon = this.wrap.createChild(
8135                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8136        
8137             this.addicon.on('click', function(e) {
8138                 this.fireEvent('add', this);
8139             }, this);
8140         }
8141         if (typeof(this.events.edit.listeners) != 'undefined') {
8142             
8143             this.editicon = this.wrap.createChild(
8144                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8145             if (this.addicon) {
8146                 this.editicon.setStyle('margin-left', '40px');
8147             }
8148             this.editicon.on('click', function(e) {
8149                 
8150                 // we fire even  if inothing is selected..
8151                 this.fireEvent('edit', this, this.lastData );
8152                 
8153             }, this);
8154         }
8155         */
8156         
8157         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8158             "up" : function(e){
8159                 this.inKeyMode = true;
8160                 this.selectPrev();
8161             },
8162
8163             "down" : function(e){
8164                 if(!this.isExpanded()){
8165                     this.onTriggerClick();
8166                 }else{
8167                     this.inKeyMode = true;
8168                     this.selectNext();
8169                 }
8170             },
8171
8172             "enter" : function(e){
8173                 this.onViewClick();
8174                 //return true;
8175             },
8176
8177             "esc" : function(e){
8178                 this.collapse();
8179             },
8180
8181             "tab" : function(e){
8182                 this.collapse();
8183                 
8184                 if(this.fireEvent("specialkey", this, e)){
8185                     this.onViewClick(false);
8186                 }
8187                 
8188                 return true;
8189             },
8190
8191             scope : this,
8192
8193             doRelay : function(foo, bar, hname){
8194                 if(hname == 'down' || this.scope.isExpanded()){
8195                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8196                 }
8197                 return true;
8198             },
8199
8200             forceKeyDown: true
8201         });
8202         
8203         
8204         this.queryDelay = Math.max(this.queryDelay || 10,
8205                 this.mode == 'local' ? 10 : 250);
8206         
8207         
8208         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8209         
8210         if(this.typeAhead){
8211             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8212         }
8213         if(this.editable !== false){
8214             this.inputEl().on("keyup", this.onKeyUp, this);
8215         }
8216         if(this.forceSelection){
8217             this.on('blur', this.doForce, this);
8218         }
8219         
8220         if(this.multiple){
8221             this.choices = this.el.select('ul.select2-choices', true).first();
8222             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8223         }
8224     },
8225
8226     onDestroy : function(){
8227         if(this.view){
8228             this.view.setStore(null);
8229             this.view.el.removeAllListeners();
8230             this.view.el.remove();
8231             this.view.purgeListeners();
8232         }
8233         if(this.list){
8234             this.list.dom.innerHTML  = '';
8235         }
8236         if(this.store){
8237             this.store.un('beforeload', this.onBeforeLoad, this);
8238             this.store.un('load', this.onLoad, this);
8239             this.store.un('loadexception', this.onLoadException, this);
8240         }
8241         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8242     },
8243
8244     // private
8245     fireKey : function(e){
8246         if(e.isNavKeyPress() && !this.list.isVisible()){
8247             this.fireEvent("specialkey", this, e);
8248         }
8249     },
8250
8251     // private
8252     onResize: function(w, h){
8253 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8254 //        
8255 //        if(typeof w != 'number'){
8256 //            // we do not handle it!?!?
8257 //            return;
8258 //        }
8259 //        var tw = this.trigger.getWidth();
8260 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8261 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8262 //        var x = w - tw;
8263 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8264 //            
8265 //        //this.trigger.setStyle('left', x+'px');
8266 //        
8267 //        if(this.list && this.listWidth === undefined){
8268 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8269 //            this.list.setWidth(lw);
8270 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8271 //        }
8272         
8273     
8274         
8275     },
8276
8277     /**
8278      * Allow or prevent the user from directly editing the field text.  If false is passed,
8279      * the user will only be able to select from the items defined in the dropdown list.  This method
8280      * is the runtime equivalent of setting the 'editable' config option at config time.
8281      * @param {Boolean} value True to allow the user to directly edit the field text
8282      */
8283     setEditable : function(value){
8284         if(value == this.editable){
8285             return;
8286         }
8287         this.editable = value;
8288         if(!value){
8289             this.inputEl().dom.setAttribute('readOnly', true);
8290             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8291             this.inputEl().addClass('x-combo-noedit');
8292         }else{
8293             this.inputEl().dom.setAttribute('readOnly', false);
8294             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8295             this.inputEl().removeClass('x-combo-noedit');
8296         }
8297     },
8298
8299     // private
8300     
8301     onBeforeLoad : function(combo,opts){
8302         if(!this.hasFocus){
8303             return;
8304         }
8305          if (!opts.add) {
8306             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8307          }
8308         this.restrictHeight();
8309         this.selectedIndex = -1;
8310     },
8311
8312     // private
8313     onLoad : function(){
8314         
8315         this.hasQuery = false;
8316         
8317         if(!this.hasFocus){
8318             return;
8319         }
8320         
8321         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8322             this.loading.hide();
8323         }
8324         
8325         if(this.store.getCount() > 0){
8326             this.expand();
8327             this.restrictHeight();
8328             if(this.lastQuery == this.allQuery){
8329                 if(this.editable){
8330                     this.inputEl().dom.select();
8331                 }
8332                 if(!this.selectByValue(this.value, true)){
8333                     this.select(0, true);
8334                 }
8335             }else{
8336                 this.selectNext();
8337                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8338                     this.taTask.delay(this.typeAheadDelay);
8339                 }
8340             }
8341         }else{
8342             this.onEmptyResults();
8343         }
8344         
8345         //this.el.focus();
8346     },
8347     // private
8348     onLoadException : function()
8349     {
8350         this.hasQuery = false;
8351         
8352         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8353             this.loading.hide();
8354         }
8355         
8356         this.collapse();
8357         Roo.log(this.store.reader.jsonData);
8358         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8359             // fixme
8360             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8361         }
8362         
8363         
8364     },
8365     // private
8366     onTypeAhead : function(){
8367         if(this.store.getCount() > 0){
8368             var r = this.store.getAt(0);
8369             var newValue = r.data[this.displayField];
8370             var len = newValue.length;
8371             var selStart = this.getRawValue().length;
8372             
8373             if(selStart != len){
8374                 this.setRawValue(newValue);
8375                 this.selectText(selStart, newValue.length);
8376             }
8377         }
8378     },
8379
8380     // private
8381     onSelect : function(record, index){
8382         
8383         if(this.fireEvent('beforeselect', this, record, index) !== false){
8384         
8385             this.setFromData(index > -1 ? record.data : false);
8386             
8387             this.collapse();
8388             this.fireEvent('select', this, record, index);
8389         }
8390     },
8391
8392     /**
8393      * Returns the currently selected field value or empty string if no value is set.
8394      * @return {String} value The selected value
8395      */
8396     getValue : function(){
8397         
8398         if(this.multiple){
8399             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8400         }
8401         
8402         if(this.valueField){
8403             return typeof this.value != 'undefined' ? this.value : '';
8404         }else{
8405             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8406         }
8407     },
8408
8409     /**
8410      * Clears any text/value currently set in the field
8411      */
8412     clearValue : function(){
8413         if(this.hiddenField){
8414             this.hiddenField.dom.value = '';
8415         }
8416         this.value = '';
8417         this.setRawValue('');
8418         this.lastSelectionText = '';
8419         
8420     },
8421
8422     /**
8423      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8424      * will be displayed in the field.  If the value does not match the data value of an existing item,
8425      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8426      * Otherwise the field will be blank (although the value will still be set).
8427      * @param {String} value The value to match
8428      */
8429     setValue : function(v){
8430         if(this.multiple){
8431             this.syncValue();
8432             return;
8433         }
8434         
8435         var text = v;
8436         if(this.valueField){
8437             var r = this.findRecord(this.valueField, v);
8438             if(r){
8439                 text = r.data[this.displayField];
8440             }else if(this.valueNotFoundText !== undefined){
8441                 text = this.valueNotFoundText;
8442             }
8443         }
8444         this.lastSelectionText = text;
8445         if(this.hiddenField){
8446             this.hiddenField.dom.value = v;
8447         }
8448         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8449         this.value = v;
8450     },
8451     /**
8452      * @property {Object} the last set data for the element
8453      */
8454     
8455     lastData : false,
8456     /**
8457      * Sets the value of the field based on a object which is related to the record format for the store.
8458      * @param {Object} value the value to set as. or false on reset?
8459      */
8460     setFromData : function(o){
8461         
8462         if(this.multiple){
8463             this.addItem(o);
8464             return;
8465         }
8466             
8467         var dv = ''; // display value
8468         var vv = ''; // value value..
8469         this.lastData = o;
8470         if (this.displayField) {
8471             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8472         } else {
8473             // this is an error condition!!!
8474             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8475         }
8476         
8477         if(this.valueField){
8478             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8479         }
8480         
8481         if(this.hiddenField){
8482             this.hiddenField.dom.value = vv;
8483             
8484             this.lastSelectionText = dv;
8485             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8486             this.value = vv;
8487             return;
8488         }
8489         // no hidden field.. - we store the value in 'value', but still display
8490         // display field!!!!
8491         this.lastSelectionText = dv;
8492         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8493         this.value = vv;
8494         
8495         
8496     },
8497     // private
8498     reset : function(){
8499         // overridden so that last data is reset..
8500         this.setValue(this.originalValue);
8501         this.clearInvalid();
8502         this.lastData = false;
8503         if (this.view) {
8504             this.view.clearSelections();
8505         }
8506     },
8507     // private
8508     findRecord : function(prop, value){
8509         var record;
8510         if(this.store.getCount() > 0){
8511             this.store.each(function(r){
8512                 if(r.data[prop] == value){
8513                     record = r;
8514                     return false;
8515                 }
8516                 return true;
8517             });
8518         }
8519         return record;
8520     },
8521     
8522     getName: function()
8523     {
8524         // returns hidden if it's set..
8525         if (!this.rendered) {return ''};
8526         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8527         
8528     },
8529     // private
8530     onViewMove : function(e, t){
8531         this.inKeyMode = false;
8532     },
8533
8534     // private
8535     onViewOver : function(e, t){
8536         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8537             return;
8538         }
8539         var item = this.view.findItemFromChild(t);
8540         if(item){
8541             var index = this.view.indexOf(item);
8542             this.select(index, false);
8543         }
8544     },
8545
8546     // private
8547     onViewClick : function(doFocus)
8548     {
8549         var index = this.view.getSelectedIndexes()[0];
8550         var r = this.store.getAt(index);
8551         if(r){
8552             this.onSelect(r, index);
8553         }
8554         if(doFocus !== false && !this.blockFocus){
8555             this.inputEl().focus();
8556         }
8557     },
8558
8559     // private
8560     restrictHeight : function(){
8561         //this.innerList.dom.style.height = '';
8562         //var inner = this.innerList.dom;
8563         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8564         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8565         //this.list.beginUpdate();
8566         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8567         this.list.alignTo(this.inputEl(), this.listAlign);
8568         //this.list.endUpdate();
8569     },
8570
8571     // private
8572     onEmptyResults : function(){
8573         this.collapse();
8574     },
8575
8576     /**
8577      * Returns true if the dropdown list is expanded, else false.
8578      */
8579     isExpanded : function(){
8580         return this.list.isVisible();
8581     },
8582
8583     /**
8584      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8585      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8586      * @param {String} value The data value of the item to select
8587      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8588      * selected item if it is not currently in view (defaults to true)
8589      * @return {Boolean} True if the value matched an item in the list, else false
8590      */
8591     selectByValue : function(v, scrollIntoView){
8592         if(v !== undefined && v !== null){
8593             var r = this.findRecord(this.valueField || this.displayField, v);
8594             if(r){
8595                 this.select(this.store.indexOf(r), scrollIntoView);
8596                 return true;
8597             }
8598         }
8599         return false;
8600     },
8601
8602     /**
8603      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8604      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8605      * @param {Number} index The zero-based index of the list item to select
8606      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8607      * selected item if it is not currently in view (defaults to true)
8608      */
8609     select : function(index, scrollIntoView){
8610         this.selectedIndex = index;
8611         this.view.select(index);
8612         if(scrollIntoView !== false){
8613             var el = this.view.getNode(index);
8614             if(el){
8615                 //this.innerList.scrollChildIntoView(el, false);
8616                 
8617             }
8618         }
8619     },
8620
8621     // private
8622     selectNext : function(){
8623         var ct = this.store.getCount();
8624         if(ct > 0){
8625             if(this.selectedIndex == -1){
8626                 this.select(0);
8627             }else if(this.selectedIndex < ct-1){
8628                 this.select(this.selectedIndex+1);
8629             }
8630         }
8631     },
8632
8633     // private
8634     selectPrev : function(){
8635         var ct = this.store.getCount();
8636         if(ct > 0){
8637             if(this.selectedIndex == -1){
8638                 this.select(0);
8639             }else if(this.selectedIndex != 0){
8640                 this.select(this.selectedIndex-1);
8641             }
8642         }
8643     },
8644
8645     // private
8646     onKeyUp : function(e){
8647         if(this.editable !== false && !e.isSpecialKey()){
8648             this.lastKey = e.getKey();
8649             this.dqTask.delay(this.queryDelay);
8650         }
8651     },
8652
8653     // private
8654     validateBlur : function(){
8655         return !this.list || !this.list.isVisible();   
8656     },
8657
8658     // private
8659     initQuery : function(){
8660         this.doQuery(this.getRawValue());
8661     },
8662
8663     // private
8664     doForce : function(){
8665         if(this.el.dom.value.length > 0){
8666             this.el.dom.value =
8667                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8668              
8669         }
8670     },
8671
8672     /**
8673      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8674      * query allowing the query action to be canceled if needed.
8675      * @param {String} query The SQL query to execute
8676      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8677      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8678      * saved in the current store (defaults to false)
8679      */
8680     doQuery : function(q, forceAll){
8681         
8682         if(q === undefined || q === null){
8683             q = '';
8684         }
8685         var qe = {
8686             query: q,
8687             forceAll: forceAll,
8688             combo: this,
8689             cancel:false
8690         };
8691         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8692             return false;
8693         }
8694         q = qe.query;
8695         
8696         forceAll = qe.forceAll;
8697         if(forceAll === true || (q.length >= this.minChars)){
8698             
8699             this.hasQuery = true;
8700             
8701             if(this.lastQuery != q || this.alwaysQuery){
8702                 this.lastQuery = q;
8703                 if(this.mode == 'local'){
8704                     this.selectedIndex = -1;
8705                     if(forceAll){
8706                         this.store.clearFilter();
8707                     }else{
8708                         this.store.filter(this.displayField, q);
8709                     }
8710                     this.onLoad();
8711                 }else{
8712                     this.store.baseParams[this.queryParam] = q;
8713                     
8714                     var options = {params : this.getParams(q)};
8715                     
8716                     if(this.loadNext){
8717                         options.add = true;
8718                         options.params.start = this.page * this.pageSize;
8719                     }
8720                     
8721                     this.store.load(options);
8722                     this.expand();
8723                 }
8724             }else{
8725                 this.selectedIndex = -1;
8726                 this.onLoad();   
8727             }
8728         }
8729         
8730         this.loadNext = false;
8731     },
8732
8733     // private
8734     getParams : function(q){
8735         var p = {};
8736         //p[this.queryParam] = q;
8737         
8738         if(this.pageSize){
8739             p.start = 0;
8740             p.limit = this.pageSize;
8741         }
8742         return p;
8743     },
8744
8745     /**
8746      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8747      */
8748     collapse : function(){
8749         if(!this.isExpanded()){
8750             return;
8751         }
8752         
8753         this.list.hide();
8754         Roo.get(document).un('mousedown', this.collapseIf, this);
8755         Roo.get(document).un('mousewheel', this.collapseIf, this);
8756         if (!this.editable) {
8757             Roo.get(document).un('keydown', this.listKeyPress, this);
8758         }
8759         this.fireEvent('collapse', this);
8760     },
8761
8762     // private
8763     collapseIf : function(e){
8764         var in_combo  = e.within(this.el);
8765         var in_list =  e.within(this.list);
8766         
8767         if (in_combo || in_list) {
8768             //e.stopPropagation();
8769             return;
8770         }
8771
8772         this.collapse();
8773         
8774     },
8775
8776     /**
8777      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8778      */
8779     expand : function(){
8780        
8781         if(this.isExpanded() || !this.hasFocus){
8782             return;
8783         }
8784          Roo.log('expand');
8785         this.list.alignTo(this.inputEl(), this.listAlign);
8786         this.list.show();
8787         Roo.get(document).on('mousedown', this.collapseIf, this);
8788         Roo.get(document).on('mousewheel', this.collapseIf, this);
8789         if (!this.editable) {
8790             Roo.get(document).on('keydown', this.listKeyPress, this);
8791         }
8792         
8793         this.fireEvent('expand', this);
8794     },
8795
8796     // private
8797     // Implements the default empty TriggerField.onTriggerClick function
8798     onTriggerClick : function()
8799     {
8800         Roo.log('trigger click');
8801         
8802         if(this.disabled){
8803             return;
8804         }
8805         
8806         this.page = 0;
8807         this.loadNext = false;
8808         
8809         if(this.isExpanded()){
8810             this.collapse();
8811             if (!this.blockFocus) {
8812                 this.inputEl().focus();
8813             }
8814             
8815         }else {
8816             this.hasFocus = true;
8817             if(this.triggerAction == 'all') {
8818                 this.doQuery(this.allQuery, true);
8819             } else {
8820                 this.doQuery(this.getRawValue());
8821             }
8822             if (!this.blockFocus) {
8823                 this.inputEl().focus();
8824             }
8825         }
8826     },
8827     listKeyPress : function(e)
8828     {
8829         //Roo.log('listkeypress');
8830         // scroll to first matching element based on key pres..
8831         if (e.isSpecialKey()) {
8832             return false;
8833         }
8834         var k = String.fromCharCode(e.getKey()).toUpperCase();
8835         //Roo.log(k);
8836         var match  = false;
8837         var csel = this.view.getSelectedNodes();
8838         var cselitem = false;
8839         if (csel.length) {
8840             var ix = this.view.indexOf(csel[0]);
8841             cselitem  = this.store.getAt(ix);
8842             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8843                 cselitem = false;
8844             }
8845             
8846         }
8847         
8848         this.store.each(function(v) { 
8849             if (cselitem) {
8850                 // start at existing selection.
8851                 if (cselitem.id == v.id) {
8852                     cselitem = false;
8853                 }
8854                 return true;
8855             }
8856                 
8857             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8858                 match = this.store.indexOf(v);
8859                 return false;
8860             }
8861             return true;
8862         }, this);
8863         
8864         if (match === false) {
8865             return true; // no more action?
8866         }
8867         // scroll to?
8868         this.view.select(match);
8869         var sn = Roo.get(this.view.getSelectedNodes()[0])
8870         //sn.scrollIntoView(sn.dom.parentNode, false);
8871     },
8872     
8873     onViewScroll : function(e, t){
8874         
8875         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8876             return;
8877         }
8878         
8879         this.hasQuery = true;
8880         
8881         this.loading = this.list.select('.loading', true).first();
8882         
8883         if(this.loading === null){
8884             this.list.createChild({
8885                 tag: 'div',
8886                 cls: 'loading select2-more-results select2-active',
8887                 html: 'Loading more results...'
8888             })
8889             
8890             this.loading = this.list.select('.loading', true).first();
8891             
8892             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8893             
8894             this.loading.hide();
8895         }
8896         
8897         this.loading.show();
8898         
8899         var _combo = this;
8900         
8901         this.page++;
8902         this.loadNext = true;
8903         
8904         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8905         
8906         return;
8907     },
8908     
8909     addItem : function(o)
8910     {   
8911         var dv = ''; // display value
8912         
8913         if (this.displayField) {
8914             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8915         } else {
8916             // this is an error condition!!!
8917             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8918         }
8919         
8920         if(!dv.length){
8921             return;
8922         }
8923         
8924         var choice = this.choices.createChild({
8925             tag: 'li',
8926             cls: 'select2-search-choice',
8927             cn: [
8928                 {
8929                     tag: 'div',
8930                     html: dv
8931                 },
8932                 {
8933                     tag: 'a',
8934                     href: '#',
8935                     cls: 'select2-search-choice-close',
8936                     tabindex: '-1'
8937                 }
8938             ]
8939             
8940         }, this.searchField);
8941         
8942         var close = choice.select('a.select2-search-choice-close', true).first()
8943         
8944         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8945         
8946         this.item.push(o);
8947         this.lastData = o;
8948         
8949         this.syncValue();
8950         
8951         this.inputEl().dom.value = '';
8952         
8953     },
8954     
8955     onRemoveItem : function(e, _self, o)
8956     {
8957         Roo.log('remove item');
8958         var index = this.item.indexOf(o.data) * 1;
8959         
8960         if( index < 0){
8961             Roo.log('not this item?!');
8962             return;
8963         }
8964         
8965         this.item.splice(index, 1);
8966         o.item.remove();
8967         
8968         this.syncValue();
8969         
8970         this.fireEvent('remove', this);
8971         
8972     },
8973     
8974     syncValue : function()
8975     {
8976         if(!this.item.length){
8977             this.clearValue();
8978             return;
8979         }
8980             
8981         var value = [];
8982         var _this = this;
8983         Roo.each(this.item, function(i){
8984             if(_this.valueField){
8985                 value.push(i[_this.valueField]);
8986                 return;
8987             }
8988
8989             value.push(i);
8990         });
8991
8992         this.value = value.join(',');
8993
8994         if(this.hiddenField){
8995             this.hiddenField.dom.value = this.value;
8996         }
8997     },
8998     
8999     clearItem : function()
9000     {
9001         if(!this.multiple){
9002             return;
9003         }
9004         
9005         this.item = [];
9006         
9007         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9008            c.remove();
9009         });
9010         
9011         this.syncValue();
9012     }
9013     
9014     
9015
9016     /** 
9017     * @cfg {Boolean} grow 
9018     * @hide 
9019     */
9020     /** 
9021     * @cfg {Number} growMin 
9022     * @hide 
9023     */
9024     /** 
9025     * @cfg {Number} growMax 
9026     * @hide 
9027     */
9028     /**
9029      * @hide
9030      * @method autoSize
9031      */
9032 });
9033 /*
9034  * Based on:
9035  * Ext JS Library 1.1.1
9036  * Copyright(c) 2006-2007, Ext JS, LLC.
9037  *
9038  * Originally Released Under LGPL - original licence link has changed is not relivant.
9039  *
9040  * Fork - LGPL
9041  * <script type="text/javascript">
9042  */
9043
9044 /**
9045  * @class Roo.View
9046  * @extends Roo.util.Observable
9047  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9048  * This class also supports single and multi selection modes. <br>
9049  * Create a data model bound view:
9050  <pre><code>
9051  var store = new Roo.data.Store(...);
9052
9053  var view = new Roo.View({
9054     el : "my-element",
9055     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9056  
9057     singleSelect: true,
9058     selectedClass: "ydataview-selected",
9059     store: store
9060  });
9061
9062  // listen for node click?
9063  view.on("click", function(vw, index, node, e){
9064  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9065  });
9066
9067  // load XML data
9068  dataModel.load("foobar.xml");
9069  </code></pre>
9070  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9071  * <br><br>
9072  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9073  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9074  * 
9075  * Note: old style constructor is still suported (container, template, config)
9076  * 
9077  * @constructor
9078  * Create a new View
9079  * @param {Object} config The config object
9080  * 
9081  */
9082 Roo.View = function(config, depreciated_tpl, depreciated_config){
9083     
9084     if (typeof(depreciated_tpl) == 'undefined') {
9085         // new way.. - universal constructor.
9086         Roo.apply(this, config);
9087         this.el  = Roo.get(this.el);
9088     } else {
9089         // old format..
9090         this.el  = Roo.get(config);
9091         this.tpl = depreciated_tpl;
9092         Roo.apply(this, depreciated_config);
9093     }
9094     this.wrapEl  = this.el.wrap().wrap();
9095     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9096     
9097     
9098     if(typeof(this.tpl) == "string"){
9099         this.tpl = new Roo.Template(this.tpl);
9100     } else {
9101         // support xtype ctors..
9102         this.tpl = new Roo.factory(this.tpl, Roo);
9103     }
9104     
9105     
9106     this.tpl.compile();
9107    
9108   
9109     
9110      
9111     /** @private */
9112     this.addEvents({
9113         /**
9114          * @event beforeclick
9115          * Fires before a click is processed. Returns false to cancel the default action.
9116          * @param {Roo.View} this
9117          * @param {Number} index The index of the target node
9118          * @param {HTMLElement} node The target node
9119          * @param {Roo.EventObject} e The raw event object
9120          */
9121             "beforeclick" : true,
9122         /**
9123          * @event click
9124          * Fires when a template node is clicked.
9125          * @param {Roo.View} this
9126          * @param {Number} index The index of the target node
9127          * @param {HTMLElement} node The target node
9128          * @param {Roo.EventObject} e The raw event object
9129          */
9130             "click" : true,
9131         /**
9132          * @event dblclick
9133          * Fires when a template node is double clicked.
9134          * @param {Roo.View} this
9135          * @param {Number} index The index of the target node
9136          * @param {HTMLElement} node The target node
9137          * @param {Roo.EventObject} e The raw event object
9138          */
9139             "dblclick" : true,
9140         /**
9141          * @event contextmenu
9142          * Fires when a template node is right clicked.
9143          * @param {Roo.View} this
9144          * @param {Number} index The index of the target node
9145          * @param {HTMLElement} node The target node
9146          * @param {Roo.EventObject} e The raw event object
9147          */
9148             "contextmenu" : true,
9149         /**
9150          * @event selectionchange
9151          * Fires when the selected nodes change.
9152          * @param {Roo.View} this
9153          * @param {Array} selections Array of the selected nodes
9154          */
9155             "selectionchange" : true,
9156     
9157         /**
9158          * @event beforeselect
9159          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9160          * @param {Roo.View} this
9161          * @param {HTMLElement} node The node to be selected
9162          * @param {Array} selections Array of currently selected nodes
9163          */
9164             "beforeselect" : true,
9165         /**
9166          * @event preparedata
9167          * Fires on every row to render, to allow you to change the data.
9168          * @param {Roo.View} this
9169          * @param {Object} data to be rendered (change this)
9170          */
9171           "preparedata" : true
9172           
9173           
9174         });
9175
9176
9177
9178     this.el.on({
9179         "click": this.onClick,
9180         "dblclick": this.onDblClick,
9181         "contextmenu": this.onContextMenu,
9182         scope:this
9183     });
9184
9185     this.selections = [];
9186     this.nodes = [];
9187     this.cmp = new Roo.CompositeElementLite([]);
9188     if(this.store){
9189         this.store = Roo.factory(this.store, Roo.data);
9190         this.setStore(this.store, true);
9191     }
9192     
9193     if ( this.footer && this.footer.xtype) {
9194            
9195          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9196         
9197         this.footer.dataSource = this.store
9198         this.footer.container = fctr;
9199         this.footer = Roo.factory(this.footer, Roo);
9200         fctr.insertFirst(this.el);
9201         
9202         // this is a bit insane - as the paging toolbar seems to detach the el..
9203 //        dom.parentNode.parentNode.parentNode
9204          // they get detached?
9205     }
9206     
9207     
9208     Roo.View.superclass.constructor.call(this);
9209     
9210     
9211 };
9212
9213 Roo.extend(Roo.View, Roo.util.Observable, {
9214     
9215      /**
9216      * @cfg {Roo.data.Store} store Data store to load data from.
9217      */
9218     store : false,
9219     
9220     /**
9221      * @cfg {String|Roo.Element} el The container element.
9222      */
9223     el : '',
9224     
9225     /**
9226      * @cfg {String|Roo.Template} tpl The template used by this View 
9227      */
9228     tpl : false,
9229     /**
9230      * @cfg {String} dataName the named area of the template to use as the data area
9231      *                          Works with domtemplates roo-name="name"
9232      */
9233     dataName: false,
9234     /**
9235      * @cfg {String} selectedClass The css class to add to selected nodes
9236      */
9237     selectedClass : "x-view-selected",
9238      /**
9239      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9240      */
9241     emptyText : "",
9242     
9243     /**
9244      * @cfg {String} text to display on mask (default Loading)
9245      */
9246     mask : false,
9247     /**
9248      * @cfg {Boolean} multiSelect Allow multiple selection
9249      */
9250     multiSelect : false,
9251     /**
9252      * @cfg {Boolean} singleSelect Allow single selection
9253      */
9254     singleSelect:  false,
9255     
9256     /**
9257      * @cfg {Boolean} toggleSelect - selecting 
9258      */
9259     toggleSelect : false,
9260     
9261     /**
9262      * Returns the element this view is bound to.
9263      * @return {Roo.Element}
9264      */
9265     getEl : function(){
9266         return this.wrapEl;
9267     },
9268     
9269     
9270
9271     /**
9272      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9273      */
9274     refresh : function(){
9275         Roo.log('refresh');
9276         var t = this.tpl;
9277         
9278         // if we are using something like 'domtemplate', then
9279         // the what gets used is:
9280         // t.applySubtemplate(NAME, data, wrapping data..)
9281         // the outer template then get' applied with
9282         //     the store 'extra data'
9283         // and the body get's added to the
9284         //      roo-name="data" node?
9285         //      <span class='roo-tpl-{name}'></span> ?????
9286         
9287         
9288         
9289         this.clearSelections();
9290         this.el.update("");
9291         var html = [];
9292         var records = this.store.getRange();
9293         if(records.length < 1) {
9294             
9295             // is this valid??  = should it render a template??
9296             
9297             this.el.update(this.emptyText);
9298             return;
9299         }
9300         var el = this.el;
9301         if (this.dataName) {
9302             this.el.update(t.apply(this.store.meta)); //????
9303             el = this.el.child('.roo-tpl-' + this.dataName);
9304         }
9305         
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var data = this.prepareData(records[i].data, i, records[i]);
9308             this.fireEvent("preparedata", this, data, i, records[i]);
9309             html[html.length] = Roo.util.Format.trim(
9310                 this.dataName ?
9311                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9312                     t.apply(data)
9313             );
9314         }
9315         
9316         
9317         
9318         el.update(html.join(""));
9319         this.nodes = el.dom.childNodes;
9320         this.updateIndexes(0);
9321     },
9322     
9323
9324     /**
9325      * Function to override to reformat the data that is sent to
9326      * the template for each node.
9327      * DEPRICATED - use the preparedata event handler.
9328      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9329      * a JSON object for an UpdateManager bound view).
9330      */
9331     prepareData : function(data, index, record)
9332     {
9333         this.fireEvent("preparedata", this, data, index, record);
9334         return data;
9335     },
9336
9337     onUpdate : function(ds, record){
9338          Roo.log('on update');   
9339         this.clearSelections();
9340         var index = this.store.indexOf(record);
9341         var n = this.nodes[index];
9342         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9343         n.parentNode.removeChild(n);
9344         this.updateIndexes(index, index);
9345     },
9346
9347     
9348     
9349 // --------- FIXME     
9350     onAdd : function(ds, records, index)
9351     {
9352         Roo.log(['on Add', ds, records, index] );        
9353         this.clearSelections();
9354         if(this.nodes.length == 0){
9355             this.refresh();
9356             return;
9357         }
9358         var n = this.nodes[index];
9359         for(var i = 0, len = records.length; i < len; i++){
9360             var d = this.prepareData(records[i].data, i, records[i]);
9361             if(n){
9362                 this.tpl.insertBefore(n, d);
9363             }else{
9364                 
9365                 this.tpl.append(this.el, d);
9366             }
9367         }
9368         this.updateIndexes(index);
9369     },
9370
9371     onRemove : function(ds, record, index){
9372         Roo.log('onRemove');
9373         this.clearSelections();
9374         var el = this.dataName  ?
9375             this.el.child('.roo-tpl-' + this.dataName) :
9376             this.el; 
9377         
9378         el.dom.removeChild(this.nodes[index]);
9379         this.updateIndexes(index);
9380     },
9381
9382     /**
9383      * Refresh an individual node.
9384      * @param {Number} index
9385      */
9386     refreshNode : function(index){
9387         this.onUpdate(this.store, this.store.getAt(index));
9388     },
9389
9390     updateIndexes : function(startIndex, endIndex){
9391         var ns = this.nodes;
9392         startIndex = startIndex || 0;
9393         endIndex = endIndex || ns.length - 1;
9394         for(var i = startIndex; i <= endIndex; i++){
9395             ns[i].nodeIndex = i;
9396         }
9397     },
9398
9399     /**
9400      * Changes the data store this view uses and refresh the view.
9401      * @param {Store} store
9402      */
9403     setStore : function(store, initial){
9404         if(!initial && this.store){
9405             this.store.un("datachanged", this.refresh);
9406             this.store.un("add", this.onAdd);
9407             this.store.un("remove", this.onRemove);
9408             this.store.un("update", this.onUpdate);
9409             this.store.un("clear", this.refresh);
9410             this.store.un("beforeload", this.onBeforeLoad);
9411             this.store.un("load", this.onLoad);
9412             this.store.un("loadexception", this.onLoad);
9413         }
9414         if(store){
9415           
9416             store.on("datachanged", this.refresh, this);
9417             store.on("add", this.onAdd, this);
9418             store.on("remove", this.onRemove, this);
9419             store.on("update", this.onUpdate, this);
9420             store.on("clear", this.refresh, this);
9421             store.on("beforeload", this.onBeforeLoad, this);
9422             store.on("load", this.onLoad, this);
9423             store.on("loadexception", this.onLoad, this);
9424         }
9425         
9426         if(store){
9427             this.refresh();
9428         }
9429     },
9430     /**
9431      * onbeforeLoad - masks the loading area.
9432      *
9433      */
9434     onBeforeLoad : function(store,opts)
9435     {
9436          Roo.log('onBeforeLoad');   
9437         if (!opts.add) {
9438             this.el.update("");
9439         }
9440         this.el.mask(this.mask ? this.mask : "Loading" ); 
9441     },
9442     onLoad : function ()
9443     {
9444         this.el.unmask();
9445     },
9446     
9447
9448     /**
9449      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9450      * @param {HTMLElement} node
9451      * @return {HTMLElement} The template node
9452      */
9453     findItemFromChild : function(node){
9454         var el = this.dataName  ?
9455             this.el.child('.roo-tpl-' + this.dataName,true) :
9456             this.el.dom; 
9457         
9458         if(!node || node.parentNode == el){
9459                     return node;
9460             }
9461             var p = node.parentNode;
9462             while(p && p != el){
9463             if(p.parentNode == el){
9464                 return p;
9465             }
9466             p = p.parentNode;
9467         }
9468             return null;
9469     },
9470
9471     /** @ignore */
9472     onClick : function(e){
9473         var item = this.findItemFromChild(e.getTarget());
9474         if(item){
9475             var index = this.indexOf(item);
9476             if(this.onItemClick(item, index, e) !== false){
9477                 this.fireEvent("click", this, index, item, e);
9478             }
9479         }else{
9480             this.clearSelections();
9481         }
9482     },
9483
9484     /** @ignore */
9485     onContextMenu : function(e){
9486         var item = this.findItemFromChild(e.getTarget());
9487         if(item){
9488             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9489         }
9490     },
9491
9492     /** @ignore */
9493     onDblClick : function(e){
9494         var item = this.findItemFromChild(e.getTarget());
9495         if(item){
9496             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9497         }
9498     },
9499
9500     onItemClick : function(item, index, e)
9501     {
9502         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9503             return false;
9504         }
9505         if (this.toggleSelect) {
9506             var m = this.isSelected(item) ? 'unselect' : 'select';
9507             Roo.log(m);
9508             var _t = this;
9509             _t[m](item, true, false);
9510             return true;
9511         }
9512         if(this.multiSelect || this.singleSelect){
9513             if(this.multiSelect && e.shiftKey && this.lastSelection){
9514                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9515             }else{
9516                 this.select(item, this.multiSelect && e.ctrlKey);
9517                 this.lastSelection = item;
9518             }
9519             e.preventDefault();
9520         }
9521         return true;
9522     },
9523
9524     /**
9525      * Get the number of selected nodes.
9526      * @return {Number}
9527      */
9528     getSelectionCount : function(){
9529         return this.selections.length;
9530     },
9531
9532     /**
9533      * Get the currently selected nodes.
9534      * @return {Array} An array of HTMLElements
9535      */
9536     getSelectedNodes : function(){
9537         return this.selections;
9538     },
9539
9540     /**
9541      * Get the indexes of the selected nodes.
9542      * @return {Array}
9543      */
9544     getSelectedIndexes : function(){
9545         var indexes = [], s = this.selections;
9546         for(var i = 0, len = s.length; i < len; i++){
9547             indexes.push(s[i].nodeIndex);
9548         }
9549         return indexes;
9550     },
9551
9552     /**
9553      * Clear all selections
9554      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9555      */
9556     clearSelections : function(suppressEvent){
9557         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9558             this.cmp.elements = this.selections;
9559             this.cmp.removeClass(this.selectedClass);
9560             this.selections = [];
9561             if(!suppressEvent){
9562                 this.fireEvent("selectionchange", this, this.selections);
9563             }
9564         }
9565     },
9566
9567     /**
9568      * Returns true if the passed node is selected
9569      * @param {HTMLElement/Number} node The node or node index
9570      * @return {Boolean}
9571      */
9572     isSelected : function(node){
9573         var s = this.selections;
9574         if(s.length < 1){
9575             return false;
9576         }
9577         node = this.getNode(node);
9578         return s.indexOf(node) !== -1;
9579     },
9580
9581     /**
9582      * Selects nodes.
9583      * @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
9584      * @param {Boolean} keepExisting (optional) true to keep existing selections
9585      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9586      */
9587     select : function(nodeInfo, keepExisting, suppressEvent){
9588         if(nodeInfo instanceof Array){
9589             if(!keepExisting){
9590                 this.clearSelections(true);
9591             }
9592             for(var i = 0, len = nodeInfo.length; i < len; i++){
9593                 this.select(nodeInfo[i], true, true);
9594             }
9595             return;
9596         } 
9597         var node = this.getNode(nodeInfo);
9598         if(!node || this.isSelected(node)){
9599             return; // already selected.
9600         }
9601         if(!keepExisting){
9602             this.clearSelections(true);
9603         }
9604         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9605             Roo.fly(node).addClass(this.selectedClass);
9606             this.selections.push(node);
9607             if(!suppressEvent){
9608                 this.fireEvent("selectionchange", this, this.selections);
9609             }
9610         }
9611         
9612         
9613     },
9614       /**
9615      * Unselects nodes.
9616      * @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
9617      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9619      */
9620     unselect : function(nodeInfo, keepExisting, suppressEvent)
9621     {
9622         if(nodeInfo instanceof Array){
9623             Roo.each(this.selections, function(s) {
9624                 this.unselect(s, nodeInfo);
9625             }, this);
9626             return;
9627         }
9628         var node = this.getNode(nodeInfo);
9629         if(!node || !this.isSelected(node)){
9630             Roo.log("not selected");
9631             return; // not selected.
9632         }
9633         // fireevent???
9634         var ns = [];
9635         Roo.each(this.selections, function(s) {
9636             if (s == node ) {
9637                 Roo.fly(node).removeClass(this.selectedClass);
9638
9639                 return;
9640             }
9641             ns.push(s);
9642         },this);
9643         
9644         this.selections= ns;
9645         this.fireEvent("selectionchange", this, this.selections);
9646     },
9647
9648     /**
9649      * Gets a template node.
9650      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9651      * @return {HTMLElement} The node or null if it wasn't found
9652      */
9653     getNode : function(nodeInfo){
9654         if(typeof nodeInfo == "string"){
9655             return document.getElementById(nodeInfo);
9656         }else if(typeof nodeInfo == "number"){
9657             return this.nodes[nodeInfo];
9658         }
9659         return nodeInfo;
9660     },
9661
9662     /**
9663      * Gets a range template nodes.
9664      * @param {Number} startIndex
9665      * @param {Number} endIndex
9666      * @return {Array} An array of nodes
9667      */
9668     getNodes : function(start, end){
9669         var ns = this.nodes;
9670         start = start || 0;
9671         end = typeof end == "undefined" ? ns.length - 1 : end;
9672         var nodes = [];
9673         if(start <= end){
9674             for(var i = start; i <= end; i++){
9675                 nodes.push(ns[i]);
9676             }
9677         } else{
9678             for(var i = start; i >= end; i--){
9679                 nodes.push(ns[i]);
9680             }
9681         }
9682         return nodes;
9683     },
9684
9685     /**
9686      * Finds the index of the passed node
9687      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9688      * @return {Number} The index of the node or -1
9689      */
9690     indexOf : function(node){
9691         node = this.getNode(node);
9692         if(typeof node.nodeIndex == "number"){
9693             return node.nodeIndex;
9694         }
9695         var ns = this.nodes;
9696         for(var i = 0, len = ns.length; i < len; i++){
9697             if(ns[i] == node){
9698                 return i;
9699             }
9700         }
9701         return -1;
9702     }
9703 });
9704 /*
9705  * - LGPL
9706  *
9707  * based on jquery fullcalendar
9708  * 
9709  */
9710
9711 Roo.bootstrap = Roo.bootstrap || {};
9712 /**
9713  * @class Roo.bootstrap.Calendar
9714  * @extends Roo.bootstrap.Component
9715  * Bootstrap Calendar class
9716  * @cfg {Boolean} loadMask (true|false) default false
9717     
9718  * @constructor
9719  * Create a new Container
9720  * @param {Object} config The config object
9721  */
9722
9723
9724
9725 Roo.bootstrap.Calendar = function(config){
9726     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9727      this.addEvents({
9728         /**
9729              * @event select
9730              * Fires when a date is selected
9731              * @param {DatePicker} this
9732              * @param {Date} date The selected date
9733              */
9734         'select': true,
9735         /**
9736              * @event monthchange
9737              * Fires when the displayed month changes 
9738              * @param {DatePicker} this
9739              * @param {Date} date The selected month
9740              */
9741         'monthchange': true,
9742         /**
9743              * @event evententer
9744              * Fires when mouse over an event
9745              * @param {Calendar} this
9746              * @param {event} Event
9747              */
9748         'evententer': true,
9749         /**
9750              * @event eventleave
9751              * Fires when the mouse leaves an
9752              * @param {Calendar} this
9753              * @param {event}
9754              */
9755         'eventleave': true,
9756         /**
9757              * @event eventclick
9758              * Fires when the mouse click an
9759              * @param {Calendar} this
9760              * @param {event}
9761              */
9762         'eventclick': true
9763         
9764     });
9765
9766 };
9767
9768 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9769     
9770      /**
9771      * @cfg {Number} startDay
9772      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9773      */
9774     startDay : 0,
9775     
9776     loadMask : false,
9777       
9778     getAutoCreate : function(){
9779         
9780         
9781         var fc_button = function(name, corner, style, content ) {
9782             return Roo.apply({},{
9783                 tag : 'span',
9784                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9785                          (corner.length ?
9786                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9787                             ''
9788                         ),
9789                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9790                 unselectable: 'on'
9791             });
9792         };
9793         
9794         var header = {
9795             tag : 'table',
9796             cls : 'fc-header',
9797             style : 'width:100%',
9798             cn : [
9799                 {
9800                     tag: 'tr',
9801                     cn : [
9802                         {
9803                             tag : 'td',
9804                             cls : 'fc-header-left',
9805                             cn : [
9806                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9807                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9808                                 { tag: 'span', cls: 'fc-header-space' },
9809                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9810                                 
9811                                 
9812                             ]
9813                         },
9814                         
9815                         {
9816                             tag : 'td',
9817                             cls : 'fc-header-center',
9818                             cn : [
9819                                 {
9820                                     tag: 'span',
9821                                     cls: 'fc-header-title',
9822                                     cn : {
9823                                         tag: 'H2',
9824                                         html : 'month / year'
9825                                     }
9826                                 }
9827                                 
9828                             ]
9829                         },
9830                         {
9831                             tag : 'td',
9832                             cls : 'fc-header-right',
9833                             cn : [
9834                           /*      fc_button('month', 'left', '', 'month' ),
9835                                 fc_button('week', '', '', 'week' ),
9836                                 fc_button('day', 'right', '', 'day' )
9837                             */    
9838                                 
9839                             ]
9840                         }
9841                         
9842                     ]
9843                 }
9844             ]
9845         };
9846         
9847        
9848         var cal_heads = function() {
9849             var ret = [];
9850             // fixme - handle this.
9851             
9852             for (var i =0; i < Date.dayNames.length; i++) {
9853                 var d = Date.dayNames[i];
9854                 ret.push({
9855                     tag: 'th',
9856                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9857                     html : d.substring(0,3)
9858                 });
9859                 
9860             }
9861             ret[0].cls += ' fc-first';
9862             ret[6].cls += ' fc-last';
9863             return ret;
9864         };
9865         var cal_cell = function(n) {
9866             return  {
9867                 tag: 'td',
9868                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9869                 cn : [
9870                     {
9871                         cn : [
9872                             {
9873                                 cls: 'fc-day-number',
9874                                 html: 'D'
9875                             },
9876                             {
9877                                 cls: 'fc-day-content',
9878                              
9879                                 cn : [
9880                                      {
9881                                         style: 'position: relative;' // height: 17px;
9882                                     }
9883                                 ]
9884                             }
9885                             
9886                             
9887                         ]
9888                     }
9889                 ]
9890                 
9891             }
9892         };
9893         var cal_rows = function() {
9894             
9895             var ret = []
9896             for (var r = 0; r < 6; r++) {
9897                 var row= {
9898                     tag : 'tr',
9899                     cls : 'fc-week',
9900                     cn : []
9901                 };
9902                 
9903                 for (var i =0; i < Date.dayNames.length; i++) {
9904                     var d = Date.dayNames[i];
9905                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9906
9907                 }
9908                 row.cn[0].cls+=' fc-first';
9909                 row.cn[0].cn[0].style = 'min-height:90px';
9910                 row.cn[6].cls+=' fc-last';
9911                 ret.push(row);
9912                 
9913             }
9914             ret[0].cls += ' fc-first';
9915             ret[4].cls += ' fc-prev-last';
9916             ret[5].cls += ' fc-last';
9917             return ret;
9918             
9919         };
9920         
9921         var cal_table = {
9922             tag: 'table',
9923             cls: 'fc-border-separate',
9924             style : 'width:100%',
9925             cellspacing  : 0,
9926             cn : [
9927                 { 
9928                     tag: 'thead',
9929                     cn : [
9930                         { 
9931                             tag: 'tr',
9932                             cls : 'fc-first fc-last',
9933                             cn : cal_heads()
9934                         }
9935                     ]
9936                 },
9937                 { 
9938                     tag: 'tbody',
9939                     cn : cal_rows()
9940                 }
9941                   
9942             ]
9943         };
9944          
9945          var cfg = {
9946             cls : 'fc fc-ltr',
9947             cn : [
9948                 header,
9949                 {
9950                     cls : 'fc-content',
9951                     style : "position: relative;",
9952                     cn : [
9953                         {
9954                             cls : 'fc-view fc-view-month fc-grid',
9955                             style : 'position: relative',
9956                             unselectable : 'on',
9957                             cn : [
9958                                 {
9959                                     cls : 'fc-event-container',
9960                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9961                                 },
9962                                 cal_table
9963                             ]
9964                         }
9965                     ]
9966     
9967                 }
9968            ] 
9969             
9970         };
9971         
9972          
9973         
9974         return cfg;
9975     },
9976     
9977     
9978     initEvents : function()
9979     {
9980         if(!this.store){
9981             throw "can not find store for calendar";
9982         }
9983         
9984         var mark = {
9985             tag: "div",
9986             cls:"x-dlg-mask",
9987             style: "text-align:center",
9988             cn: [
9989                 {
9990                     tag: "div",
9991                     style: "background-color:white;width:50%;margin:250 auto",
9992                     cn: [
9993                         {
9994                             tag: "img",
9995                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9996                         },
9997                         {
9998                             tag: "span",
9999                             html: "Loading"
10000                         }
10001                         
10002                     ]
10003                 }
10004             ]
10005         }
10006         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10007         
10008         var size = this.el.select('.fc-content', true).first().getSize();
10009         this.maskEl.setSize(size.width, size.height);
10010         this.maskEl.enableDisplayMode("block");
10011         if(!this.loadMask){
10012             this.maskEl.hide();
10013         }
10014         
10015         this.store = Roo.factory(this.store, Roo.data);
10016         this.store.on('load', this.onLoad, this);
10017         this.store.on('beforeload', this.onBeforeLoad, this);
10018         
10019         this.resize();
10020         
10021         this.cells = this.el.select('.fc-day',true);
10022         //Roo.log(this.cells);
10023         this.textNodes = this.el.query('.fc-day-number');
10024         this.cells.addClassOnOver('fc-state-hover');
10025         
10026         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10027         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10028         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10029         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10030         
10031         this.on('monthchange', this.onMonthChange, this);
10032         
10033         this.update(new Date().clearTime());
10034     },
10035     
10036     resize : function() {
10037         var sz  = this.el.getSize();
10038         
10039         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10040         this.el.select('.fc-day-content div',true).setHeight(34);
10041     },
10042     
10043     
10044     // private
10045     showPrevMonth : function(e){
10046         this.update(this.activeDate.add("mo", -1));
10047     },
10048     showToday : function(e){
10049         this.update(new Date().clearTime());
10050     },
10051     // private
10052     showNextMonth : function(e){
10053         this.update(this.activeDate.add("mo", 1));
10054     },
10055
10056     // private
10057     showPrevYear : function(){
10058         this.update(this.activeDate.add("y", -1));
10059     },
10060
10061     // private
10062     showNextYear : function(){
10063         this.update(this.activeDate.add("y", 1));
10064     },
10065
10066     
10067    // private
10068     update : function(date)
10069     {
10070         var vd = this.activeDate;
10071         this.activeDate = date;
10072 //        if(vd && this.el){
10073 //            var t = date.getTime();
10074 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10075 //                Roo.log('using add remove');
10076 //                
10077 //                this.fireEvent('monthchange', this, date);
10078 //                
10079 //                this.cells.removeClass("fc-state-highlight");
10080 //                this.cells.each(function(c){
10081 //                   if(c.dateValue == t){
10082 //                       c.addClass("fc-state-highlight");
10083 //                       setTimeout(function(){
10084 //                            try{c.dom.firstChild.focus();}catch(e){}
10085 //                       }, 50);
10086 //                       return false;
10087 //                   }
10088 //                   return true;
10089 //                });
10090 //                return;
10091 //            }
10092 //        }
10093         
10094         var days = date.getDaysInMonth();
10095         
10096         var firstOfMonth = date.getFirstDateOfMonth();
10097         var startingPos = firstOfMonth.getDay()-this.startDay;
10098         
10099         if(startingPos < this.startDay){
10100             startingPos += 7;
10101         }
10102         
10103         var pm = date.add(Date.MONTH, -1);
10104         var prevStart = pm.getDaysInMonth()-startingPos;
10105 //        
10106         this.cells = this.el.select('.fc-day',true);
10107         this.textNodes = this.el.query('.fc-day-number');
10108         this.cells.addClassOnOver('fc-state-hover');
10109         
10110         var cells = this.cells.elements;
10111         var textEls = this.textNodes;
10112         
10113         Roo.each(cells, function(cell){
10114             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10115         });
10116         
10117         days += startingPos;
10118
10119         // convert everything to numbers so it's fast
10120         var day = 86400000;
10121         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10122         //Roo.log(d);
10123         //Roo.log(pm);
10124         //Roo.log(prevStart);
10125         
10126         var today = new Date().clearTime().getTime();
10127         var sel = date.clearTime().getTime();
10128         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10129         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10130         var ddMatch = this.disabledDatesRE;
10131         var ddText = this.disabledDatesText;
10132         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10133         var ddaysText = this.disabledDaysText;
10134         var format = this.format;
10135         
10136         var setCellClass = function(cal, cell){
10137             
10138             //Roo.log('set Cell Class');
10139             cell.title = "";
10140             var t = d.getTime();
10141             
10142             //Roo.log(d);
10143             
10144             cell.dateValue = t;
10145             if(t == today){
10146                 cell.className += " fc-today";
10147                 cell.className += " fc-state-highlight";
10148                 cell.title = cal.todayText;
10149             }
10150             if(t == sel){
10151                 // disable highlight in other month..
10152                 //cell.className += " fc-state-highlight";
10153                 
10154             }
10155             // disabling
10156             if(t < min) {
10157                 cell.className = " fc-state-disabled";
10158                 cell.title = cal.minText;
10159                 return;
10160             }
10161             if(t > max) {
10162                 cell.className = " fc-state-disabled";
10163                 cell.title = cal.maxText;
10164                 return;
10165             }
10166             if(ddays){
10167                 if(ddays.indexOf(d.getDay()) != -1){
10168                     cell.title = ddaysText;
10169                     cell.className = " fc-state-disabled";
10170                 }
10171             }
10172             if(ddMatch && format){
10173                 var fvalue = d.dateFormat(format);
10174                 if(ddMatch.test(fvalue)){
10175                     cell.title = ddText.replace("%0", fvalue);
10176                     cell.className = " fc-state-disabled";
10177                 }
10178             }
10179             
10180             if (!cell.initialClassName) {
10181                 cell.initialClassName = cell.dom.className;
10182             }
10183             
10184             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10185         };
10186
10187         var i = 0;
10188         
10189         for(; i < startingPos; i++) {
10190             textEls[i].innerHTML = (++prevStart);
10191             d.setDate(d.getDate()+1);
10192             
10193             cells[i].className = "fc-past fc-other-month";
10194             setCellClass(this, cells[i]);
10195         }
10196         
10197         var intDay = 0;
10198         
10199         for(; i < days; i++){
10200             intDay = i - startingPos + 1;
10201             textEls[i].innerHTML = (intDay);
10202             d.setDate(d.getDate()+1);
10203             
10204             cells[i].className = ''; // "x-date-active";
10205             setCellClass(this, cells[i]);
10206         }
10207         var extraDays = 0;
10208         
10209         for(; i < 42; i++) {
10210             textEls[i].innerHTML = (++extraDays);
10211             d.setDate(d.getDate()+1);
10212             
10213             cells[i].className = "fc-future fc-other-month";
10214             setCellClass(this, cells[i]);
10215         }
10216         
10217         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10218         
10219         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10220         
10221         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10222         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10223         
10224         if(totalRows != 6){
10225             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10226             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10227         }
10228         
10229         this.fireEvent('monthchange', this, date);
10230         
10231         
10232         /*
10233         if(!this.internalRender){
10234             var main = this.el.dom.firstChild;
10235             var w = main.offsetWidth;
10236             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10237             Roo.fly(main).setWidth(w);
10238             this.internalRender = true;
10239             // opera does not respect the auto grow header center column
10240             // then, after it gets a width opera refuses to recalculate
10241             // without a second pass
10242             if(Roo.isOpera && !this.secondPass){
10243                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10244                 this.secondPass = true;
10245                 this.update.defer(10, this, [date]);
10246             }
10247         }
10248         */
10249         
10250     },
10251     
10252     findCell : function(dt) {
10253         dt = dt.clearTime().getTime();
10254         var ret = false;
10255         this.cells.each(function(c){
10256             //Roo.log("check " +c.dateValue + '?=' + dt);
10257             if(c.dateValue == dt){
10258                 ret = c;
10259                 return false;
10260             }
10261             return true;
10262         });
10263         
10264         return ret;
10265     },
10266     
10267     findCells : function(ev) {
10268         var s = ev.start.clone().clearTime().getTime();
10269        // Roo.log(s);
10270         var e= ev.end.clone().clearTime().getTime();
10271        // Roo.log(e);
10272         var ret = [];
10273         this.cells.each(function(c){
10274              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10275             
10276             if(c.dateValue > e){
10277                 return ;
10278             }
10279             if(c.dateValue < s){
10280                 return ;
10281             }
10282             ret.push(c);
10283         });
10284         
10285         return ret;    
10286     },
10287     
10288     findBestRow: function(cells)
10289     {
10290         var ret = 0;
10291         
10292         for (var i =0 ; i < cells.length;i++) {
10293             ret  = Math.max(cells[i].rows || 0,ret);
10294         }
10295         return ret;
10296         
10297     },
10298     
10299     
10300     addItem : function(ev)
10301     {
10302         // look for vertical location slot in
10303         var cells = this.findCells(ev);
10304         
10305         ev.row = this.findBestRow(cells);
10306         
10307         // work out the location.
10308         
10309         var crow = false;
10310         var rows = [];
10311         for(var i =0; i < cells.length; i++) {
10312             if (!crow) {
10313                 crow = {
10314                     start : cells[i],
10315                     end :  cells[i]
10316                 };
10317                 continue;
10318             }
10319             if (crow.start.getY() == cells[i].getY()) {
10320                 // on same row.
10321                 crow.end = cells[i];
10322                 continue;
10323             }
10324             // different row.
10325             rows.push(crow);
10326             crow = {
10327                 start: cells[i],
10328                 end : cells[i]
10329             };
10330             
10331         }
10332         
10333         rows.push(crow);
10334         ev.els = [];
10335         ev.rows = rows;
10336         ev.cells = cells;
10337         for (var i = 0; i < cells.length;i++) {
10338             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10339             
10340         }
10341         
10342         this.calevents.push(ev);
10343     },
10344     
10345     clearEvents: function() {
10346         
10347         if(!this.calevents){
10348             return;
10349         }
10350         
10351         Roo.each(this.cells.elements, function(c){
10352             c.rows = 0;
10353         });
10354         
10355         Roo.each(this.calevents, function(e) {
10356             Roo.each(e.els, function(el) {
10357                 el.un('mouseenter' ,this.onEventEnter, this);
10358                 el.un('mouseleave' ,this.onEventLeave, this);
10359                 el.remove();
10360             },this);
10361         },this);
10362         
10363     },
10364     
10365     renderEvents: function()
10366     {   
10367         // first make sure there is enough space..
10368         
10369         this.cells.each(function(c) {
10370         
10371             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10372         });
10373         
10374         for (var e = 0; e < this.calevents.length; e++) {
10375             var ev = this.calevents[e];
10376             var cells = ev.cells;
10377             var rows = ev.rows;
10378             
10379             for(var i =0; i < rows.length; i++) {
10380                 
10381                  
10382                 // how many rows should it span..
10383                 
10384                 var  cfg = {
10385                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10386                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10387                     
10388                     unselectable : "on",
10389                     cn : [
10390                         {
10391                             cls: 'fc-event-inner',
10392                             cn : [
10393 //                                {
10394 //                                  tag:'span',
10395 //                                  cls: 'fc-event-time',
10396 //                                  html : cells.length > 1 ? '' : ev.time
10397 //                                },
10398                                 {
10399                                   tag:'span',
10400                                   cls: 'fc-event-title',
10401                                   html : String.format('{0}', ev.title)
10402                                 }
10403                                 
10404                                 
10405                             ]
10406                         },
10407                         {
10408                             cls: 'ui-resizable-handle ui-resizable-e',
10409                             html : '&nbsp;&nbsp;&nbsp'
10410                         }
10411                         
10412                     ]
10413                 };
10414                 if (i == 0) {
10415                     cfg.cls += ' fc-event-start';
10416                 }
10417                 if ((i+1) == rows.length) {
10418                     cfg.cls += ' fc-event-end';
10419                 }
10420                 
10421                 var ctr = this.el.select('.fc-event-container',true).first();
10422                 var cg = ctr.createChild(cfg);
10423                 
10424                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10425                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10426                 cg.on('click', this.onEventClick, this, ev);
10427                 
10428                 ev.els.push(cg);
10429                 
10430                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10431                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10432                 //Roo.log(cg);
10433                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10434                 cg.setWidth(ebox.right - sbox.x -2);
10435             }
10436             
10437             
10438         }
10439         
10440     },
10441     
10442     onEventEnter: function (e, el,event,d) {
10443         this.fireEvent('evententer', this, el, event);
10444     },
10445     
10446     onEventLeave: function (e, el,event,d) {
10447         this.fireEvent('eventleave', this, el, event);
10448     },
10449     
10450     onEventClick: function (e, el,event,d) {
10451         this.fireEvent('eventclick', this, el, event);
10452     },
10453     
10454     onMonthChange: function () {
10455         this.store.load();
10456     },
10457     
10458     onLoad: function () 
10459     {   
10460         this.calevents = [];
10461         var cal = this;
10462         
10463         if(this.store.getCount() > 0){
10464             this.store.data.each(function(d){
10465                cal.addItem({
10466                     id : d.data.id,
10467                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10468                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10469                     time : d.data.start_time,
10470                     title : d.data.title,
10471                     description : d.data.description,
10472                     venue : d.data.venue
10473                 });
10474             });
10475         }
10476         
10477         this.renderEvents();
10478         
10479         if(this.loadMask){
10480             this.maskEl.hide();
10481         }
10482     },
10483     
10484     onBeforeLoad: function()
10485     {
10486         this.clearEvents();
10487         
10488         if(this.loadMask){
10489             this.maskEl.show();
10490         }
10491     }
10492 });
10493
10494  
10495  /*
10496  * - LGPL
10497  *
10498  * element
10499  * 
10500  */
10501
10502 /**
10503  * @class Roo.bootstrap.Popover
10504  * @extends Roo.bootstrap.Component
10505  * Bootstrap Popover class
10506  * @cfg {String} html contents of the popover   (or false to use children..)
10507  * @cfg {String} title of popover (or false to hide)
10508  * @cfg {String} placement how it is placed
10509  * @cfg {String} trigger click || hover (or false to trigger manually)
10510  * @cfg {String} over what (parent or false to trigger manually.)
10511  * 
10512  * @constructor
10513  * Create a new Popover
10514  * @param {Object} config The config object
10515  */
10516
10517 Roo.bootstrap.Popover = function(config){
10518     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10519 };
10520
10521 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10522     
10523     title: 'Fill in a title',
10524     html: false,
10525     
10526     placement : 'right',
10527     trigger : 'hover', // hover
10528     
10529     over: 'parent',
10530     
10531     can_build_overlaid : false,
10532     
10533     getChildContainer : function()
10534     {
10535         return this.el.select('.popover-content',true).first();
10536     },
10537     
10538     getAutoCreate : function(){
10539          Roo.log('make popover?');
10540         var cfg = {
10541            cls : 'popover roo-dynamic',
10542            style: 'display:block',
10543            cn : [
10544                 {
10545                     cls : 'arrow'
10546                 },
10547                 {
10548                     cls : 'popover-inner',
10549                     cn : [
10550                         {
10551                             tag: 'h3',
10552                             cls: 'popover-title',
10553                             html : this.title
10554                         },
10555                         {
10556                             cls : 'popover-content',
10557                             html : this.html
10558                         }
10559                     ]
10560                     
10561                 }
10562            ]
10563         };
10564         
10565         return cfg;
10566     },
10567     setTitle: function(str)
10568     {
10569         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10570     },
10571     setContent: function(str)
10572     {
10573         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10574     },
10575     // as it get's added to the bottom of the page.
10576     onRender : function(ct, position)
10577     {
10578         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10579         if(!this.el){
10580             var cfg = Roo.apply({},  this.getAutoCreate());
10581             cfg.id = Roo.id();
10582             
10583             if (this.cls) {
10584                 cfg.cls += ' ' + this.cls;
10585             }
10586             if (this.style) {
10587                 cfg.style = this.style;
10588             }
10589             Roo.log("adding to ")
10590             this.el = Roo.get(document.body).createChild(cfg, position);
10591             Roo.log(this.el);
10592         }
10593         this.initEvents();
10594     },
10595     
10596     initEvents : function()
10597     {
10598         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10599         this.el.enableDisplayMode('block');
10600         this.el.hide();
10601         if (this.over === false) {
10602             return; 
10603         }
10604         if (this.triggers === false) {
10605             return;
10606         }
10607         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10608         var triggers = this.trigger ? this.trigger.split(' ') : [];
10609         Roo.each(triggers, function(trigger) {
10610         
10611             if (trigger == 'click') {
10612                 on_el.on('click', this.toggle, this);
10613             } else if (trigger != 'manual') {
10614                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10615                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10616       
10617                 on_el.on(eventIn  ,this.enter, this);
10618                 on_el.on(eventOut, this.leave, this);
10619             }
10620         }, this);
10621         
10622     },
10623     
10624     
10625     // private
10626     timeout : null,
10627     hoverState : null,
10628     
10629     toggle : function () {
10630         this.hoverState == 'in' ? this.leave() : this.enter();
10631     },
10632     
10633     enter : function () {
10634        
10635     
10636         clearTimeout(this.timeout);
10637     
10638         this.hoverState = 'in'
10639     
10640         if (!this.delay || !this.delay.show) {
10641             this.show();
10642             return 
10643         }
10644         var _t = this;
10645         this.timeout = setTimeout(function () {
10646             if (_t.hoverState == 'in') {
10647                 _t.show();
10648             }
10649         }, this.delay.show)
10650     },
10651     leave : function() {
10652         clearTimeout(this.timeout);
10653     
10654         this.hoverState = 'out'
10655     
10656         if (!this.delay || !this.delay.hide) {
10657             this.hide();
10658             return 
10659         }
10660         var _t = this;
10661         this.timeout = setTimeout(function () {
10662             if (_t.hoverState == 'out') {
10663                 _t.hide();
10664             }
10665         }, this.delay.hide)
10666     },
10667     
10668     show : function (on_el)
10669     {
10670         if (!on_el) {
10671             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10672         }
10673         // set content.
10674         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10675         if (this.html !== false) {
10676             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10677         }
10678         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10679         if (!this.title.length) {
10680             this.el.select('.popover-title',true).hide();
10681         }
10682         
10683         var placement = typeof this.placement == 'function' ?
10684             this.placement.call(this, this.el, on_el) :
10685             this.placement;
10686             
10687         var autoToken = /\s?auto?\s?/i;
10688         var autoPlace = autoToken.test(placement);
10689         if (autoPlace) {
10690             placement = placement.replace(autoToken, '') || 'top';
10691         }
10692         
10693         //this.el.detach()
10694         //this.el.setXY([0,0]);
10695         this.el.show();
10696         this.el.dom.style.display='block';
10697         this.el.addClass(placement);
10698         
10699         //this.el.appendTo(on_el);
10700         
10701         var p = this.getPosition();
10702         var box = this.el.getBox();
10703         
10704         if (autoPlace) {
10705             // fixme..
10706         }
10707         var align = Roo.bootstrap.Popover.alignment[placement]
10708         this.el.alignTo(on_el, align[0],align[1]);
10709         //var arrow = this.el.select('.arrow',true).first();
10710         //arrow.set(align[2], 
10711         
10712         this.el.addClass('in');
10713         this.hoverState = null;
10714         
10715         if (this.el.hasClass('fade')) {
10716             // fade it?
10717         }
10718         
10719     },
10720     hide : function()
10721     {
10722         this.el.setXY([0,0]);
10723         this.el.removeClass('in');
10724         this.el.hide();
10725         
10726     }
10727     
10728 });
10729
10730 Roo.bootstrap.Popover.alignment = {
10731     'left' : ['r-l', [-10,0], 'right'],
10732     'right' : ['l-r', [10,0], 'left'],
10733     'bottom' : ['t-b', [0,10], 'top'],
10734     'top' : [ 'b-t', [0,-10], 'bottom']
10735 };
10736
10737  /*
10738  * - LGPL
10739  *
10740  * Progress
10741  * 
10742  */
10743
10744 /**
10745  * @class Roo.bootstrap.Progress
10746  * @extends Roo.bootstrap.Component
10747  * Bootstrap Progress class
10748  * @cfg {Boolean} striped striped of the progress bar
10749  * @cfg {Boolean} active animated of the progress bar
10750  * 
10751  * 
10752  * @constructor
10753  * Create a new Progress
10754  * @param {Object} config The config object
10755  */
10756
10757 Roo.bootstrap.Progress = function(config){
10758     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10759 };
10760
10761 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10762     
10763     striped : false,
10764     active: false,
10765     
10766     getAutoCreate : function(){
10767         var cfg = {
10768             tag: 'div',
10769             cls: 'progress'
10770         };
10771         
10772         
10773         if(this.striped){
10774             cfg.cls += ' progress-striped';
10775         }
10776       
10777         if(this.active){
10778             cfg.cls += ' active';
10779         }
10780         
10781         
10782         return cfg;
10783     }
10784    
10785 });
10786
10787  
10788
10789  /*
10790  * - LGPL
10791  *
10792  * ProgressBar
10793  * 
10794  */
10795
10796 /**
10797  * @class Roo.bootstrap.ProgressBar
10798  * @extends Roo.bootstrap.Component
10799  * Bootstrap ProgressBar class
10800  * @cfg {Number} aria_valuenow aria-value now
10801  * @cfg {Number} aria_valuemin aria-value min
10802  * @cfg {Number} aria_valuemax aria-value max
10803  * @cfg {String} label label for the progress bar
10804  * @cfg {String} panel (success | info | warning | danger )
10805  * @cfg {String} role role of the progress bar
10806  * @cfg {String} sr_only text
10807  * 
10808  * 
10809  * @constructor
10810  * Create a new ProgressBar
10811  * @param {Object} config The config object
10812  */
10813
10814 Roo.bootstrap.ProgressBar = function(config){
10815     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10816 };
10817
10818 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10819     
10820     aria_valuenow : 0,
10821     aria_valuemin : 0,
10822     aria_valuemax : 100,
10823     label : false,
10824     panel : false,
10825     role : false,
10826     sr_only: false,
10827     
10828     getAutoCreate : function()
10829     {
10830         
10831         var cfg = {
10832             tag: 'div',
10833             cls: 'progress-bar',
10834             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10835         };
10836         
10837         if(this.sr_only){
10838             cfg.cn = {
10839                 tag: 'span',
10840                 cls: 'sr-only',
10841                 html: this.sr_only
10842             }
10843         }
10844         
10845         if(this.role){
10846             cfg.role = this.role;
10847         }
10848         
10849         if(this.aria_valuenow){
10850             cfg['aria-valuenow'] = this.aria_valuenow;
10851         }
10852         
10853         if(this.aria_valuemin){
10854             cfg['aria-valuemin'] = this.aria_valuemin;
10855         }
10856         
10857         if(this.aria_valuemax){
10858             cfg['aria-valuemax'] = this.aria_valuemax;
10859         }
10860         
10861         if(this.label && !this.sr_only){
10862             cfg.html = this.label;
10863         }
10864         
10865         if(this.panel){
10866             cfg.cls += ' progress-bar-' + this.panel;
10867         }
10868         
10869         return cfg;
10870     },
10871     
10872     update : function(aria_valuenow)
10873     {
10874         this.aria_valuenow = aria_valuenow;
10875         
10876         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10877     }
10878    
10879 });
10880
10881  
10882
10883  /*
10884  * - LGPL
10885  *
10886  * TabPanel
10887  * 
10888  */
10889
10890 /**
10891  * @class Roo.bootstrap.TabPanel
10892  * @extends Roo.bootstrap.Component
10893  * Bootstrap TabPanel class
10894  * @cfg {Boolean} active panel active
10895  * @cfg {String} html panel content
10896  * @cfg {String} tabId tab relate id
10897  * 
10898  * 
10899  * @constructor
10900  * Create a new TabPanel
10901  * @param {Object} config The config object
10902  */
10903
10904 Roo.bootstrap.TabPanel = function(config){
10905     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10906 };
10907
10908 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10909     
10910     active: false,
10911     html: false,
10912     tabId: false,
10913     
10914     getAutoCreate : function(){
10915         var cfg = {
10916             tag: 'div',
10917             cls: 'tab-pane',
10918             html: this.html || ''
10919         };
10920         
10921         if(this.active){
10922             cfg.cls += ' active';
10923         }
10924         
10925         if(this.tabId){
10926             cfg.tabId = this.tabId;
10927         }
10928         
10929         return cfg;
10930     }
10931    
10932 });
10933
10934  
10935
10936  /*
10937  * - LGPL
10938  *
10939  * DateField
10940  * 
10941  */
10942
10943 /**
10944  * @class Roo.bootstrap.DateField
10945  * @extends Roo.bootstrap.Input
10946  * Bootstrap DateField class
10947  * @cfg {Number} weekStart default 0
10948  * @cfg {Number} weekStart default 0
10949  * @cfg {Number} viewMode default empty, (months|years)
10950  * @cfg {Number} minViewMode default empty, (months|years)
10951  * @cfg {Number} startDate default -Infinity
10952  * @cfg {Number} endDate default Infinity
10953  * @cfg {Boolean} todayHighlight default false
10954  * @cfg {Boolean} todayBtn default false
10955  * @cfg {Boolean} calendarWeeks default false
10956  * @cfg {Object} daysOfWeekDisabled default empty
10957  * 
10958  * @cfg {Boolean} keyboardNavigation default true
10959  * @cfg {String} language default en
10960  * 
10961  * @constructor
10962  * Create a new DateField
10963  * @param {Object} config The config object
10964  */
10965
10966 Roo.bootstrap.DateField = function(config){
10967     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10968      this.addEvents({
10969             /**
10970              * @event show
10971              * Fires when this field show.
10972              * @param {Roo.bootstrap.DateField} this
10973              * @param {Mixed} date The date value
10974              */
10975             show : true,
10976             /**
10977              * @event show
10978              * Fires when this field hide.
10979              * @param {Roo.bootstrap.DateField} this
10980              * @param {Mixed} date The date value
10981              */
10982             hide : true,
10983             /**
10984              * @event select
10985              * Fires when select a date.
10986              * @param {Roo.bootstrap.DateField} this
10987              * @param {Mixed} date The date value
10988              */
10989             select : true
10990         });
10991 };
10992
10993 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10994     
10995     /**
10996      * @cfg {String} format
10997      * The default date format string which can be overriden for localization support.  The format must be
10998      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10999      */
11000     format : "m/d/y",
11001     /**
11002      * @cfg {String} altFormats
11003      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11004      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11005      */
11006     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11007     
11008     weekStart : 0,
11009     
11010     viewMode : '',
11011     
11012     minViewMode : '',
11013     
11014     todayHighlight : false,
11015     
11016     todayBtn: false,
11017     
11018     language: 'en',
11019     
11020     keyboardNavigation: true,
11021     
11022     calendarWeeks: false,
11023     
11024     startDate: -Infinity,
11025     
11026     endDate: Infinity,
11027     
11028     daysOfWeekDisabled: [],
11029     
11030     _events: [],
11031     
11032     UTCDate: function()
11033     {
11034         return new Date(Date.UTC.apply(Date, arguments));
11035     },
11036     
11037     UTCToday: function()
11038     {
11039         var today = new Date();
11040         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11041     },
11042     
11043     getDate: function() {
11044             var d = this.getUTCDate();
11045             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11046     },
11047     
11048     getUTCDate: function() {
11049             return this.date;
11050     },
11051     
11052     setDate: function(d) {
11053             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11054     },
11055     
11056     setUTCDate: function(d) {
11057             this.date = d;
11058             this.setValue(this.formatDate(this.date));
11059     },
11060         
11061     onRender: function(ct, position)
11062     {
11063         
11064         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11065         
11066         this.language = this.language || 'en';
11067         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11068         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11069         
11070         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11071         this.format = this.format || 'm/d/y';
11072         this.isInline = false;
11073         this.isInput = true;
11074         this.component = this.el.select('.add-on', true).first() || false;
11075         this.component = (this.component && this.component.length === 0) ? false : this.component;
11076         this.hasInput = this.component && this.inputEL().length;
11077         
11078         if (typeof(this.minViewMode === 'string')) {
11079             switch (this.minViewMode) {
11080                 case 'months':
11081                     this.minViewMode = 1;
11082                     break;
11083                 case 'years':
11084                     this.minViewMode = 2;
11085                     break;
11086                 default:
11087                     this.minViewMode = 0;
11088                     break;
11089             }
11090         }
11091         
11092         if (typeof(this.viewMode === 'string')) {
11093             switch (this.viewMode) {
11094                 case 'months':
11095                     this.viewMode = 1;
11096                     break;
11097                 case 'years':
11098                     this.viewMode = 2;
11099                     break;
11100                 default:
11101                     this.viewMode = 0;
11102                     break;
11103             }
11104         }
11105                 
11106         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11107         
11108         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11109         
11110         this.picker().on('mousedown', this.onMousedown, this);
11111         this.picker().on('click', this.onClick, this);
11112         
11113         this.picker().addClass('datepicker-dropdown');
11114         
11115         this.startViewMode = this.viewMode;
11116         
11117         
11118         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11119             if(!this.calendarWeeks){
11120                 v.remove();
11121                 return;
11122             };
11123             
11124             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11125             v.attr('colspan', function(i, val){
11126                 return parseInt(val) + 1;
11127             });
11128         })
11129                         
11130         
11131         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11132         
11133         this.setStartDate(this.startDate);
11134         this.setEndDate(this.endDate);
11135         
11136         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11137         
11138         this.fillDow();
11139         this.fillMonths();
11140         this.update();
11141         this.showMode();
11142         
11143         if(this.isInline) {
11144             this.show();
11145         }
11146     },
11147     
11148     picker : function()
11149     {
11150         return this.el.select('.datepicker', true).first();
11151     },
11152     
11153     fillDow: function()
11154     {
11155         var dowCnt = this.weekStart;
11156         
11157         var dow = {
11158             tag: 'tr',
11159             cn: [
11160                 
11161             ]
11162         };
11163         
11164         if(this.calendarWeeks){
11165             dow.cn.push({
11166                 tag: 'th',
11167                 cls: 'cw',
11168                 html: '&nbsp;'
11169             })
11170         }
11171         
11172         while (dowCnt < this.weekStart + 7) {
11173             dow.cn.push({
11174                 tag: 'th',
11175                 cls: 'dow',
11176                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11177             });
11178         }
11179         
11180         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11181     },
11182     
11183     fillMonths: function()
11184     {    
11185         var i = 0
11186         var months = this.picker().select('>.datepicker-months td', true).first();
11187         
11188         months.dom.innerHTML = '';
11189         
11190         while (i < 12) {
11191             var month = {
11192                 tag: 'span',
11193                 cls: 'month',
11194                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11195             }
11196             
11197             months.createChild(month);
11198         }
11199         
11200     },
11201     
11202     update: function(){
11203         
11204         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11205         
11206         if (this.date < this.startDate) {
11207             this.viewDate = new Date(this.startDate);
11208         } else if (this.date > this.endDate) {
11209             this.viewDate = new Date(this.endDate);
11210         } else {
11211             this.viewDate = new Date(this.date);
11212         }
11213         
11214         this.fill();
11215     },
11216     
11217     fill: function() {
11218         var d = new Date(this.viewDate),
11219                 year = d.getUTCFullYear(),
11220                 month = d.getUTCMonth(),
11221                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11222                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11223                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11224                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11225                 currentDate = this.date && this.date.valueOf(),
11226                 today = this.UTCToday();
11227         
11228         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11229         
11230 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11231         
11232 //        this.picker.select('>tfoot th.today').
11233 //                                              .text(dates[this.language].today)
11234 //                                              .toggle(this.todayBtn !== false);
11235     
11236         this.updateNavArrows();
11237         this.fillMonths();
11238                                                 
11239         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11240         
11241         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11242          
11243         prevMonth.setUTCDate(day);
11244         
11245         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11246         
11247         var nextMonth = new Date(prevMonth);
11248         
11249         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11250         
11251         nextMonth = nextMonth.valueOf();
11252         
11253         var fillMonths = false;
11254         
11255         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11256         
11257         while(prevMonth.valueOf() < nextMonth) {
11258             var clsName = '';
11259             
11260             if (prevMonth.getUTCDay() === this.weekStart) {
11261                 if(fillMonths){
11262                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11263                 }
11264                     
11265                 fillMonths = {
11266                     tag: 'tr',
11267                     cn: []
11268                 };
11269                 
11270                 if(this.calendarWeeks){
11271                     // ISO 8601: First week contains first thursday.
11272                     // ISO also states week starts on Monday, but we can be more abstract here.
11273                     var
11274                     // Start of current week: based on weekstart/current date
11275                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11276                     // Thursday of this week
11277                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11278                     // First Thursday of year, year from thursday
11279                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11280                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11281                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11282                     
11283                     fillMonths.cn.push({
11284                         tag: 'td',
11285                         cls: 'cw',
11286                         html: calWeek
11287                     });
11288                 }
11289             }
11290             
11291             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11292                 clsName += ' old';
11293             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11294                 clsName += ' new';
11295             }
11296             if (this.todayHighlight &&
11297                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11298                 prevMonth.getUTCMonth() == today.getMonth() &&
11299                 prevMonth.getUTCDate() == today.getDate()) {
11300                 clsName += ' today';
11301             }
11302             
11303             if (currentDate && prevMonth.valueOf() === currentDate) {
11304                 clsName += ' active';
11305             }
11306             
11307             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11308                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11309                     clsName += ' disabled';
11310             }
11311             
11312             fillMonths.cn.push({
11313                 tag: 'td',
11314                 cls: 'day ' + clsName,
11315                 html: prevMonth.getDate()
11316             })
11317             
11318             prevMonth.setDate(prevMonth.getDate()+1);
11319         }
11320           
11321         var currentYear = this.date && this.date.getUTCFullYear();
11322         var currentMonth = this.date && this.date.getUTCMonth();
11323         
11324         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11325         
11326         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11327             v.removeClass('active');
11328             
11329             if(currentYear === year && k === currentMonth){
11330                 v.addClass('active');
11331             }
11332             
11333             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11334                 v.addClass('disabled');
11335             }
11336             
11337         });
11338         
11339         
11340         year = parseInt(year/10, 10) * 10;
11341         
11342         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11343         
11344         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11345         
11346         year -= 1;
11347         for (var i = -1; i < 11; i++) {
11348             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11349                 tag: 'span',
11350                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11351                 html: year
11352             })
11353             
11354             year += 1;
11355         }
11356     },
11357     
11358     showMode: function(dir) {
11359         if (dir) {
11360             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11361         }
11362         Roo.each(this.picker().select('>div',true).elements, function(v){
11363             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11364             v.hide();
11365         });
11366         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11367     },
11368     
11369     place: function()
11370     {
11371         if(this.isInline) return;
11372         
11373         this.picker().removeClass(['bottom', 'top']);
11374         
11375         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11376             /*
11377              * place to the top of element!
11378              *
11379              */
11380             
11381             this.picker().addClass('top');
11382             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11383             
11384             return;
11385         }
11386         
11387         this.picker().addClass('bottom');
11388         
11389         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11390     },
11391     
11392     parseDate : function(value){
11393         if(!value || value instanceof Date){
11394             return value;
11395         }
11396         var v = Date.parseDate(value, this.format);
11397         if (!v && this.useIso) {
11398             v = Date.parseDate(value, 'Y-m-d');
11399         }
11400         if(!v && this.altFormats){
11401             if(!this.altFormatsArray){
11402                 this.altFormatsArray = this.altFormats.split("|");
11403             }
11404             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11405                 v = Date.parseDate(value, this.altFormatsArray[i]);
11406             }
11407         }
11408         return v;
11409     },
11410     
11411     formatDate : function(date, fmt){
11412         return (!date || !(date instanceof Date)) ?
11413         date : date.dateFormat(fmt || this.format);
11414     },
11415     
11416     onFocus : function()
11417     {
11418         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11419         this.show();
11420     },
11421     
11422     onBlur : function()
11423     {
11424         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11425         this.hide();
11426     },
11427     
11428     show : function()
11429     {
11430         this.picker().show();
11431         this.update();
11432         this.place();
11433         
11434         this.fireEvent('show', this, this.date);
11435     },
11436     
11437     hide : function()
11438     {
11439         if(this.isInline) return;
11440         this.picker().hide();
11441         this.viewMode = this.startViewMode;
11442         this.showMode();
11443         
11444         this.fireEvent('hide', this, this.date);
11445         
11446     },
11447     
11448     onMousedown: function(e){
11449         e.stopPropagation();
11450         e.preventDefault();
11451     },
11452     
11453     keyup: function(e){
11454         Roo.bootstrap.DateField.superclass.keyup.call(this);
11455         this.update();
11456         
11457     },
11458
11459     setValue: function(v){
11460         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11461         
11462         this.fireEvent('select', this, this.date);
11463         
11464     },
11465     
11466     fireKey: function(e){
11467         if (!this.picker().isVisible()){
11468             if (e.keyCode == 27) // allow escape to hide and re-show picker
11469                 this.show();
11470             return;
11471         }
11472         var dateChanged = false,
11473         dir, day, month,
11474         newDate, newViewDate;
11475         switch(e.keyCode){
11476             case 27: // escape
11477                 this.hide();
11478                 e.preventDefault();
11479                 break;
11480             case 37: // left
11481             case 39: // right
11482                 if (!this.keyboardNavigation) break;
11483                 dir = e.keyCode == 37 ? -1 : 1;
11484                 
11485                 if (e.ctrlKey){
11486                     newDate = this.moveYear(this.date, dir);
11487                     newViewDate = this.moveYear(this.viewDate, dir);
11488                 } else if (e.shiftKey){
11489                     newDate = this.moveMonth(this.date, dir);
11490                     newViewDate = this.moveMonth(this.viewDate, dir);
11491                 } else {
11492                     newDate = new Date(this.date);
11493                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11494                     newViewDate = new Date(this.viewDate);
11495                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11496                 }
11497                 if (this.dateWithinRange(newDate)){
11498                     this.date = newDate;
11499                     this.viewDate = newViewDate;
11500                     this.setValue(this.formatDate(this.date));
11501                     this.update();
11502                     e.preventDefault();
11503                     dateChanged = true;
11504                 }
11505                 break;
11506             case 38: // up
11507             case 40: // down
11508                 if (!this.keyboardNavigation) break;
11509                 dir = e.keyCode == 38 ? -1 : 1;
11510                 if (e.ctrlKey){
11511                     newDate = this.moveYear(this.date, dir);
11512                     newViewDate = this.moveYear(this.viewDate, dir);
11513                 } else if (e.shiftKey){
11514                     newDate = this.moveMonth(this.date, dir);
11515                     newViewDate = this.moveMonth(this.viewDate, dir);
11516                 } else {
11517                     newDate = new Date(this.date);
11518                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11519                     newViewDate = new Date(this.viewDate);
11520                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11521                 }
11522                 if (this.dateWithinRange(newDate)){
11523                     this.date = newDate;
11524                     this.viewDate = newViewDate;
11525                     this.setValue(this.formatDate(this.date));
11526                     this.update();
11527                     e.preventDefault();
11528                     dateChanged = true;
11529                 }
11530                 break;
11531             case 13: // enter
11532                 this.setValue(this.formatDate(this.date));
11533                 this.hide();
11534                 e.preventDefault();
11535                 break;
11536             case 9: // tab
11537                 this.setValue(this.formatDate(this.date));
11538                 this.hide();
11539                 break;
11540         }
11541     },
11542     
11543     
11544     onClick: function(e) {
11545         e.stopPropagation();
11546         e.preventDefault();
11547         
11548         var target = e.getTarget();
11549         
11550         if(target.nodeName.toLowerCase() === 'i'){
11551             target = Roo.get(target).dom.parentNode;
11552         }
11553         
11554         var nodeName = target.nodeName;
11555         var className = target.className;
11556         var html = target.innerHTML;
11557         
11558         switch(nodeName.toLowerCase()) {
11559             case 'th':
11560                 switch(className) {
11561                     case 'switch':
11562                         this.showMode(1);
11563                         break;
11564                     case 'prev':
11565                     case 'next':
11566                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11567                         switch(this.viewMode){
11568                                 case 0:
11569                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11570                                         break;
11571                                 case 1:
11572                                 case 2:
11573                                         this.viewDate = this.moveYear(this.viewDate, dir);
11574                                         break;
11575                         }
11576                         this.fill();
11577                         break;
11578                     case 'today':
11579                         var date = new Date();
11580                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11581                         this.fill()
11582                         this.setValue(this.formatDate(this.date));
11583                         this.hide();
11584                         break;
11585                 }
11586                 break;
11587             case 'span':
11588                 if (className.indexOf('disabled') === -1) {
11589                     this.viewDate.setUTCDate(1);
11590                     if (className.indexOf('month') !== -1) {
11591                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11592                     } else {
11593                         var year = parseInt(html, 10) || 0;
11594                         this.viewDate.setUTCFullYear(year);
11595                         
11596                     }
11597                     this.showMode(-1);
11598                     this.fill();
11599                 }
11600                 break;
11601                 
11602             case 'td':
11603                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11604                     var day = parseInt(html, 10) || 1;
11605                     var year = this.viewDate.getUTCFullYear(),
11606                         month = this.viewDate.getUTCMonth();
11607
11608                     if (className.indexOf('old') !== -1) {
11609                         if(month === 0 ){
11610                             month = 11;
11611                             year -= 1;
11612                         }else{
11613                             month -= 1;
11614                         }
11615                     } else if (className.indexOf('new') !== -1) {
11616                         if (month == 11) {
11617                             month = 0;
11618                             year += 1;
11619                         } else {
11620                             month += 1;
11621                         }
11622                     }
11623                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11624                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11625                     this.fill();
11626                     this.setValue(this.formatDate(this.date));
11627                     this.hide();
11628                 }
11629                 break;
11630         }
11631     },
11632     
11633     setStartDate: function(startDate){
11634         this.startDate = startDate || -Infinity;
11635         if (this.startDate !== -Infinity) {
11636             this.startDate = this.parseDate(this.startDate);
11637         }
11638         this.update();
11639         this.updateNavArrows();
11640     },
11641
11642     setEndDate: function(endDate){
11643         this.endDate = endDate || Infinity;
11644         if (this.endDate !== Infinity) {
11645             this.endDate = this.parseDate(this.endDate);
11646         }
11647         this.update();
11648         this.updateNavArrows();
11649     },
11650     
11651     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11652         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11653         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11654             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11655         }
11656         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11657             return parseInt(d, 10);
11658         });
11659         this.update();
11660         this.updateNavArrows();
11661     },
11662     
11663     updateNavArrows: function() {
11664         var d = new Date(this.viewDate),
11665         year = d.getUTCFullYear(),
11666         month = d.getUTCMonth();
11667         
11668         Roo.each(this.picker().select('.prev', true).elements, function(v){
11669             v.show();
11670             switch (this.viewMode) {
11671                 case 0:
11672
11673                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11674                         v.hide();
11675                     }
11676                     break;
11677                 case 1:
11678                 case 2:
11679                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11680                         v.hide();
11681                     }
11682                     break;
11683             }
11684         });
11685         
11686         Roo.each(this.picker().select('.next', true).elements, function(v){
11687             v.show();
11688             switch (this.viewMode) {
11689                 case 0:
11690
11691                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11692                         v.hide();
11693                     }
11694                     break;
11695                 case 1:
11696                 case 2:
11697                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11698                         v.hide();
11699                     }
11700                     break;
11701             }
11702         })
11703     },
11704     
11705     moveMonth: function(date, dir){
11706         if (!dir) return date;
11707         var new_date = new Date(date.valueOf()),
11708         day = new_date.getUTCDate(),
11709         month = new_date.getUTCMonth(),
11710         mag = Math.abs(dir),
11711         new_month, test;
11712         dir = dir > 0 ? 1 : -1;
11713         if (mag == 1){
11714             test = dir == -1
11715             // If going back one month, make sure month is not current month
11716             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11717             ? function(){
11718                 return new_date.getUTCMonth() == month;
11719             }
11720             // If going forward one month, make sure month is as expected
11721             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11722             : function(){
11723                 return new_date.getUTCMonth() != new_month;
11724             };
11725             new_month = month + dir;
11726             new_date.setUTCMonth(new_month);
11727             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11728             if (new_month < 0 || new_month > 11)
11729                 new_month = (new_month + 12) % 12;
11730         } else {
11731             // For magnitudes >1, move one month at a time...
11732             for (var i=0; i<mag; i++)
11733                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11734                 new_date = this.moveMonth(new_date, dir);
11735             // ...then reset the day, keeping it in the new month
11736             new_month = new_date.getUTCMonth();
11737             new_date.setUTCDate(day);
11738             test = function(){
11739                 return new_month != new_date.getUTCMonth();
11740             };
11741         }
11742         // Common date-resetting loop -- if date is beyond end of month, make it
11743         // end of month
11744         while (test()){
11745             new_date.setUTCDate(--day);
11746             new_date.setUTCMonth(new_month);
11747         }
11748         return new_date;
11749     },
11750
11751     moveYear: function(date, dir){
11752         return this.moveMonth(date, dir*12);
11753     },
11754
11755     dateWithinRange: function(date){
11756         return date >= this.startDate && date <= this.endDate;
11757     },
11758
11759     
11760     remove: function() {
11761         this.picker().remove();
11762     }
11763    
11764 });
11765
11766 Roo.apply(Roo.bootstrap.DateField,  {
11767     
11768     head : {
11769         tag: 'thead',
11770         cn: [
11771         {
11772             tag: 'tr',
11773             cn: [
11774             {
11775                 tag: 'th',
11776                 cls: 'prev',
11777                 html: '<i class="icon-arrow-left"/>'
11778             },
11779             {
11780                 tag: 'th',
11781                 cls: 'switch',
11782                 colspan: '5'
11783             },
11784             {
11785                 tag: 'th',
11786                 cls: 'next',
11787                 html: '<i class="icon-arrow-right"/>'
11788             }
11789
11790             ]
11791         }
11792         ]
11793     },
11794     
11795     content : {
11796         tag: 'tbody',
11797         cn: [
11798         {
11799             tag: 'tr',
11800             cn: [
11801             {
11802                 tag: 'td',
11803                 colspan: '7'
11804             }
11805             ]
11806         }
11807         ]
11808     },
11809     
11810     footer : {
11811         tag: 'tfoot',
11812         cn: [
11813         {
11814             tag: 'tr',
11815             cn: [
11816             {
11817                 tag: 'th',
11818                 colspan: '7',
11819                 cls: 'today'
11820             }
11821                     
11822             ]
11823         }
11824         ]
11825     },
11826     
11827     dates:{
11828         en: {
11829             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11830             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11831             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11832             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11833             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11834             today: "Today"
11835         }
11836     },
11837     
11838     modes: [
11839     {
11840         clsName: 'days',
11841         navFnc: 'Month',
11842         navStep: 1
11843     },
11844     {
11845         clsName: 'months',
11846         navFnc: 'FullYear',
11847         navStep: 1
11848     },
11849     {
11850         clsName: 'years',
11851         navFnc: 'FullYear',
11852         navStep: 10
11853     }]
11854 });
11855
11856 Roo.apply(Roo.bootstrap.DateField,  {
11857   
11858     template : {
11859         tag: 'div',
11860         cls: 'datepicker dropdown-menu',
11861         cn: [
11862         {
11863             tag: 'div',
11864             cls: 'datepicker-days',
11865             cn: [
11866             {
11867                 tag: 'table',
11868                 cls: 'table-condensed',
11869                 cn:[
11870                 Roo.bootstrap.DateField.head,
11871                 {
11872                     tag: 'tbody'
11873                 },
11874                 Roo.bootstrap.DateField.footer
11875                 ]
11876             }
11877             ]
11878         },
11879         {
11880             tag: 'div',
11881             cls: 'datepicker-months',
11882             cn: [
11883             {
11884                 tag: 'table',
11885                 cls: 'table-condensed',
11886                 cn:[
11887                 Roo.bootstrap.DateField.head,
11888                 Roo.bootstrap.DateField.content,
11889                 Roo.bootstrap.DateField.footer
11890                 ]
11891             }
11892             ]
11893         },
11894         {
11895             tag: 'div',
11896             cls: 'datepicker-years',
11897             cn: [
11898             {
11899                 tag: 'table',
11900                 cls: 'table-condensed',
11901                 cn:[
11902                 Roo.bootstrap.DateField.head,
11903                 Roo.bootstrap.DateField.content,
11904                 Roo.bootstrap.DateField.footer
11905                 ]
11906             }
11907             ]
11908         }
11909         ]
11910     }
11911 });
11912
11913  
11914
11915  /*
11916  * - LGPL
11917  *
11918  * TimeField
11919  * 
11920  */
11921
11922 /**
11923  * @class Roo.bootstrap.TimeField
11924  * @extends Roo.bootstrap.Input
11925  * Bootstrap DateField class
11926  * 
11927  * 
11928  * @constructor
11929  * Create a new TimeField
11930  * @param {Object} config The config object
11931  */
11932
11933 Roo.bootstrap.TimeField = function(config){
11934     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11935     this.addEvents({
11936             /**
11937              * @event show
11938              * Fires when this field show.
11939              * @param {Roo.bootstrap.DateField} this
11940              * @param {Mixed} date The date value
11941              */
11942             show : true,
11943             /**
11944              * @event show
11945              * Fires when this field hide.
11946              * @param {Roo.bootstrap.DateField} this
11947              * @param {Mixed} date The date value
11948              */
11949             hide : true,
11950             /**
11951              * @event select
11952              * Fires when select a date.
11953              * @param {Roo.bootstrap.DateField} this
11954              * @param {Mixed} date The date value
11955              */
11956             select : true
11957         });
11958 };
11959
11960 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11961     
11962     /**
11963      * @cfg {String} format
11964      * The default time format string which can be overriden for localization support.  The format must be
11965      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11966      */
11967     format : "H:i",
11968        
11969     onRender: function(ct, position)
11970     {
11971         
11972         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11973                 
11974         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11975         
11976         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11977         
11978         this.pop = this.picker().select('>.datepicker-time',true).first();
11979         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11980         
11981         this.picker().on('mousedown', this.onMousedown, this);
11982         this.picker().on('click', this.onClick, this);
11983         
11984         this.picker().addClass('datepicker-dropdown');
11985     
11986         this.fillTime();
11987         this.update();
11988             
11989         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11990         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11991         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11992         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11993         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11994         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11995
11996     },
11997     
11998     fireKey: function(e){
11999         if (!this.picker().isVisible()){
12000             if (e.keyCode == 27) // allow escape to hide and re-show picker
12001                 this.show();
12002             return;
12003         }
12004
12005         e.preventDefault();
12006         
12007         switch(e.keyCode){
12008             case 27: // escape
12009                 this.hide();
12010                 break;
12011             case 37: // left
12012             case 39: // right
12013                 this.onTogglePeriod();
12014                 break;
12015             case 38: // up
12016                 this.onIncrementMinutes();
12017                 break;
12018             case 40: // down
12019                 this.onDecrementMinutes();
12020                 break;
12021             case 13: // enter
12022             case 9: // tab
12023                 this.setTime();
12024                 break;
12025         }
12026     },
12027     
12028     onClick: function(e) {
12029         e.stopPropagation();
12030         e.preventDefault();
12031     },
12032     
12033     picker : function()
12034     {
12035         return this.el.select('.datepicker', true).first();
12036     },
12037     
12038     fillTime: function()
12039     {    
12040         var time = this.pop.select('tbody', true).first();
12041         
12042         time.dom.innerHTML = '';
12043         
12044         time.createChild({
12045             tag: 'tr',
12046             cn: [
12047                 {
12048                     tag: 'td',
12049                     cn: [
12050                         {
12051                             tag: 'a',
12052                             href: '#',
12053                             cls: 'btn',
12054                             cn: [
12055                                 {
12056                                     tag: 'span',
12057                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12058                                 }
12059                             ]
12060                         } 
12061                     ]
12062                 },
12063                 {
12064                     tag: 'td',
12065                     cls: 'separator'
12066                 },
12067                 {
12068                     tag: 'td',
12069                     cn: [
12070                         {
12071                             tag: 'a',
12072                             href: '#',
12073                             cls: 'btn',
12074                             cn: [
12075                                 {
12076                                     tag: 'span',
12077                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12078                                 }
12079                             ]
12080                         }
12081                     ]
12082                 },
12083                 {
12084                     tag: 'td',
12085                     cls: 'separator'
12086                 }
12087             ]
12088         });
12089         
12090         time.createChild({
12091             tag: 'tr',
12092             cn: [
12093                 {
12094                     tag: 'td',
12095                     cn: [
12096                         {
12097                             tag: 'span',
12098                             cls: 'timepicker-hour',
12099                             html: '00'
12100                         }  
12101                     ]
12102                 },
12103                 {
12104                     tag: 'td',
12105                     cls: 'separator',
12106                     html: ':'
12107                 },
12108                 {
12109                     tag: 'td',
12110                     cn: [
12111                         {
12112                             tag: 'span',
12113                             cls: 'timepicker-minute',
12114                             html: '00'
12115                         }  
12116                     ]
12117                 },
12118                 {
12119                     tag: 'td',
12120                     cls: 'separator'
12121                 },
12122                 {
12123                     tag: 'td',
12124                     cn: [
12125                         {
12126                             tag: 'button',
12127                             type: 'button',
12128                             cls: 'btn btn-primary period',
12129                             html: 'AM'
12130                             
12131                         }
12132                     ]
12133                 }
12134             ]
12135         });
12136         
12137         time.createChild({
12138             tag: 'tr',
12139             cn: [
12140                 {
12141                     tag: 'td',
12142                     cn: [
12143                         {
12144                             tag: 'a',
12145                             href: '#',
12146                             cls: 'btn',
12147                             cn: [
12148                                 {
12149                                     tag: 'span',
12150                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12151                                 }
12152                             ]
12153                         }
12154                     ]
12155                 },
12156                 {
12157                     tag: 'td',
12158                     cls: 'separator'
12159                 },
12160                 {
12161                     tag: 'td',
12162                     cn: [
12163                         {
12164                             tag: 'a',
12165                             href: '#',
12166                             cls: 'btn',
12167                             cn: [
12168                                 {
12169                                     tag: 'span',
12170                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12171                                 }
12172                             ]
12173                         }
12174                     ]
12175                 },
12176                 {
12177                     tag: 'td',
12178                     cls: 'separator'
12179                 }
12180             ]
12181         });
12182         
12183     },
12184     
12185     update: function()
12186     {
12187         
12188         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12189         
12190         this.fill();
12191     },
12192     
12193     fill: function() 
12194     {
12195         var hours = this.time.getHours();
12196         var minutes = this.time.getMinutes();
12197         var period = 'AM';
12198         
12199         if(hours > 11){
12200             period = 'PM';
12201         }
12202         
12203         if(hours == 0){
12204             hours = 12;
12205         }
12206         
12207         
12208         if(hours > 12){
12209             hours = hours - 12;
12210         }
12211         
12212         if(hours < 10){
12213             hours = '0' + hours;
12214         }
12215         
12216         if(minutes < 10){
12217             minutes = '0' + minutes;
12218         }
12219         
12220         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12221         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12222         this.pop.select('button', true).first().dom.innerHTML = period;
12223         
12224     },
12225     
12226     place: function()
12227     {   
12228         this.picker().removeClass(['bottom', 'top']);
12229         
12230         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12231             /*
12232              * place to the top of element!
12233              *
12234              */
12235             
12236             this.picker().addClass('top');
12237             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12238             
12239             return;
12240         }
12241         
12242         this.picker().addClass('bottom');
12243         
12244         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12245     },
12246   
12247     onFocus : function()
12248     {
12249         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12250         this.show();
12251     },
12252     
12253     onBlur : function()
12254     {
12255         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12256         this.hide();
12257     },
12258     
12259     show : function()
12260     {
12261         this.picker().show();
12262         this.pop.show();
12263         this.update();
12264         this.place();
12265         
12266         this.fireEvent('show', this, this.date);
12267     },
12268     
12269     hide : function()
12270     {
12271         this.picker().hide();
12272         this.pop.hide();
12273         
12274         this.fireEvent('hide', this, this.date);
12275     },
12276     
12277     setTime : function()
12278     {
12279         this.hide();
12280         this.setValue(this.time.format(this.format));
12281         
12282         this.fireEvent('select', this, this.date);
12283         
12284         
12285     },
12286     
12287     onMousedown: function(e){
12288         e.stopPropagation();
12289         e.preventDefault();
12290     },
12291     
12292     onIncrementHours: function()
12293     {
12294         Roo.log('onIncrementHours');
12295         this.time = this.time.add(Date.HOUR, 1);
12296         this.update();
12297         
12298     },
12299     
12300     onDecrementHours: function()
12301     {
12302         Roo.log('onDecrementHours');
12303         this.time = this.time.add(Date.HOUR, -1);
12304         this.update();
12305     },
12306     
12307     onIncrementMinutes: function()
12308     {
12309         Roo.log('onIncrementMinutes');
12310         this.time = this.time.add(Date.MINUTE, 1);
12311         this.update();
12312     },
12313     
12314     onDecrementMinutes: function()
12315     {
12316         Roo.log('onDecrementMinutes');
12317         this.time = this.time.add(Date.MINUTE, -1);
12318         this.update();
12319     },
12320     
12321     onTogglePeriod: function()
12322     {
12323         Roo.log('onTogglePeriod');
12324         this.time = this.time.add(Date.HOUR, 12);
12325         this.update();
12326     }
12327     
12328    
12329 });
12330
12331 Roo.apply(Roo.bootstrap.TimeField,  {
12332     
12333     content : {
12334         tag: 'tbody',
12335         cn: [
12336             {
12337                 tag: 'tr',
12338                 cn: [
12339                 {
12340                     tag: 'td',
12341                     colspan: '7'
12342                 }
12343                 ]
12344             }
12345         ]
12346     },
12347     
12348     footer : {
12349         tag: 'tfoot',
12350         cn: [
12351             {
12352                 tag: 'tr',
12353                 cn: [
12354                 {
12355                     tag: 'th',
12356                     colspan: '7',
12357                     cls: '',
12358                     cn: [
12359                         {
12360                             tag: 'button',
12361                             cls: 'btn btn-info ok',
12362                             html: 'OK'
12363                         }
12364                     ]
12365                 }
12366
12367                 ]
12368             }
12369         ]
12370     }
12371 });
12372
12373 Roo.apply(Roo.bootstrap.TimeField,  {
12374   
12375     template : {
12376         tag: 'div',
12377         cls: 'datepicker dropdown-menu',
12378         cn: [
12379             {
12380                 tag: 'div',
12381                 cls: 'datepicker-time',
12382                 cn: [
12383                 {
12384                     tag: 'table',
12385                     cls: 'table-condensed',
12386                     cn:[
12387                     Roo.bootstrap.TimeField.content,
12388                     Roo.bootstrap.TimeField.footer
12389                     ]
12390                 }
12391                 ]
12392             }
12393         ]
12394     }
12395 });
12396
12397  
12398
12399  /*
12400  * - LGPL
12401  *
12402  * CheckBox
12403  * 
12404  */
12405
12406 /**
12407  * @class Roo.bootstrap.CheckBox
12408  * @extends Roo.bootstrap.Input
12409  * Bootstrap CheckBox class
12410  * 
12411  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12412  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12413  * @cfg {String} boxLabel The text that appears beside the checkbox
12414  * @cfg {Boolean} checked initnal the element
12415  * 
12416  * @constructor
12417  * Create a new CheckBox
12418  * @param {Object} config The config object
12419  */
12420
12421 Roo.bootstrap.CheckBox = function(config){
12422     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12423    
12424         this.addEvents({
12425             /**
12426             * @event check
12427             * Fires when the element is checked or unchecked.
12428             * @param {Roo.bootstrap.CheckBox} this This input
12429             * @param {Boolean} checked The new checked value
12430             */
12431            check : true
12432         });
12433 };
12434
12435 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12436     
12437     inputType: 'checkbox',
12438     inputValue: 1,
12439     valueOff: 0,
12440     boxLabel: false,
12441     checked: false,
12442     
12443     getAutoCreate : function()
12444     {
12445         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12446         
12447         var id = Roo.id();
12448         
12449         var cfg = {};
12450         
12451         cfg.cls = 'form-group' //input-group
12452         
12453         var input =  {
12454             tag: 'input',
12455             id : id,
12456             type : this.inputType,
12457             value : (!this.checked) ? this.valueOff : this.inputValue,
12458             cls : 'form-box',
12459             placeholder : this.placeholder || ''
12460             
12461         };
12462         
12463         if (this.disabled) {
12464             input.disabled=true;
12465         }
12466         
12467         if(this.checked){
12468             input.checked = this.checked;
12469         }
12470         
12471         if (this.name) {
12472             input.name = this.name;
12473         }
12474         
12475         if (this.size) {
12476             input.cls += ' input-' + this.size;
12477         }
12478         
12479         var settings=this;
12480         ['xs','sm','md','lg'].map(function(size){
12481             if (settings[size]) {
12482                 cfg.cls += ' col-' + size + '-' + settings[size];
12483             }
12484         });
12485         
12486         var inputblock = input;
12487         
12488         if (this.before || this.after) {
12489             
12490             inputblock = {
12491                 cls : 'input-group',
12492                 cn :  [] 
12493             };
12494             if (this.before) {
12495                 inputblock.cn.push({
12496                     tag :'span',
12497                     cls : 'input-group-addon',
12498                     html : this.before
12499                 });
12500             }
12501             inputblock.cn.push(input);
12502             if (this.after) {
12503                 inputblock.cn.push({
12504                     tag :'span',
12505                     cls : 'input-group-addon',
12506                     html : this.after
12507                 });
12508             }
12509             
12510         };
12511         
12512         if (align ==='left' && this.fieldLabel.length) {
12513                 Roo.log("left and has label");
12514                 cfg.cn = [
12515                     
12516                     {
12517                         tag: 'label',
12518                         'for' :  id,
12519                         cls : 'control-label col-md-' + this.labelWidth,
12520                         html : this.fieldLabel
12521                         
12522                     },
12523                     {
12524                         cls : "col-md-" + (12 - this.labelWidth), 
12525                         cn: [
12526                             inputblock
12527                         ]
12528                     }
12529                     
12530                 ];
12531         } else if ( this.fieldLabel.length) {
12532                 Roo.log(" label");
12533                 cfg.cn = [
12534                    
12535                     {
12536                         tag: this.boxLabel ? 'span' : 'label',
12537                         'for': id,
12538                         cls: 'control-label box-input-label',
12539                         //cls : 'input-group-addon',
12540                         html : this.fieldLabel
12541                         
12542                     },
12543                     
12544                     inputblock
12545                     
12546                 ];
12547
12548         } else {
12549             
12550                    Roo.log(" no label && no align");
12551                 cfg.cn = [
12552                     
12553                         inputblock
12554                     
12555                 ];
12556                 
12557                 
12558         };
12559         
12560         if(this.boxLabel){
12561             cfg.cn.push({
12562                 tag: 'label',
12563                 'for': id,
12564                 cls: 'box-label',
12565                 html: this.boxLabel
12566             })
12567         }
12568         
12569         return cfg;
12570         
12571     },
12572     
12573     /**
12574      * return the real input element.
12575      */
12576     inputEl: function ()
12577     {
12578         return this.el.select('input.form-box',true).first();
12579     },
12580     
12581     initEvents : function()
12582     {
12583 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12584         
12585         this.inputEl().on('click', this.onClick,  this);
12586         
12587     },
12588     
12589     onClick : function()
12590     {   
12591         this.setChecked(!this.checked);
12592     },
12593     
12594     setChecked : function(state,suppressEvent)
12595     {
12596         this.checked = state;
12597         
12598         this.inputEl().dom.checked = state;
12599         
12600         if(suppressEvent !== true){
12601             this.fireEvent('check', this, state);
12602         }
12603         
12604         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12605         
12606     },
12607     
12608     setValue : function(v,suppressEvent)
12609     {
12610         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12611     }
12612     
12613 });
12614
12615  
12616 /*
12617  * - LGPL
12618  *
12619  * Radio
12620  * 
12621  */
12622
12623 /**
12624  * @class Roo.bootstrap.Radio
12625  * @extends Roo.bootstrap.CheckBox
12626  * Bootstrap Radio class
12627
12628  * @constructor
12629  * Create a new Radio
12630  * @param {Object} config The config object
12631  */
12632
12633 Roo.bootstrap.Radio = function(config){
12634     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12635    
12636 };
12637
12638 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12639     
12640     inputType: 'radio',
12641     inputValue: '',
12642     valueOff: '',
12643     
12644     getAutoCreate : function()
12645     {
12646         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12647         
12648         var id = Roo.id();
12649         
12650         var cfg = {};
12651         
12652         cfg.cls = 'form-group' //input-group
12653         
12654         var input =  {
12655             tag: 'input',
12656             id : id,
12657             type : this.inputType,
12658             value : (!this.checked) ? this.valueOff : this.inputValue,
12659             cls : 'form-box',
12660             placeholder : this.placeholder || ''
12661             
12662         };
12663         
12664         if (this.disabled) {
12665             input.disabled=true;
12666         }
12667         
12668         if(this.checked){
12669             input.checked = this.checked;
12670         }
12671         
12672         if (this.name) {
12673             input.name = this.name;
12674         }
12675         
12676         if (this.size) {
12677             input.cls += ' input-' + this.size;
12678         }
12679         
12680         var settings=this;
12681         ['xs','sm','md','lg'].map(function(size){
12682             if (settings[size]) {
12683                 cfg.cls += ' col-' + size + '-' + settings[size];
12684             }
12685         });
12686         
12687         var inputblock = input;
12688         
12689         if (this.before || this.after) {
12690             
12691             inputblock = {
12692                 cls : 'input-group',
12693                 cn :  [] 
12694             };
12695             if (this.before) {
12696                 inputblock.cn.push({
12697                     tag :'span',
12698                     cls : 'input-group-addon',
12699                     html : this.before
12700                 });
12701             }
12702             inputblock.cn.push(input);
12703             if (this.after) {
12704                 inputblock.cn.push({
12705                     tag :'span',
12706                     cls : 'input-group-addon',
12707                     html : this.after
12708                 });
12709             }
12710             
12711         };
12712         
12713         if (align ==='left' && this.fieldLabel.length) {
12714                 Roo.log("left and has label");
12715                 cfg.cn = [
12716                     
12717                     {
12718                         tag: 'label',
12719                         'for' :  id,
12720                         cls : 'control-label col-md-' + this.labelWidth,
12721                         html : this.fieldLabel
12722                         
12723                     },
12724                     {
12725                         cls : "col-md-" + (12 - this.labelWidth), 
12726                         cn: [
12727                             inputblock
12728                         ]
12729                     }
12730                     
12731                 ];
12732         } else if ( this.fieldLabel.length) {
12733                 Roo.log(" label");
12734                  cfg.cn = [
12735                    
12736                     {
12737                         tag: 'label',
12738                         'for': id,
12739                         cls: 'control-label box-input-label',
12740                         //cls : 'input-group-addon',
12741                         html : this.fieldLabel
12742                         
12743                     },
12744                     
12745                     inputblock
12746                     
12747                 ];
12748
12749         } else {
12750             
12751                    Roo.log(" no label && no align");
12752                 cfg.cn = [
12753                     
12754                         inputblock
12755                     
12756                 ];
12757                 
12758                 
12759         };
12760         
12761         if(this.boxLabel){
12762             cfg.cn.push({
12763                 tag: 'label',
12764                 'for': id,
12765                 cls: 'box-label',
12766                 html: this.boxLabel
12767             })
12768         }
12769         
12770         return cfg;
12771         
12772     },
12773    
12774     onClick : function()
12775     {   
12776         this.setChecked(true);
12777     },
12778     
12779     setChecked : function(state,suppressEvent)
12780     {
12781         if(state){
12782             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12783                 v.dom.checked = false;
12784             });
12785         }
12786         
12787         this.checked = state;
12788         this.inputEl().dom.checked = state;
12789         
12790         if(suppressEvent !== true){
12791             this.fireEvent('check', this, state);
12792         }
12793         
12794         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12795         
12796     },
12797     
12798     getGroupValue : function()
12799     {
12800         var value = ''
12801         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12802             if(v.dom.checked == true){
12803                 value = v.dom.value;
12804             }
12805         });
12806         
12807         return value;
12808     },
12809     
12810     /**
12811      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12812      * @return {Mixed} value The field value
12813      */
12814     getValue : function(){
12815         return this.getGroupValue();
12816     }
12817     
12818 });
12819
12820  
12821 //<script type="text/javascript">
12822
12823 /*
12824  * Based  Ext JS Library 1.1.1
12825  * Copyright(c) 2006-2007, Ext JS, LLC.
12826  * LGPL
12827  *
12828  */
12829  
12830 /**
12831  * @class Roo.HtmlEditorCore
12832  * @extends Roo.Component
12833  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12834  *
12835  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12836  */
12837
12838 Roo.HtmlEditorCore = function(config){
12839     
12840     
12841     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12842     this.addEvents({
12843         /**
12844          * @event initialize
12845          * Fires when the editor is fully initialized (including the iframe)
12846          * @param {Roo.HtmlEditorCore} this
12847          */
12848         initialize: true,
12849         /**
12850          * @event activate
12851          * Fires when the editor is first receives the focus. Any insertion must wait
12852          * until after this event.
12853          * @param {Roo.HtmlEditorCore} this
12854          */
12855         activate: true,
12856          /**
12857          * @event beforesync
12858          * Fires before the textarea is updated with content from the editor iframe. Return false
12859          * to cancel the sync.
12860          * @param {Roo.HtmlEditorCore} this
12861          * @param {String} html
12862          */
12863         beforesync: true,
12864          /**
12865          * @event beforepush
12866          * Fires before the iframe editor is updated with content from the textarea. Return false
12867          * to cancel the push.
12868          * @param {Roo.HtmlEditorCore} this
12869          * @param {String} html
12870          */
12871         beforepush: true,
12872          /**
12873          * @event sync
12874          * Fires when the textarea is updated with content from the editor iframe.
12875          * @param {Roo.HtmlEditorCore} this
12876          * @param {String} html
12877          */
12878         sync: true,
12879          /**
12880          * @event push
12881          * Fires when the iframe editor is updated with content from the textarea.
12882          * @param {Roo.HtmlEditorCore} this
12883          * @param {String} html
12884          */
12885         push: true,
12886         
12887         /**
12888          * @event editorevent
12889          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12890          * @param {Roo.HtmlEditorCore} this
12891          */
12892         editorevent: true
12893     });
12894      
12895 };
12896
12897
12898 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12899
12900
12901      /**
12902      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12903      */
12904     
12905     owner : false,
12906     
12907      /**
12908      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12909      *                        Roo.resizable.
12910      */
12911     resizable : false,
12912      /**
12913      * @cfg {Number} height (in pixels)
12914      */   
12915     height: 300,
12916    /**
12917      * @cfg {Number} width (in pixels)
12918      */   
12919     width: 500,
12920     
12921     /**
12922      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12923      * 
12924      */
12925     stylesheets: false,
12926     
12927     // id of frame..
12928     frameId: false,
12929     
12930     // private properties
12931     validationEvent : false,
12932     deferHeight: true,
12933     initialized : false,
12934     activated : false,
12935     sourceEditMode : false,
12936     onFocus : Roo.emptyFn,
12937     iframePad:3,
12938     hideMode:'offsets',
12939     
12940     clearUp: true,
12941     
12942      
12943     
12944
12945     /**
12946      * Protected method that will not generally be called directly. It
12947      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12948      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12949      */
12950     getDocMarkup : function(){
12951         // body styles..
12952         var st = '';
12953         Roo.log(this.stylesheets);
12954         
12955         // inherit styels from page...?? 
12956         if (this.stylesheets === false) {
12957             
12958             Roo.get(document.head).select('style').each(function(node) {
12959                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12960             });
12961             
12962             Roo.get(document.head).select('link').each(function(node) { 
12963                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12964             });
12965             
12966         } else if (!this.stylesheets.length) {
12967                 // simple..
12968                 st = '<style type="text/css">' +
12969                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12970                    '</style>';
12971         } else {
12972             Roo.each(this.stylesheets, function(s) {
12973                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12974             });
12975             
12976         }
12977         
12978         st +=  '<style type="text/css">' +
12979             'IMG { cursor: pointer } ' +
12980         '</style>';
12981
12982         
12983         return '<html><head>' + st  +
12984             //<style type="text/css">' +
12985             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12986             //'</style>' +
12987             ' </head><body class="roo-htmleditor-body"></body></html>';
12988     },
12989
12990     // private
12991     onRender : function(ct, position)
12992     {
12993         var _t = this;
12994         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12995         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12996         
12997         
12998         this.el.dom.style.border = '0 none';
12999         this.el.dom.setAttribute('tabIndex', -1);
13000         this.el.addClass('x-hidden hide');
13001         
13002         
13003         
13004         if(Roo.isIE){ // fix IE 1px bogus margin
13005             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13006         }
13007        
13008         
13009         this.frameId = Roo.id();
13010         
13011          
13012         
13013         var iframe = this.owner.wrap.createChild({
13014             tag: 'iframe',
13015             cls: 'form-control', // bootstrap..
13016             id: this.frameId,
13017             name: this.frameId,
13018             frameBorder : 'no',
13019             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13020         }, this.el
13021         );
13022         
13023         
13024         this.iframe = iframe.dom;
13025
13026          this.assignDocWin();
13027         
13028         this.doc.designMode = 'on';
13029        
13030         this.doc.open();
13031         this.doc.write(this.getDocMarkup());
13032         this.doc.close();
13033
13034         
13035         var task = { // must defer to wait for browser to be ready
13036             run : function(){
13037                 //console.log("run task?" + this.doc.readyState);
13038                 this.assignDocWin();
13039                 if(this.doc.body || this.doc.readyState == 'complete'){
13040                     try {
13041                         this.doc.designMode="on";
13042                     } catch (e) {
13043                         return;
13044                     }
13045                     Roo.TaskMgr.stop(task);
13046                     this.initEditor.defer(10, this);
13047                 }
13048             },
13049             interval : 10,
13050             duration: 10000,
13051             scope: this
13052         };
13053         Roo.TaskMgr.start(task);
13054
13055         
13056          
13057     },
13058
13059     // private
13060     onResize : function(w, h)
13061     {
13062          Roo.log('resize: ' +w + ',' + h );
13063         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13064         if(!this.iframe){
13065             return;
13066         }
13067         if(typeof w == 'number'){
13068             
13069             this.iframe.style.width = w + 'px';
13070         }
13071         if(typeof h == 'number'){
13072             
13073             this.iframe.style.height = h + 'px';
13074             if(this.doc){
13075                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13076             }
13077         }
13078         
13079     },
13080
13081     /**
13082      * Toggles the editor between standard and source edit mode.
13083      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13084      */
13085     toggleSourceEdit : function(sourceEditMode){
13086         
13087         this.sourceEditMode = sourceEditMode === true;
13088         
13089         if(this.sourceEditMode){
13090  
13091             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13092             
13093         }else{
13094             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13095             //this.iframe.className = '';
13096             this.deferFocus();
13097         }
13098         //this.setSize(this.owner.wrap.getSize());
13099         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13100     },
13101
13102     
13103   
13104
13105     /**
13106      * Protected method that will not generally be called directly. If you need/want
13107      * custom HTML cleanup, this is the method you should override.
13108      * @param {String} html The HTML to be cleaned
13109      * return {String} The cleaned HTML
13110      */
13111     cleanHtml : function(html){
13112         html = String(html);
13113         if(html.length > 5){
13114             if(Roo.isSafari){ // strip safari nonsense
13115                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13116             }
13117         }
13118         if(html == '&nbsp;'){
13119             html = '';
13120         }
13121         return html;
13122     },
13123
13124     /**
13125      * HTML Editor -> Textarea
13126      * Protected method that will not generally be called directly. Syncs the contents
13127      * of the editor iframe with the textarea.
13128      */
13129     syncValue : function(){
13130         if(this.initialized){
13131             var bd = (this.doc.body || this.doc.documentElement);
13132             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13133             var html = bd.innerHTML;
13134             if(Roo.isSafari){
13135                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13136                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13137                 if(m && m[1]){
13138                     html = '<div style="'+m[0]+'">' + html + '</div>';
13139                 }
13140             }
13141             html = this.cleanHtml(html);
13142             // fix up the special chars.. normaly like back quotes in word...
13143             // however we do not want to do this with chinese..
13144             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13145                 var cc = b.charCodeAt();
13146                 if (
13147                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13148                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13149                     (cc >= 0xf900 && cc < 0xfb00 )
13150                 ) {
13151                         return b;
13152                 }
13153                 return "&#"+cc+";" 
13154             });
13155             if(this.owner.fireEvent('beforesync', this, html) !== false){
13156                 this.el.dom.value = html;
13157                 this.owner.fireEvent('sync', this, html);
13158             }
13159         }
13160     },
13161
13162     /**
13163      * Protected method that will not generally be called directly. Pushes the value of the textarea
13164      * into the iframe editor.
13165      */
13166     pushValue : function(){
13167         if(this.initialized){
13168             var v = this.el.dom.value.trim();
13169             
13170 //            if(v.length < 1){
13171 //                v = '&#160;';
13172 //            }
13173             
13174             if(this.owner.fireEvent('beforepush', this, v) !== false){
13175                 var d = (this.doc.body || this.doc.documentElement);
13176                 d.innerHTML = v;
13177                 this.cleanUpPaste();
13178                 this.el.dom.value = d.innerHTML;
13179                 this.owner.fireEvent('push', this, v);
13180             }
13181         }
13182     },
13183
13184     // private
13185     deferFocus : function(){
13186         this.focus.defer(10, this);
13187     },
13188
13189     // doc'ed in Field
13190     focus : function(){
13191         if(this.win && !this.sourceEditMode){
13192             this.win.focus();
13193         }else{
13194             this.el.focus();
13195         }
13196     },
13197     
13198     assignDocWin: function()
13199     {
13200         var iframe = this.iframe;
13201         
13202          if(Roo.isIE){
13203             this.doc = iframe.contentWindow.document;
13204             this.win = iframe.contentWindow;
13205         } else {
13206             if (!Roo.get(this.frameId)) {
13207                 return;
13208             }
13209             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13210             this.win = Roo.get(this.frameId).dom.contentWindow;
13211         }
13212     },
13213     
13214     // private
13215     initEditor : function(){
13216         //console.log("INIT EDITOR");
13217         this.assignDocWin();
13218         
13219         
13220         
13221         this.doc.designMode="on";
13222         this.doc.open();
13223         this.doc.write(this.getDocMarkup());
13224         this.doc.close();
13225         
13226         var dbody = (this.doc.body || this.doc.documentElement);
13227         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13228         // this copies styles from the containing element into thsi one..
13229         // not sure why we need all of this..
13230         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13231         ss['background-attachment'] = 'fixed'; // w3c
13232         dbody.bgProperties = 'fixed'; // ie
13233         Roo.DomHelper.applyStyles(dbody, ss);
13234         Roo.EventManager.on(this.doc, {
13235             //'mousedown': this.onEditorEvent,
13236             'mouseup': this.onEditorEvent,
13237             'dblclick': this.onEditorEvent,
13238             'click': this.onEditorEvent,
13239             'keyup': this.onEditorEvent,
13240             buffer:100,
13241             scope: this
13242         });
13243         if(Roo.isGecko){
13244             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13245         }
13246         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13247             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13248         }
13249         this.initialized = true;
13250
13251         this.owner.fireEvent('initialize', this);
13252         this.pushValue();
13253     },
13254
13255     // private
13256     onDestroy : function(){
13257         
13258         
13259         
13260         if(this.rendered){
13261             
13262             //for (var i =0; i < this.toolbars.length;i++) {
13263             //    // fixme - ask toolbars for heights?
13264             //    this.toolbars[i].onDestroy();
13265            // }
13266             
13267             //this.wrap.dom.innerHTML = '';
13268             //this.wrap.remove();
13269         }
13270     },
13271
13272     // private
13273     onFirstFocus : function(){
13274         
13275         this.assignDocWin();
13276         
13277         
13278         this.activated = true;
13279          
13280     
13281         if(Roo.isGecko){ // prevent silly gecko errors
13282             this.win.focus();
13283             var s = this.win.getSelection();
13284             if(!s.focusNode || s.focusNode.nodeType != 3){
13285                 var r = s.getRangeAt(0);
13286                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13287                 r.collapse(true);
13288                 this.deferFocus();
13289             }
13290             try{
13291                 this.execCmd('useCSS', true);
13292                 this.execCmd('styleWithCSS', false);
13293             }catch(e){}
13294         }
13295         this.owner.fireEvent('activate', this);
13296     },
13297
13298     // private
13299     adjustFont: function(btn){
13300         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13301         //if(Roo.isSafari){ // safari
13302         //    adjust *= 2;
13303        // }
13304         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13305         if(Roo.isSafari){ // safari
13306             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13307             v =  (v < 10) ? 10 : v;
13308             v =  (v > 48) ? 48 : v;
13309             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13310             
13311         }
13312         
13313         
13314         v = Math.max(1, v+adjust);
13315         
13316         this.execCmd('FontSize', v  );
13317     },
13318
13319     onEditorEvent : function(e){
13320         this.owner.fireEvent('editorevent', this, e);
13321       //  this.updateToolbar();
13322         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13323     },
13324
13325     insertTag : function(tg)
13326     {
13327         // could be a bit smarter... -> wrap the current selected tRoo..
13328         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13329             
13330             range = this.createRange(this.getSelection());
13331             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13332             wrappingNode.appendChild(range.extractContents());
13333             range.insertNode(wrappingNode);
13334
13335             return;
13336             
13337             
13338             
13339         }
13340         this.execCmd("formatblock",   tg);
13341         
13342     },
13343     
13344     insertText : function(txt)
13345     {
13346         
13347         
13348         var range = this.createRange();
13349         range.deleteContents();
13350                //alert(Sender.getAttribute('label'));
13351                
13352         range.insertNode(this.doc.createTextNode(txt));
13353     } ,
13354     
13355      
13356
13357     /**
13358      * Executes a Midas editor command on the editor document and performs necessary focus and
13359      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13360      * @param {String} cmd The Midas command
13361      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13362      */
13363     relayCmd : function(cmd, value){
13364         this.win.focus();
13365         this.execCmd(cmd, value);
13366         this.owner.fireEvent('editorevent', this);
13367         //this.updateToolbar();
13368         this.owner.deferFocus();
13369     },
13370
13371     /**
13372      * Executes a Midas editor command directly on the editor document.
13373      * For visual commands, you should use {@link #relayCmd} instead.
13374      * <b>This should only be called after the editor is initialized.</b>
13375      * @param {String} cmd The Midas command
13376      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13377      */
13378     execCmd : function(cmd, value){
13379         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13380         this.syncValue();
13381     },
13382  
13383  
13384    
13385     /**
13386      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13387      * to insert tRoo.
13388      * @param {String} text | dom node.. 
13389      */
13390     insertAtCursor : function(text)
13391     {
13392         
13393         
13394         
13395         if(!this.activated){
13396             return;
13397         }
13398         /*
13399         if(Roo.isIE){
13400             this.win.focus();
13401             var r = this.doc.selection.createRange();
13402             if(r){
13403                 r.collapse(true);
13404                 r.pasteHTML(text);
13405                 this.syncValue();
13406                 this.deferFocus();
13407             
13408             }
13409             return;
13410         }
13411         */
13412         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13413             this.win.focus();
13414             
13415             
13416             // from jquery ui (MIT licenced)
13417             var range, node;
13418             var win = this.win;
13419             
13420             if (win.getSelection && win.getSelection().getRangeAt) {
13421                 range = win.getSelection().getRangeAt(0);
13422                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13423                 range.insertNode(node);
13424             } else if (win.document.selection && win.document.selection.createRange) {
13425                 // no firefox support
13426                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13427                 win.document.selection.createRange().pasteHTML(txt);
13428             } else {
13429                 // no firefox support
13430                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13431                 this.execCmd('InsertHTML', txt);
13432             } 
13433             
13434             this.syncValue();
13435             
13436             this.deferFocus();
13437         }
13438     },
13439  // private
13440     mozKeyPress : function(e){
13441         if(e.ctrlKey){
13442             var c = e.getCharCode(), cmd;
13443           
13444             if(c > 0){
13445                 c = String.fromCharCode(c).toLowerCase();
13446                 switch(c){
13447                     case 'b':
13448                         cmd = 'bold';
13449                         break;
13450                     case 'i':
13451                         cmd = 'italic';
13452                         break;
13453                     
13454                     case 'u':
13455                         cmd = 'underline';
13456                         break;
13457                     
13458                     case 'v':
13459                         this.cleanUpPaste.defer(100, this);
13460                         return;
13461                         
13462                 }
13463                 if(cmd){
13464                     this.win.focus();
13465                     this.execCmd(cmd);
13466                     this.deferFocus();
13467                     e.preventDefault();
13468                 }
13469                 
13470             }
13471         }
13472     },
13473
13474     // private
13475     fixKeys : function(){ // load time branching for fastest keydown performance
13476         if(Roo.isIE){
13477             return function(e){
13478                 var k = e.getKey(), r;
13479                 if(k == e.TAB){
13480                     e.stopEvent();
13481                     r = this.doc.selection.createRange();
13482                     if(r){
13483                         r.collapse(true);
13484                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13485                         this.deferFocus();
13486                     }
13487                     return;
13488                 }
13489                 
13490                 if(k == e.ENTER){
13491                     r = this.doc.selection.createRange();
13492                     if(r){
13493                         var target = r.parentElement();
13494                         if(!target || target.tagName.toLowerCase() != 'li'){
13495                             e.stopEvent();
13496                             r.pasteHTML('<br />');
13497                             r.collapse(false);
13498                             r.select();
13499                         }
13500                     }
13501                 }
13502                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13503                     this.cleanUpPaste.defer(100, this);
13504                     return;
13505                 }
13506                 
13507                 
13508             };
13509         }else if(Roo.isOpera){
13510             return function(e){
13511                 var k = e.getKey();
13512                 if(k == e.TAB){
13513                     e.stopEvent();
13514                     this.win.focus();
13515                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13516                     this.deferFocus();
13517                 }
13518                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13519                     this.cleanUpPaste.defer(100, this);
13520                     return;
13521                 }
13522                 
13523             };
13524         }else if(Roo.isSafari){
13525             return function(e){
13526                 var k = e.getKey();
13527                 
13528                 if(k == e.TAB){
13529                     e.stopEvent();
13530                     this.execCmd('InsertText','\t');
13531                     this.deferFocus();
13532                     return;
13533                 }
13534                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13535                     this.cleanUpPaste.defer(100, this);
13536                     return;
13537                 }
13538                 
13539              };
13540         }
13541     }(),
13542     
13543     getAllAncestors: function()
13544     {
13545         var p = this.getSelectedNode();
13546         var a = [];
13547         if (!p) {
13548             a.push(p); // push blank onto stack..
13549             p = this.getParentElement();
13550         }
13551         
13552         
13553         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13554             a.push(p);
13555             p = p.parentNode;
13556         }
13557         a.push(this.doc.body);
13558         return a;
13559     },
13560     lastSel : false,
13561     lastSelNode : false,
13562     
13563     
13564     getSelection : function() 
13565     {
13566         this.assignDocWin();
13567         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13568     },
13569     
13570     getSelectedNode: function() 
13571     {
13572         // this may only work on Gecko!!!
13573         
13574         // should we cache this!!!!
13575         
13576         
13577         
13578          
13579         var range = this.createRange(this.getSelection()).cloneRange();
13580         
13581         if (Roo.isIE) {
13582             var parent = range.parentElement();
13583             while (true) {
13584                 var testRange = range.duplicate();
13585                 testRange.moveToElementText(parent);
13586                 if (testRange.inRange(range)) {
13587                     break;
13588                 }
13589                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13590                     break;
13591                 }
13592                 parent = parent.parentElement;
13593             }
13594             return parent;
13595         }
13596         
13597         // is ancestor a text element.
13598         var ac =  range.commonAncestorContainer;
13599         if (ac.nodeType == 3) {
13600             ac = ac.parentNode;
13601         }
13602         
13603         var ar = ac.childNodes;
13604          
13605         var nodes = [];
13606         var other_nodes = [];
13607         var has_other_nodes = false;
13608         for (var i=0;i<ar.length;i++) {
13609             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13610                 continue;
13611             }
13612             // fullly contained node.
13613             
13614             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13615                 nodes.push(ar[i]);
13616                 continue;
13617             }
13618             
13619             // probably selected..
13620             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13621                 other_nodes.push(ar[i]);
13622                 continue;
13623             }
13624             // outer..
13625             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13626                 continue;
13627             }
13628             
13629             
13630             has_other_nodes = true;
13631         }
13632         if (!nodes.length && other_nodes.length) {
13633             nodes= other_nodes;
13634         }
13635         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13636             return false;
13637         }
13638         
13639         return nodes[0];
13640     },
13641     createRange: function(sel)
13642     {
13643         // this has strange effects when using with 
13644         // top toolbar - not sure if it's a great idea.
13645         //this.editor.contentWindow.focus();
13646         if (typeof sel != "undefined") {
13647             try {
13648                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13649             } catch(e) {
13650                 return this.doc.createRange();
13651             }
13652         } else {
13653             return this.doc.createRange();
13654         }
13655     },
13656     getParentElement: function()
13657     {
13658         
13659         this.assignDocWin();
13660         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13661         
13662         var range = this.createRange(sel);
13663          
13664         try {
13665             var p = range.commonAncestorContainer;
13666             while (p.nodeType == 3) { // text node
13667                 p = p.parentNode;
13668             }
13669             return p;
13670         } catch (e) {
13671             return null;
13672         }
13673     
13674     },
13675     /***
13676      *
13677      * Range intersection.. the hard stuff...
13678      *  '-1' = before
13679      *  '0' = hits..
13680      *  '1' = after.
13681      *         [ -- selected range --- ]
13682      *   [fail]                        [fail]
13683      *
13684      *    basically..
13685      *      if end is before start or  hits it. fail.
13686      *      if start is after end or hits it fail.
13687      *
13688      *   if either hits (but other is outside. - then it's not 
13689      *   
13690      *    
13691      **/
13692     
13693     
13694     // @see http://www.thismuchiknow.co.uk/?p=64.
13695     rangeIntersectsNode : function(range, node)
13696     {
13697         var nodeRange = node.ownerDocument.createRange();
13698         try {
13699             nodeRange.selectNode(node);
13700         } catch (e) {
13701             nodeRange.selectNodeContents(node);
13702         }
13703     
13704         var rangeStartRange = range.cloneRange();
13705         rangeStartRange.collapse(true);
13706     
13707         var rangeEndRange = range.cloneRange();
13708         rangeEndRange.collapse(false);
13709     
13710         var nodeStartRange = nodeRange.cloneRange();
13711         nodeStartRange.collapse(true);
13712     
13713         var nodeEndRange = nodeRange.cloneRange();
13714         nodeEndRange.collapse(false);
13715     
13716         return rangeStartRange.compareBoundaryPoints(
13717                  Range.START_TO_START, nodeEndRange) == -1 &&
13718                rangeEndRange.compareBoundaryPoints(
13719                  Range.START_TO_START, nodeStartRange) == 1;
13720         
13721          
13722     },
13723     rangeCompareNode : function(range, node)
13724     {
13725         var nodeRange = node.ownerDocument.createRange();
13726         try {
13727             nodeRange.selectNode(node);
13728         } catch (e) {
13729             nodeRange.selectNodeContents(node);
13730         }
13731         
13732         
13733         range.collapse(true);
13734     
13735         nodeRange.collapse(true);
13736      
13737         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13738         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13739          
13740         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13741         
13742         var nodeIsBefore   =  ss == 1;
13743         var nodeIsAfter    = ee == -1;
13744         
13745         if (nodeIsBefore && nodeIsAfter)
13746             return 0; // outer
13747         if (!nodeIsBefore && nodeIsAfter)
13748             return 1; //right trailed.
13749         
13750         if (nodeIsBefore && !nodeIsAfter)
13751             return 2;  // left trailed.
13752         // fully contined.
13753         return 3;
13754     },
13755
13756     // private? - in a new class?
13757     cleanUpPaste :  function()
13758     {
13759         // cleans up the whole document..
13760         Roo.log('cleanuppaste');
13761         
13762         this.cleanUpChildren(this.doc.body);
13763         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13764         if (clean != this.doc.body.innerHTML) {
13765             this.doc.body.innerHTML = clean;
13766         }
13767         
13768     },
13769     
13770     cleanWordChars : function(input) {// change the chars to hex code
13771         var he = Roo.HtmlEditorCore;
13772         
13773         var output = input;
13774         Roo.each(he.swapCodes, function(sw) { 
13775             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13776             
13777             output = output.replace(swapper, sw[1]);
13778         });
13779         
13780         return output;
13781     },
13782     
13783     
13784     cleanUpChildren : function (n)
13785     {
13786         if (!n.childNodes.length) {
13787             return;
13788         }
13789         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13790            this.cleanUpChild(n.childNodes[i]);
13791         }
13792     },
13793     
13794     
13795         
13796     
13797     cleanUpChild : function (node)
13798     {
13799         var ed = this;
13800         //console.log(node);
13801         if (node.nodeName == "#text") {
13802             // clean up silly Windows -- stuff?
13803             return; 
13804         }
13805         if (node.nodeName == "#comment") {
13806             node.parentNode.removeChild(node);
13807             // clean up silly Windows -- stuff?
13808             return; 
13809         }
13810         
13811         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13812             // remove node.
13813             node.parentNode.removeChild(node);
13814             return;
13815             
13816         }
13817         
13818         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13819         
13820         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13821         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13822         
13823         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13824         //    remove_keep_children = true;
13825         //}
13826         
13827         if (remove_keep_children) {
13828             this.cleanUpChildren(node);
13829             // inserts everything just before this node...
13830             while (node.childNodes.length) {
13831                 var cn = node.childNodes[0];
13832                 node.removeChild(cn);
13833                 node.parentNode.insertBefore(cn, node);
13834             }
13835             node.parentNode.removeChild(node);
13836             return;
13837         }
13838         
13839         if (!node.attributes || !node.attributes.length) {
13840             this.cleanUpChildren(node);
13841             return;
13842         }
13843         
13844         function cleanAttr(n,v)
13845         {
13846             
13847             if (v.match(/^\./) || v.match(/^\//)) {
13848                 return;
13849             }
13850             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13851                 return;
13852             }
13853             if (v.match(/^#/)) {
13854                 return;
13855             }
13856 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13857             node.removeAttribute(n);
13858             
13859         }
13860         
13861         function cleanStyle(n,v)
13862         {
13863             if (v.match(/expression/)) { //XSS?? should we even bother..
13864                 node.removeAttribute(n);
13865                 return;
13866             }
13867             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13868             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13869             
13870             
13871             var parts = v.split(/;/);
13872             var clean = [];
13873             
13874             Roo.each(parts, function(p) {
13875                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13876                 if (!p.length) {
13877                     return true;
13878                 }
13879                 var l = p.split(':').shift().replace(/\s+/g,'');
13880                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13881                 
13882                 if ( cblack.indexOf(l) > -1) {
13883 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13884                     //node.removeAttribute(n);
13885                     return true;
13886                 }
13887                 //Roo.log()
13888                 // only allow 'c whitelisted system attributes'
13889                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13890 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13891                     //node.removeAttribute(n);
13892                     return true;
13893                 }
13894                 
13895                 
13896                  
13897                 
13898                 clean.push(p);
13899                 return true;
13900             });
13901             if (clean.length) { 
13902                 node.setAttribute(n, clean.join(';'));
13903             } else {
13904                 node.removeAttribute(n);
13905             }
13906             
13907         }
13908         
13909         
13910         for (var i = node.attributes.length-1; i > -1 ; i--) {
13911             var a = node.attributes[i];
13912             //console.log(a);
13913             
13914             if (a.name.toLowerCase().substr(0,2)=='on')  {
13915                 node.removeAttribute(a.name);
13916                 continue;
13917             }
13918             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13919                 node.removeAttribute(a.name);
13920                 continue;
13921             }
13922             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13923                 cleanAttr(a.name,a.value); // fixme..
13924                 continue;
13925             }
13926             if (a.name == 'style') {
13927                 cleanStyle(a.name,a.value);
13928                 continue;
13929             }
13930             /// clean up MS crap..
13931             // tecnically this should be a list of valid class'es..
13932             
13933             
13934             if (a.name == 'class') {
13935                 if (a.value.match(/^Mso/)) {
13936                     node.className = '';
13937                 }
13938                 
13939                 if (a.value.match(/body/)) {
13940                     node.className = '';
13941                 }
13942                 continue;
13943             }
13944             
13945             // style cleanup!?
13946             // class cleanup?
13947             
13948         }
13949         
13950         
13951         this.cleanUpChildren(node);
13952         
13953         
13954     }
13955     
13956     
13957     // hide stuff that is not compatible
13958     /**
13959      * @event blur
13960      * @hide
13961      */
13962     /**
13963      * @event change
13964      * @hide
13965      */
13966     /**
13967      * @event focus
13968      * @hide
13969      */
13970     /**
13971      * @event specialkey
13972      * @hide
13973      */
13974     /**
13975      * @cfg {String} fieldClass @hide
13976      */
13977     /**
13978      * @cfg {String} focusClass @hide
13979      */
13980     /**
13981      * @cfg {String} autoCreate @hide
13982      */
13983     /**
13984      * @cfg {String} inputType @hide
13985      */
13986     /**
13987      * @cfg {String} invalidClass @hide
13988      */
13989     /**
13990      * @cfg {String} invalidText @hide
13991      */
13992     /**
13993      * @cfg {String} msgFx @hide
13994      */
13995     /**
13996      * @cfg {String} validateOnBlur @hide
13997      */
13998 });
13999
14000 Roo.HtmlEditorCore.white = [
14001         'area', 'br', 'img', 'input', 'hr', 'wbr',
14002         
14003        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14004        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14005        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14006        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14007        'table',   'ul',         'xmp', 
14008        
14009        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14010       'thead',   'tr', 
14011      
14012       'dir', 'menu', 'ol', 'ul', 'dl',
14013        
14014       'embed',  'object'
14015 ];
14016
14017
14018 Roo.HtmlEditorCore.black = [
14019     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14020         'applet', // 
14021         'base',   'basefont', 'bgsound', 'blink',  'body', 
14022         'frame',  'frameset', 'head',    'html',   'ilayer', 
14023         'iframe', 'layer',  'link',     'meta',    'object',   
14024         'script', 'style' ,'title',  'xml' // clean later..
14025 ];
14026 Roo.HtmlEditorCore.clean = [
14027     'script', 'style', 'title', 'xml'
14028 ];
14029 Roo.HtmlEditorCore.remove = [
14030     'font'
14031 ];
14032 // attributes..
14033
14034 Roo.HtmlEditorCore.ablack = [
14035     'on'
14036 ];
14037     
14038 Roo.HtmlEditorCore.aclean = [ 
14039     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14040 ];
14041
14042 // protocols..
14043 Roo.HtmlEditorCore.pwhite= [
14044         'http',  'https',  'mailto'
14045 ];
14046
14047 // white listed style attributes.
14048 Roo.HtmlEditorCore.cwhite= [
14049       //  'text-align', /// default is to allow most things..
14050       
14051          
14052 //        'font-size'//??
14053 ];
14054
14055 // black listed style attributes.
14056 Roo.HtmlEditorCore.cblack= [
14057       //  'font-size' -- this can be set by the project 
14058 ];
14059
14060
14061 Roo.HtmlEditorCore.swapCodes   =[ 
14062     [    8211, "--" ], 
14063     [    8212, "--" ], 
14064     [    8216,  "'" ],  
14065     [    8217, "'" ],  
14066     [    8220, '"' ],  
14067     [    8221, '"' ],  
14068     [    8226, "*" ],  
14069     [    8230, "..." ]
14070 ]; 
14071
14072     /*
14073  * - LGPL
14074  *
14075  * HtmlEditor
14076  * 
14077  */
14078
14079 /**
14080  * @class Roo.bootstrap.HtmlEditor
14081  * @extends Roo.bootstrap.TextArea
14082  * Bootstrap HtmlEditor class
14083
14084  * @constructor
14085  * Create a new HtmlEditor
14086  * @param {Object} config The config object
14087  */
14088
14089 Roo.bootstrap.HtmlEditor = function(config){
14090     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14091     if (!this.toolbars) {
14092         this.toolbars = [];
14093     }
14094     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14095     this.addEvents({
14096             /**
14097              * @event initialize
14098              * Fires when the editor is fully initialized (including the iframe)
14099              * @param {HtmlEditor} this
14100              */
14101             initialize: true,
14102             /**
14103              * @event activate
14104              * Fires when the editor is first receives the focus. Any insertion must wait
14105              * until after this event.
14106              * @param {HtmlEditor} this
14107              */
14108             activate: true,
14109              /**
14110              * @event beforesync
14111              * Fires before the textarea is updated with content from the editor iframe. Return false
14112              * to cancel the sync.
14113              * @param {HtmlEditor} this
14114              * @param {String} html
14115              */
14116             beforesync: true,
14117              /**
14118              * @event beforepush
14119              * Fires before the iframe editor is updated with content from the textarea. Return false
14120              * to cancel the push.
14121              * @param {HtmlEditor} this
14122              * @param {String} html
14123              */
14124             beforepush: true,
14125              /**
14126              * @event sync
14127              * Fires when the textarea is updated with content from the editor iframe.
14128              * @param {HtmlEditor} this
14129              * @param {String} html
14130              */
14131             sync: true,
14132              /**
14133              * @event push
14134              * Fires when the iframe editor is updated with content from the textarea.
14135              * @param {HtmlEditor} this
14136              * @param {String} html
14137              */
14138             push: true,
14139              /**
14140              * @event editmodechange
14141              * Fires when the editor switches edit modes
14142              * @param {HtmlEditor} this
14143              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14144              */
14145             editmodechange: true,
14146             /**
14147              * @event editorevent
14148              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14149              * @param {HtmlEditor} this
14150              */
14151             editorevent: true,
14152             /**
14153              * @event firstfocus
14154              * Fires when on first focus - needed by toolbars..
14155              * @param {HtmlEditor} this
14156              */
14157             firstfocus: true,
14158             /**
14159              * @event autosave
14160              * Auto save the htmlEditor value as a file into Events
14161              * @param {HtmlEditor} this
14162              */
14163             autosave: true,
14164             /**
14165              * @event savedpreview
14166              * preview the saved version of htmlEditor
14167              * @param {HtmlEditor} this
14168              */
14169             savedpreview: true
14170         });
14171 };
14172
14173
14174 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14175     
14176     
14177       /**
14178      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14179      */
14180     toolbars : false,
14181    
14182      /**
14183      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14184      *                        Roo.resizable.
14185      */
14186     resizable : false,
14187      /**
14188      * @cfg {Number} height (in pixels)
14189      */   
14190     height: 300,
14191    /**
14192      * @cfg {Number} width (in pixels)
14193      */   
14194     width: false,
14195     
14196     /**
14197      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14198      * 
14199      */
14200     stylesheets: false,
14201     
14202     // id of frame..
14203     frameId: false,
14204     
14205     // private properties
14206     validationEvent : false,
14207     deferHeight: true,
14208     initialized : false,
14209     activated : false,
14210     
14211     onFocus : Roo.emptyFn,
14212     iframePad:3,
14213     hideMode:'offsets',
14214     
14215     
14216     tbContainer : false,
14217     
14218     toolbarContainer :function() {
14219         return this.wrap.select('.x-html-editor-tb',true).first();
14220     },
14221
14222     /**
14223      * Protected method that will not generally be called directly. It
14224      * is called when the editor creates its toolbar. Override this method if you need to
14225      * add custom toolbar buttons.
14226      * @param {HtmlEditor} editor
14227      */
14228     createToolbar : function(){
14229         
14230         Roo.log("create toolbars");
14231         
14232         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14233         this.toolbars[0].render(this.toolbarContainer());
14234         
14235         return;
14236         
14237 //        if (!editor.toolbars || !editor.toolbars.length) {
14238 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14239 //        }
14240 //        
14241 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14242 //            editor.toolbars[i] = Roo.factory(
14243 //                    typeof(editor.toolbars[i]) == 'string' ?
14244 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14245 //                Roo.bootstrap.HtmlEditor);
14246 //            editor.toolbars[i].init(editor);
14247 //        }
14248     },
14249
14250      
14251     // private
14252     onRender : function(ct, position)
14253     {
14254        // Roo.log("Call onRender: " + this.xtype);
14255         var _t = this;
14256         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14257       
14258         this.wrap = this.inputEl().wrap({
14259             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14260         });
14261         
14262         this.editorcore.onRender(ct, position);
14263          
14264         if (this.resizable) {
14265             this.resizeEl = new Roo.Resizable(this.wrap, {
14266                 pinned : true,
14267                 wrap: true,
14268                 dynamic : true,
14269                 minHeight : this.height,
14270                 height: this.height,
14271                 handles : this.resizable,
14272                 width: this.width,
14273                 listeners : {
14274                     resize : function(r, w, h) {
14275                         _t.onResize(w,h); // -something
14276                     }
14277                 }
14278             });
14279             
14280         }
14281         this.createToolbar(this);
14282        
14283         
14284         if(!this.width && this.resizable){
14285             this.setSize(this.wrap.getSize());
14286         }
14287         if (this.resizeEl) {
14288             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14289             // should trigger onReize..
14290         }
14291         
14292     },
14293
14294     // private
14295     onResize : function(w, h)
14296     {
14297         Roo.log('resize: ' +w + ',' + h );
14298         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14299         var ew = false;
14300         var eh = false;
14301         
14302         if(this.inputEl() ){
14303             if(typeof w == 'number'){
14304                 var aw = w - this.wrap.getFrameWidth('lr');
14305                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14306                 ew = aw;
14307             }
14308             if(typeof h == 'number'){
14309                  var tbh = -11;  // fixme it needs to tool bar size!
14310                 for (var i =0; i < this.toolbars.length;i++) {
14311                     // fixme - ask toolbars for heights?
14312                     tbh += this.toolbars[i].el.getHeight();
14313                     //if (this.toolbars[i].footer) {
14314                     //    tbh += this.toolbars[i].footer.el.getHeight();
14315                     //}
14316                 }
14317               
14318                 
14319                 
14320                 
14321                 
14322                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14323                 ah -= 5; // knock a few pixes off for look..
14324                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14325                 var eh = ah;
14326             }
14327         }
14328         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14329         this.editorcore.onResize(ew,eh);
14330         
14331     },
14332
14333     /**
14334      * Toggles the editor between standard and source edit mode.
14335      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14336      */
14337     toggleSourceEdit : function(sourceEditMode)
14338     {
14339         this.editorcore.toggleSourceEdit(sourceEditMode);
14340         
14341         if(this.editorcore.sourceEditMode){
14342             Roo.log('editor - showing textarea');
14343             
14344 //            Roo.log('in');
14345 //            Roo.log(this.syncValue());
14346             this.syncValue();
14347             this.inputEl().removeClass('hide');
14348             this.inputEl().dom.removeAttribute('tabIndex');
14349             this.inputEl().focus();
14350         }else{
14351             Roo.log('editor - hiding textarea');
14352 //            Roo.log('out')
14353 //            Roo.log(this.pushValue()); 
14354             this.pushValue();
14355             
14356             this.inputEl().addClass('hide');
14357             this.inputEl().dom.setAttribute('tabIndex', -1);
14358             //this.deferFocus();
14359         }
14360          
14361         if(this.resizable){
14362             this.setSize(this.wrap.getSize());
14363         }
14364         
14365         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14366     },
14367  
14368     // private (for BoxComponent)
14369     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14370
14371     // private (for BoxComponent)
14372     getResizeEl : function(){
14373         return this.wrap;
14374     },
14375
14376     // private (for BoxComponent)
14377     getPositionEl : function(){
14378         return this.wrap;
14379     },
14380
14381     // private
14382     initEvents : function(){
14383         this.originalValue = this.getValue();
14384     },
14385
14386 //    /**
14387 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14388 //     * @method
14389 //     */
14390 //    markInvalid : Roo.emptyFn,
14391 //    /**
14392 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14393 //     * @method
14394 //     */
14395 //    clearInvalid : Roo.emptyFn,
14396
14397     setValue : function(v){
14398         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14399         this.editorcore.pushValue();
14400     },
14401
14402      
14403     // private
14404     deferFocus : function(){
14405         this.focus.defer(10, this);
14406     },
14407
14408     // doc'ed in Field
14409     focus : function(){
14410         this.editorcore.focus();
14411         
14412     },
14413       
14414
14415     // private
14416     onDestroy : function(){
14417         
14418         
14419         
14420         if(this.rendered){
14421             
14422             for (var i =0; i < this.toolbars.length;i++) {
14423                 // fixme - ask toolbars for heights?
14424                 this.toolbars[i].onDestroy();
14425             }
14426             
14427             this.wrap.dom.innerHTML = '';
14428             this.wrap.remove();
14429         }
14430     },
14431
14432     // private
14433     onFirstFocus : function(){
14434         //Roo.log("onFirstFocus");
14435         this.editorcore.onFirstFocus();
14436          for (var i =0; i < this.toolbars.length;i++) {
14437             this.toolbars[i].onFirstFocus();
14438         }
14439         
14440     },
14441     
14442     // private
14443     syncValue : function()
14444     {   
14445         this.editorcore.syncValue();
14446     },
14447     
14448     pushValue : function()
14449     {   
14450         this.editorcore.pushValue();
14451     }
14452      
14453     
14454     // hide stuff that is not compatible
14455     /**
14456      * @event blur
14457      * @hide
14458      */
14459     /**
14460      * @event change
14461      * @hide
14462      */
14463     /**
14464      * @event focus
14465      * @hide
14466      */
14467     /**
14468      * @event specialkey
14469      * @hide
14470      */
14471     /**
14472      * @cfg {String} fieldClass @hide
14473      */
14474     /**
14475      * @cfg {String} focusClass @hide
14476      */
14477     /**
14478      * @cfg {String} autoCreate @hide
14479      */
14480     /**
14481      * @cfg {String} inputType @hide
14482      */
14483     /**
14484      * @cfg {String} invalidClass @hide
14485      */
14486     /**
14487      * @cfg {String} invalidText @hide
14488      */
14489     /**
14490      * @cfg {String} msgFx @hide
14491      */
14492     /**
14493      * @cfg {String} validateOnBlur @hide
14494      */
14495 });
14496  
14497     
14498    
14499    
14500    
14501       
14502
14503 /**
14504  * @class Roo.bootstrap.HtmlEditorToolbar1
14505  * Basic Toolbar
14506  * 
14507  * Usage:
14508  *
14509  new Roo.bootstrap.HtmlEditor({
14510     ....
14511     toolbars : [
14512         new Roo.bootstrap.HtmlEditorToolbar1({
14513             disable : { fonts: 1 , format: 1, ..., ... , ...],
14514             btns : [ .... ]
14515         })
14516     }
14517      
14518  * 
14519  * @cfg {Object} disable List of elements to disable..
14520  * @cfg {Array} btns List of additional buttons.
14521  * 
14522  * 
14523  * NEEDS Extra CSS? 
14524  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14525  */
14526  
14527 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14528 {
14529     
14530     Roo.apply(this, config);
14531     
14532     // default disabled, based on 'good practice'..
14533     this.disable = this.disable || {};
14534     Roo.applyIf(this.disable, {
14535         fontSize : true,
14536         colors : true,
14537         specialElements : true
14538     });
14539     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14540     
14541     this.editor = config.editor;
14542     this.editorcore = config.editor.editorcore;
14543     
14544     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14545     
14546     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14547     // dont call parent... till later.
14548 }
14549 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14550     
14551     
14552     bar : true,
14553     
14554     editor : false,
14555     editorcore : false,
14556     
14557     
14558     formats : [
14559         "p" ,  
14560         "h1","h2","h3","h4","h5","h6", 
14561         "pre", "code", 
14562         "abbr", "acronym", "address", "cite", "samp", "var",
14563         'div','span'
14564     ],
14565     
14566     onRender : function(ct, position)
14567     {
14568        // Roo.log("Call onRender: " + this.xtype);
14569         
14570        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14571        Roo.log(this.el);
14572        this.el.dom.style.marginBottom = '0';
14573        var _this = this;
14574        var editorcore = this.editorcore;
14575        var editor= this.editor;
14576        
14577        var children = [];
14578        var btn = function(id,cmd , toggle, handler){
14579        
14580             var  event = toggle ? 'toggle' : 'click';
14581        
14582             var a = {
14583                 size : 'sm',
14584                 xtype: 'Button',
14585                 xns: Roo.bootstrap,
14586                 glyphicon : id,
14587                 cmd : id || cmd,
14588                 enableToggle:toggle !== false,
14589                 //html : 'submit'
14590                 pressed : toggle ? false : null,
14591                 listeners : {}
14592             }
14593             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14594                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14595             }
14596             children.push(a);
14597             return a;
14598        }
14599         
14600         var style = {
14601                 xtype: 'Button',
14602                 size : 'sm',
14603                 xns: Roo.bootstrap,
14604                 glyphicon : 'font',
14605                 //html : 'submit'
14606                 menu : {
14607                     xtype: 'Menu',
14608                     xns: Roo.bootstrap,
14609                     items:  []
14610                 }
14611         };
14612         Roo.each(this.formats, function(f) {
14613             style.menu.items.push({
14614                 xtype :'MenuItem',
14615                 xns: Roo.bootstrap,
14616                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14617                 tagname : f,
14618                 listeners : {
14619                     click : function()
14620                     {
14621                         editorcore.insertTag(this.tagname);
14622                         editor.focus();
14623                     }
14624                 }
14625                 
14626             });
14627         });
14628          children.push(style);   
14629             
14630             
14631         btn('bold',false,true);
14632         btn('italic',false,true);
14633         btn('align-left', 'justifyleft',true);
14634         btn('align-center', 'justifycenter',true);
14635         btn('align-right' , 'justifyright',true);
14636         btn('link', false, false, function(btn) {
14637             //Roo.log("create link?");
14638             var url = prompt(this.createLinkText, this.defaultLinkValue);
14639             if(url && url != 'http:/'+'/'){
14640                 this.editorcore.relayCmd('createlink', url);
14641             }
14642         }),
14643         btn('list','insertunorderedlist',true);
14644         btn('pencil', false,true, function(btn){
14645                 Roo.log(this);
14646                 
14647                 this.toggleSourceEdit(btn.pressed);
14648         });
14649         /*
14650         var cog = {
14651                 xtype: 'Button',
14652                 size : 'sm',
14653                 xns: Roo.bootstrap,
14654                 glyphicon : 'cog',
14655                 //html : 'submit'
14656                 menu : {
14657                     xtype: 'Menu',
14658                     xns: Roo.bootstrap,
14659                     items:  []
14660                 }
14661         };
14662         
14663         cog.menu.items.push({
14664             xtype :'MenuItem',
14665             xns: Roo.bootstrap,
14666             html : Clean styles,
14667             tagname : f,
14668             listeners : {
14669                 click : function()
14670                 {
14671                     editorcore.insertTag(this.tagname);
14672                     editor.focus();
14673                 }
14674             }
14675             
14676         });
14677        */
14678         
14679          
14680        this.xtype = 'Navbar';
14681         
14682         for(var i=0;i< children.length;i++) {
14683             
14684             this.buttons.add(this.addxtypeChild(children[i]));
14685             
14686         }
14687         
14688         editor.on('editorevent', this.updateToolbar, this);
14689     },
14690     onBtnClick : function(id)
14691     {
14692        this.editorcore.relayCmd(id);
14693        this.editorcore.focus();
14694     },
14695     
14696     /**
14697      * Protected method that will not generally be called directly. It triggers
14698      * a toolbar update by reading the markup state of the current selection in the editor.
14699      */
14700     updateToolbar: function(){
14701
14702         if(!this.editorcore.activated){
14703             this.editor.onFirstFocus(); // is this neeed?
14704             return;
14705         }
14706
14707         var btns = this.buttons; 
14708         var doc = this.editorcore.doc;
14709         btns.get('bold').setActive(doc.queryCommandState('bold'));
14710         btns.get('italic').setActive(doc.queryCommandState('italic'));
14711         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14712         
14713         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14714         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14715         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14716         
14717         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14718         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14719          /*
14720         
14721         var ans = this.editorcore.getAllAncestors();
14722         if (this.formatCombo) {
14723             
14724             
14725             var store = this.formatCombo.store;
14726             this.formatCombo.setValue("");
14727             for (var i =0; i < ans.length;i++) {
14728                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14729                     // select it..
14730                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14731                     break;
14732                 }
14733             }
14734         }
14735         
14736         
14737         
14738         // hides menus... - so this cant be on a menu...
14739         Roo.bootstrap.MenuMgr.hideAll();
14740         */
14741         Roo.bootstrap.MenuMgr.hideAll();
14742         //this.editorsyncValue();
14743     },
14744     onFirstFocus: function() {
14745         this.buttons.each(function(item){
14746            item.enable();
14747         });
14748     },
14749     toggleSourceEdit : function(sourceEditMode){
14750         
14751           
14752         if(sourceEditMode){
14753             Roo.log("disabling buttons");
14754            this.buttons.each( function(item){
14755                 if(item.cmd != 'pencil'){
14756                     item.disable();
14757                 }
14758             });
14759           
14760         }else{
14761             Roo.log("enabling buttons");
14762             if(this.editorcore.initialized){
14763                 this.buttons.each( function(item){
14764                     item.enable();
14765                 });
14766             }
14767             
14768         }
14769         Roo.log("calling toggole on editor");
14770         // tell the editor that it's been pressed..
14771         this.editor.toggleSourceEdit(sourceEditMode);
14772        
14773     }
14774 });
14775
14776
14777
14778
14779
14780 /**
14781  * @class Roo.bootstrap.Table.AbstractSelectionModel
14782  * @extends Roo.util.Observable
14783  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14784  * implemented by descendant classes.  This class should not be directly instantiated.
14785  * @constructor
14786  */
14787 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14788     this.locked = false;
14789     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14790 };
14791
14792
14793 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14794     /** @ignore Called by the grid automatically. Do not call directly. */
14795     init : function(grid){
14796         this.grid = grid;
14797         this.initEvents();
14798     },
14799
14800     /**
14801      * Locks the selections.
14802      */
14803     lock : function(){
14804         this.locked = true;
14805     },
14806
14807     /**
14808      * Unlocks the selections.
14809      */
14810     unlock : function(){
14811         this.locked = false;
14812     },
14813
14814     /**
14815      * Returns true if the selections are locked.
14816      * @return {Boolean}
14817      */
14818     isLocked : function(){
14819         return this.locked;
14820     }
14821 });
14822 /**
14823  * @class Roo.bootstrap.Table.ColumnModel
14824  * @extends Roo.util.Observable
14825  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14826  * the columns in the table.
14827  
14828  * @constructor
14829  * @param {Object} config An Array of column config objects. See this class's
14830  * config objects for details.
14831 */
14832 Roo.bootstrap.Table.ColumnModel = function(config){
14833         /**
14834      * The config passed into the constructor
14835      */
14836     this.config = config;
14837     this.lookup = {};
14838
14839     // if no id, create one
14840     // if the column does not have a dataIndex mapping,
14841     // map it to the order it is in the config
14842     for(var i = 0, len = config.length; i < len; i++){
14843         var c = config[i];
14844         if(typeof c.dataIndex == "undefined"){
14845             c.dataIndex = i;
14846         }
14847         if(typeof c.renderer == "string"){
14848             c.renderer = Roo.util.Format[c.renderer];
14849         }
14850         if(typeof c.id == "undefined"){
14851             c.id = Roo.id();
14852         }
14853 //        if(c.editor && c.editor.xtype){
14854 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14855 //        }
14856 //        if(c.editor && c.editor.isFormField){
14857 //            c.editor = new Roo.grid.GridEditor(c.editor);
14858 //        }
14859
14860         this.lookup[c.id] = c;
14861     }
14862
14863     /**
14864      * The width of columns which have no width specified (defaults to 100)
14865      * @type Number
14866      */
14867     this.defaultWidth = 100;
14868
14869     /**
14870      * Default sortable of columns which have no sortable specified (defaults to false)
14871      * @type Boolean
14872      */
14873     this.defaultSortable = false;
14874
14875     this.addEvents({
14876         /**
14877              * @event widthchange
14878              * Fires when the width of a column changes.
14879              * @param {ColumnModel} this
14880              * @param {Number} columnIndex The column index
14881              * @param {Number} newWidth The new width
14882              */
14883             "widthchange": true,
14884         /**
14885              * @event headerchange
14886              * Fires when the text of a header changes.
14887              * @param {ColumnModel} this
14888              * @param {Number} columnIndex The column index
14889              * @param {Number} newText The new header text
14890              */
14891             "headerchange": true,
14892         /**
14893              * @event hiddenchange
14894              * Fires when a column is hidden or "unhidden".
14895              * @param {ColumnModel} this
14896              * @param {Number} columnIndex The column index
14897              * @param {Boolean} hidden true if hidden, false otherwise
14898              */
14899             "hiddenchange": true,
14900             /**
14901          * @event columnmoved
14902          * Fires when a column is moved.
14903          * @param {ColumnModel} this
14904          * @param {Number} oldIndex
14905          * @param {Number} newIndex
14906          */
14907         "columnmoved" : true,
14908         /**
14909          * @event columlockchange
14910          * Fires when a column's locked state is changed
14911          * @param {ColumnModel} this
14912          * @param {Number} colIndex
14913          * @param {Boolean} locked true if locked
14914          */
14915         "columnlockchange" : true
14916     });
14917     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14918 };
14919 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14920     /**
14921      * @cfg {String} header The header text to display in the Grid view.
14922      */
14923     /**
14924      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14925      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14926      * specified, the column's index is used as an index into the Record's data Array.
14927      */
14928     /**
14929      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14930      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14931      */
14932     /**
14933      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14934      * Defaults to the value of the {@link #defaultSortable} property.
14935      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14936      */
14937     /**
14938      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14939      */
14940     /**
14941      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14942      */
14943     /**
14944      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14945      */
14946     /**
14947      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14948      */
14949     /**
14950      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14951      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14952      * default renderer uses the raw data value.
14953      */
14954     /**
14955      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14956      */
14957
14958     /**
14959      * Returns the id of the column at the specified index.
14960      * @param {Number} index The column index
14961      * @return {String} the id
14962      */
14963     getColumnId : function(index){
14964         return this.config[index].id;
14965     },
14966
14967     /**
14968      * Returns the column for a specified id.
14969      * @param {String} id The column id
14970      * @return {Object} the column
14971      */
14972     getColumnById : function(id){
14973         return this.lookup[id];
14974     },
14975
14976     
14977     /**
14978      * Returns the column for a specified dataIndex.
14979      * @param {String} dataIndex The column dataIndex
14980      * @return {Object|Boolean} the column or false if not found
14981      */
14982     getColumnByDataIndex: function(dataIndex){
14983         var index = this.findColumnIndex(dataIndex);
14984         return index > -1 ? this.config[index] : false;
14985     },
14986     
14987     /**
14988      * Returns the index for a specified column id.
14989      * @param {String} id The column id
14990      * @return {Number} the index, or -1 if not found
14991      */
14992     getIndexById : function(id){
14993         for(var i = 0, len = this.config.length; i < len; i++){
14994             if(this.config[i].id == id){
14995                 return i;
14996             }
14997         }
14998         return -1;
14999     },
15000     
15001     /**
15002      * Returns the index for a specified column dataIndex.
15003      * @param {String} dataIndex The column dataIndex
15004      * @return {Number} the index, or -1 if not found
15005      */
15006     
15007     findColumnIndex : function(dataIndex){
15008         for(var i = 0, len = this.config.length; i < len; i++){
15009             if(this.config[i].dataIndex == dataIndex){
15010                 return i;
15011             }
15012         }
15013         return -1;
15014     },
15015     
15016     
15017     moveColumn : function(oldIndex, newIndex){
15018         var c = this.config[oldIndex];
15019         this.config.splice(oldIndex, 1);
15020         this.config.splice(newIndex, 0, c);
15021         this.dataMap = null;
15022         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15023     },
15024
15025     isLocked : function(colIndex){
15026         return this.config[colIndex].locked === true;
15027     },
15028
15029     setLocked : function(colIndex, value, suppressEvent){
15030         if(this.isLocked(colIndex) == value){
15031             return;
15032         }
15033         this.config[colIndex].locked = value;
15034         if(!suppressEvent){
15035             this.fireEvent("columnlockchange", this, colIndex, value);
15036         }
15037     },
15038
15039     getTotalLockedWidth : function(){
15040         var totalWidth = 0;
15041         for(var i = 0; i < this.config.length; i++){
15042             if(this.isLocked(i) && !this.isHidden(i)){
15043                 this.totalWidth += this.getColumnWidth(i);
15044             }
15045         }
15046         return totalWidth;
15047     },
15048
15049     getLockedCount : function(){
15050         for(var i = 0, len = this.config.length; i < len; i++){
15051             if(!this.isLocked(i)){
15052                 return i;
15053             }
15054         }
15055     },
15056
15057     /**
15058      * Returns the number of columns.
15059      * @return {Number}
15060      */
15061     getColumnCount : function(visibleOnly){
15062         if(visibleOnly === true){
15063             var c = 0;
15064             for(var i = 0, len = this.config.length; i < len; i++){
15065                 if(!this.isHidden(i)){
15066                     c++;
15067                 }
15068             }
15069             return c;
15070         }
15071         return this.config.length;
15072     },
15073
15074     /**
15075      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15076      * @param {Function} fn
15077      * @param {Object} scope (optional)
15078      * @return {Array} result
15079      */
15080     getColumnsBy : function(fn, scope){
15081         var r = [];
15082         for(var i = 0, len = this.config.length; i < len; i++){
15083             var c = this.config[i];
15084             if(fn.call(scope||this, c, i) === true){
15085                 r[r.length] = c;
15086             }
15087         }
15088         return r;
15089     },
15090
15091     /**
15092      * Returns true if the specified column is sortable.
15093      * @param {Number} col The column index
15094      * @return {Boolean}
15095      */
15096     isSortable : function(col){
15097         if(typeof this.config[col].sortable == "undefined"){
15098             return this.defaultSortable;
15099         }
15100         return this.config[col].sortable;
15101     },
15102
15103     /**
15104      * Returns the rendering (formatting) function defined for the column.
15105      * @param {Number} col The column index.
15106      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15107      */
15108     getRenderer : function(col){
15109         if(!this.config[col].renderer){
15110             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15111         }
15112         return this.config[col].renderer;
15113     },
15114
15115     /**
15116      * Sets the rendering (formatting) function for a column.
15117      * @param {Number} col The column index
15118      * @param {Function} fn The function to use to process the cell's raw data
15119      * to return HTML markup for the grid view. The render function is called with
15120      * the following parameters:<ul>
15121      * <li>Data value.</li>
15122      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15123      * <li>css A CSS style string to apply to the table cell.</li>
15124      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15125      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15126      * <li>Row index</li>
15127      * <li>Column index</li>
15128      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15129      */
15130     setRenderer : function(col, fn){
15131         this.config[col].renderer = fn;
15132     },
15133
15134     /**
15135      * Returns the width for the specified column.
15136      * @param {Number} col The column index
15137      * @return {Number}
15138      */
15139     getColumnWidth : function(col){
15140         return this.config[col].width * 1 || this.defaultWidth;
15141     },
15142
15143     /**
15144      * Sets the width for a column.
15145      * @param {Number} col The column index
15146      * @param {Number} width The new width
15147      */
15148     setColumnWidth : function(col, width, suppressEvent){
15149         this.config[col].width = width;
15150         this.totalWidth = null;
15151         if(!suppressEvent){
15152              this.fireEvent("widthchange", this, col, width);
15153         }
15154     },
15155
15156     /**
15157      * Returns the total width of all columns.
15158      * @param {Boolean} includeHidden True to include hidden column widths
15159      * @return {Number}
15160      */
15161     getTotalWidth : function(includeHidden){
15162         if(!this.totalWidth){
15163             this.totalWidth = 0;
15164             for(var i = 0, len = this.config.length; i < len; i++){
15165                 if(includeHidden || !this.isHidden(i)){
15166                     this.totalWidth += this.getColumnWidth(i);
15167                 }
15168             }
15169         }
15170         return this.totalWidth;
15171     },
15172
15173     /**
15174      * Returns the header for the specified column.
15175      * @param {Number} col The column index
15176      * @return {String}
15177      */
15178     getColumnHeader : function(col){
15179         return this.config[col].header;
15180     },
15181
15182     /**
15183      * Sets the header for a column.
15184      * @param {Number} col The column index
15185      * @param {String} header The new header
15186      */
15187     setColumnHeader : function(col, header){
15188         this.config[col].header = header;
15189         this.fireEvent("headerchange", this, col, header);
15190     },
15191
15192     /**
15193      * Returns the tooltip for the specified column.
15194      * @param {Number} col The column index
15195      * @return {String}
15196      */
15197     getColumnTooltip : function(col){
15198             return this.config[col].tooltip;
15199     },
15200     /**
15201      * Sets the tooltip for a column.
15202      * @param {Number} col The column index
15203      * @param {String} tooltip The new tooltip
15204      */
15205     setColumnTooltip : function(col, tooltip){
15206             this.config[col].tooltip = tooltip;
15207     },
15208
15209     /**
15210      * Returns the dataIndex for the specified column.
15211      * @param {Number} col The column index
15212      * @return {Number}
15213      */
15214     getDataIndex : function(col){
15215         return this.config[col].dataIndex;
15216     },
15217
15218     /**
15219      * Sets the dataIndex for a column.
15220      * @param {Number} col The column index
15221      * @param {Number} dataIndex The new dataIndex
15222      */
15223     setDataIndex : function(col, dataIndex){
15224         this.config[col].dataIndex = dataIndex;
15225     },
15226
15227     
15228     
15229     /**
15230      * Returns true if the cell is editable.
15231      * @param {Number} colIndex The column index
15232      * @param {Number} rowIndex The row index
15233      * @return {Boolean}
15234      */
15235     isCellEditable : function(colIndex, rowIndex){
15236         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15237     },
15238
15239     /**
15240      * Returns the editor defined for the cell/column.
15241      * return false or null to disable editing.
15242      * @param {Number} colIndex The column index
15243      * @param {Number} rowIndex The row index
15244      * @return {Object}
15245      */
15246     getCellEditor : function(colIndex, rowIndex){
15247         return this.config[colIndex].editor;
15248     },
15249
15250     /**
15251      * Sets if a column is editable.
15252      * @param {Number} col The column index
15253      * @param {Boolean} editable True if the column is editable
15254      */
15255     setEditable : function(col, editable){
15256         this.config[col].editable = editable;
15257     },
15258
15259
15260     /**
15261      * Returns true if the column is hidden.
15262      * @param {Number} colIndex The column index
15263      * @return {Boolean}
15264      */
15265     isHidden : function(colIndex){
15266         return this.config[colIndex].hidden;
15267     },
15268
15269
15270     /**
15271      * Returns true if the column width cannot be changed
15272      */
15273     isFixed : function(colIndex){
15274         return this.config[colIndex].fixed;
15275     },
15276
15277     /**
15278      * Returns true if the column can be resized
15279      * @return {Boolean}
15280      */
15281     isResizable : function(colIndex){
15282         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15283     },
15284     /**
15285      * Sets if a column is hidden.
15286      * @param {Number} colIndex The column index
15287      * @param {Boolean} hidden True if the column is hidden
15288      */
15289     setHidden : function(colIndex, hidden){
15290         this.config[colIndex].hidden = hidden;
15291         this.totalWidth = null;
15292         this.fireEvent("hiddenchange", this, colIndex, hidden);
15293     },
15294
15295     /**
15296      * Sets the editor for a column.
15297      * @param {Number} col The column index
15298      * @param {Object} editor The editor object
15299      */
15300     setEditor : function(col, editor){
15301         this.config[col].editor = editor;
15302     }
15303 });
15304
15305 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15306         if(typeof value == "string" && value.length < 1){
15307             return "&#160;";
15308         }
15309         return value;
15310 };
15311
15312 // Alias for backwards compatibility
15313 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15314
15315 /**
15316  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15317  * @class Roo.bootstrap.Table.RowSelectionModel
15318  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15319  * It supports multiple selections and keyboard selection/navigation. 
15320  * @constructor
15321  * @param {Object} config
15322  */
15323
15324 Roo.bootstrap.Table.RowSelectionModel = function(config){
15325     Roo.apply(this, config);
15326     this.selections = new Roo.util.MixedCollection(false, function(o){
15327         return o.id;
15328     });
15329
15330     this.last = false;
15331     this.lastActive = false;
15332
15333     this.addEvents({
15334         /**
15335              * @event selectionchange
15336              * Fires when the selection changes
15337              * @param {SelectionModel} this
15338              */
15339             "selectionchange" : true,
15340         /**
15341              * @event afterselectionchange
15342              * Fires after the selection changes (eg. by key press or clicking)
15343              * @param {SelectionModel} this
15344              */
15345             "afterselectionchange" : true,
15346         /**
15347              * @event beforerowselect
15348              * Fires when a row is selected being selected, return false to cancel.
15349              * @param {SelectionModel} this
15350              * @param {Number} rowIndex The selected index
15351              * @param {Boolean} keepExisting False if other selections will be cleared
15352              */
15353             "beforerowselect" : true,
15354         /**
15355              * @event rowselect
15356              * Fires when a row is selected.
15357              * @param {SelectionModel} this
15358              * @param {Number} rowIndex The selected index
15359              * @param {Roo.data.Record} r The record
15360              */
15361             "rowselect" : true,
15362         /**
15363              * @event rowdeselect
15364              * Fires when a row is deselected.
15365              * @param {SelectionModel} this
15366              * @param {Number} rowIndex The selected index
15367              */
15368         "rowdeselect" : true
15369     });
15370     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15371     this.locked = false;
15372 };
15373
15374 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15375     /**
15376      * @cfg {Boolean} singleSelect
15377      * True to allow selection of only one row at a time (defaults to false)
15378      */
15379     singleSelect : false,
15380
15381     // private
15382     initEvents : function(){
15383
15384         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15385             this.grid.on("mousedown", this.handleMouseDown, this);
15386         }else{ // allow click to work like normal
15387             this.grid.on("rowclick", this.handleDragableRowClick, this);
15388         }
15389
15390         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15391             "up" : function(e){
15392                 if(!e.shiftKey){
15393                     this.selectPrevious(e.shiftKey);
15394                 }else if(this.last !== false && this.lastActive !== false){
15395                     var last = this.last;
15396                     this.selectRange(this.last,  this.lastActive-1);
15397                     this.grid.getView().focusRow(this.lastActive);
15398                     if(last !== false){
15399                         this.last = last;
15400                     }
15401                 }else{
15402                     this.selectFirstRow();
15403                 }
15404                 this.fireEvent("afterselectionchange", this);
15405             },
15406             "down" : function(e){
15407                 if(!e.shiftKey){
15408                     this.selectNext(e.shiftKey);
15409                 }else if(this.last !== false && this.lastActive !== false){
15410                     var last = this.last;
15411                     this.selectRange(this.last,  this.lastActive+1);
15412                     this.grid.getView().focusRow(this.lastActive);
15413                     if(last !== false){
15414                         this.last = last;
15415                     }
15416                 }else{
15417                     this.selectFirstRow();
15418                 }
15419                 this.fireEvent("afterselectionchange", this);
15420             },
15421             scope: this
15422         });
15423
15424         var view = this.grid.view;
15425         view.on("refresh", this.onRefresh, this);
15426         view.on("rowupdated", this.onRowUpdated, this);
15427         view.on("rowremoved", this.onRemove, this);
15428     },
15429
15430     // private
15431     onRefresh : function(){
15432         var ds = this.grid.dataSource, i, v = this.grid.view;
15433         var s = this.selections;
15434         s.each(function(r){
15435             if((i = ds.indexOfId(r.id)) != -1){
15436                 v.onRowSelect(i);
15437             }else{
15438                 s.remove(r);
15439             }
15440         });
15441     },
15442
15443     // private
15444     onRemove : function(v, index, r){
15445         this.selections.remove(r);
15446     },
15447
15448     // private
15449     onRowUpdated : function(v, index, r){
15450         if(this.isSelected(r)){
15451             v.onRowSelect(index);
15452         }
15453     },
15454
15455     /**
15456      * Select records.
15457      * @param {Array} records The records to select
15458      * @param {Boolean} keepExisting (optional) True to keep existing selections
15459      */
15460     selectRecords : function(records, keepExisting){
15461         if(!keepExisting){
15462             this.clearSelections();
15463         }
15464         var ds = this.grid.dataSource;
15465         for(var i = 0, len = records.length; i < len; i++){
15466             this.selectRow(ds.indexOf(records[i]), true);
15467         }
15468     },
15469
15470     /**
15471      * Gets the number of selected rows.
15472      * @return {Number}
15473      */
15474     getCount : function(){
15475         return this.selections.length;
15476     },
15477
15478     /**
15479      * Selects the first row in the grid.
15480      */
15481     selectFirstRow : function(){
15482         this.selectRow(0);
15483     },
15484
15485     /**
15486      * Select the last row.
15487      * @param {Boolean} keepExisting (optional) True to keep existing selections
15488      */
15489     selectLastRow : function(keepExisting){
15490         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15491     },
15492
15493     /**
15494      * Selects the row immediately following the last selected row.
15495      * @param {Boolean} keepExisting (optional) True to keep existing selections
15496      */
15497     selectNext : function(keepExisting){
15498         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15499             this.selectRow(this.last+1, keepExisting);
15500             this.grid.getView().focusRow(this.last);
15501         }
15502     },
15503
15504     /**
15505      * Selects the row that precedes the last selected row.
15506      * @param {Boolean} keepExisting (optional) True to keep existing selections
15507      */
15508     selectPrevious : function(keepExisting){
15509         if(this.last){
15510             this.selectRow(this.last-1, keepExisting);
15511             this.grid.getView().focusRow(this.last);
15512         }
15513     },
15514
15515     /**
15516      * Returns the selected records
15517      * @return {Array} Array of selected records
15518      */
15519     getSelections : function(){
15520         return [].concat(this.selections.items);
15521     },
15522
15523     /**
15524      * Returns the first selected record.
15525      * @return {Record}
15526      */
15527     getSelected : function(){
15528         return this.selections.itemAt(0);
15529     },
15530
15531
15532     /**
15533      * Clears all selections.
15534      */
15535     clearSelections : function(fast){
15536         if(this.locked) return;
15537         if(fast !== true){
15538             var ds = this.grid.dataSource;
15539             var s = this.selections;
15540             s.each(function(r){
15541                 this.deselectRow(ds.indexOfId(r.id));
15542             }, this);
15543             s.clear();
15544         }else{
15545             this.selections.clear();
15546         }
15547         this.last = false;
15548     },
15549
15550
15551     /**
15552      * Selects all rows.
15553      */
15554     selectAll : function(){
15555         if(this.locked) return;
15556         this.selections.clear();
15557         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15558             this.selectRow(i, true);
15559         }
15560     },
15561
15562     /**
15563      * Returns True if there is a selection.
15564      * @return {Boolean}
15565      */
15566     hasSelection : function(){
15567         return this.selections.length > 0;
15568     },
15569
15570     /**
15571      * Returns True if the specified row is selected.
15572      * @param {Number/Record} record The record or index of the record to check
15573      * @return {Boolean}
15574      */
15575     isSelected : function(index){
15576         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15577         return (r && this.selections.key(r.id) ? true : false);
15578     },
15579
15580     /**
15581      * Returns True if the specified record id is selected.
15582      * @param {String} id The id of record to check
15583      * @return {Boolean}
15584      */
15585     isIdSelected : function(id){
15586         return (this.selections.key(id) ? true : false);
15587     },
15588
15589     // private
15590     handleMouseDown : function(e, t){
15591         var view = this.grid.getView(), rowIndex;
15592         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15593             return;
15594         };
15595         if(e.shiftKey && this.last !== false){
15596             var last = this.last;
15597             this.selectRange(last, rowIndex, e.ctrlKey);
15598             this.last = last; // reset the last
15599             view.focusRow(rowIndex);
15600         }else{
15601             var isSelected = this.isSelected(rowIndex);
15602             if(e.button !== 0 && isSelected){
15603                 view.focusRow(rowIndex);
15604             }else if(e.ctrlKey && isSelected){
15605                 this.deselectRow(rowIndex);
15606             }else if(!isSelected){
15607                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15608                 view.focusRow(rowIndex);
15609             }
15610         }
15611         this.fireEvent("afterselectionchange", this);
15612     },
15613     // private
15614     handleDragableRowClick :  function(grid, rowIndex, e) 
15615     {
15616         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15617             this.selectRow(rowIndex, false);
15618             grid.view.focusRow(rowIndex);
15619              this.fireEvent("afterselectionchange", this);
15620         }
15621     },
15622     
15623     /**
15624      * Selects multiple rows.
15625      * @param {Array} rows Array of the indexes of the row to select
15626      * @param {Boolean} keepExisting (optional) True to keep existing selections
15627      */
15628     selectRows : function(rows, keepExisting){
15629         if(!keepExisting){
15630             this.clearSelections();
15631         }
15632         for(var i = 0, len = rows.length; i < len; i++){
15633             this.selectRow(rows[i], true);
15634         }
15635     },
15636
15637     /**
15638      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15639      * @param {Number} startRow The index of the first row in the range
15640      * @param {Number} endRow The index of the last row in the range
15641      * @param {Boolean} keepExisting (optional) True to retain existing selections
15642      */
15643     selectRange : function(startRow, endRow, keepExisting){
15644         if(this.locked) return;
15645         if(!keepExisting){
15646             this.clearSelections();
15647         }
15648         if(startRow <= endRow){
15649             for(var i = startRow; i <= endRow; i++){
15650                 this.selectRow(i, true);
15651             }
15652         }else{
15653             for(var i = startRow; i >= endRow; i--){
15654                 this.selectRow(i, true);
15655             }
15656         }
15657     },
15658
15659     /**
15660      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15661      * @param {Number} startRow The index of the first row in the range
15662      * @param {Number} endRow The index of the last row in the range
15663      */
15664     deselectRange : function(startRow, endRow, preventViewNotify){
15665         if(this.locked) return;
15666         for(var i = startRow; i <= endRow; i++){
15667             this.deselectRow(i, preventViewNotify);
15668         }
15669     },
15670
15671     /**
15672      * Selects a row.
15673      * @param {Number} row The index of the row to select
15674      * @param {Boolean} keepExisting (optional) True to keep existing selections
15675      */
15676     selectRow : function(index, keepExisting, preventViewNotify){
15677         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15678         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15679             if(!keepExisting || this.singleSelect){
15680                 this.clearSelections();
15681             }
15682             var r = this.grid.dataSource.getAt(index);
15683             this.selections.add(r);
15684             this.last = this.lastActive = index;
15685             if(!preventViewNotify){
15686                 this.grid.getView().onRowSelect(index);
15687             }
15688             this.fireEvent("rowselect", this, index, r);
15689             this.fireEvent("selectionchange", this);
15690         }
15691     },
15692
15693     /**
15694      * Deselects a row.
15695      * @param {Number} row The index of the row to deselect
15696      */
15697     deselectRow : function(index, preventViewNotify){
15698         if(this.locked) return;
15699         if(this.last == index){
15700             this.last = false;
15701         }
15702         if(this.lastActive == index){
15703             this.lastActive = false;
15704         }
15705         var r = this.grid.dataSource.getAt(index);
15706         this.selections.remove(r);
15707         if(!preventViewNotify){
15708             this.grid.getView().onRowDeselect(index);
15709         }
15710         this.fireEvent("rowdeselect", this, index);
15711         this.fireEvent("selectionchange", this);
15712     },
15713
15714     // private
15715     restoreLast : function(){
15716         if(this._last){
15717             this.last = this._last;
15718         }
15719     },
15720
15721     // private
15722     acceptsNav : function(row, col, cm){
15723         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15724     },
15725
15726     // private
15727     onEditorKey : function(field, e){
15728         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15729         if(k == e.TAB){
15730             e.stopEvent();
15731             ed.completeEdit();
15732             if(e.shiftKey){
15733                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15734             }else{
15735                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15736             }
15737         }else if(k == e.ENTER && !e.ctrlKey){
15738             e.stopEvent();
15739             ed.completeEdit();
15740             if(e.shiftKey){
15741                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15742             }else{
15743                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15744             }
15745         }else if(k == e.ESC){
15746             ed.cancelEdit();
15747         }
15748         if(newCell){
15749             g.startEditing(newCell[0], newCell[1]);
15750         }
15751     }
15752 });/*
15753  * - LGPL
15754  *
15755  * element
15756  * 
15757  */
15758
15759 /**
15760  * @class Roo.bootstrap.MessageBar
15761  * @extends Roo.bootstrap.Component
15762  * Bootstrap MessageBar class
15763  * @cfg {String} html contents of the MessageBar
15764  * @cfg {String} weight (info | success | warning | danger) default info
15765  * @cfg {String} beforeClass insert the bar before the given class
15766  * @cfg {Boolean} closable (true | false) default false
15767  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15768  * 
15769  * @constructor
15770  * Create a new Element
15771  * @param {Object} config The config object
15772  */
15773
15774 Roo.bootstrap.MessageBar = function(config){
15775     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15776 };
15777
15778 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15779     
15780     html: '',
15781     weight: 'info',
15782     closable: false,
15783     fixed: false,
15784     beforeClass: 'bootstrap-sticky-wrap',
15785     
15786     getAutoCreate : function(){
15787         
15788         var cfg = {
15789             tag: 'div',
15790             cls: 'alert alert-dismissable alert-' + this.weight,
15791             cn: [
15792                 {
15793                     tag: 'span',
15794                     cls: 'message',
15795                     html: this.html || ''
15796                 }
15797             ]
15798         }
15799         
15800         if(this.fixed){
15801             cfg.cls += ' alert-messages-fixed';
15802         }
15803         
15804         if(this.closable){
15805             cfg.cn.push({
15806                 tag: 'button',
15807                 cls: 'close',
15808                 html: 'x'
15809             });
15810         }
15811         
15812         return cfg;
15813     },
15814     
15815     onRender : function(ct, position)
15816     {
15817         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15818         
15819         if(!this.el){
15820             var cfg = Roo.apply({},  this.getAutoCreate());
15821             cfg.id = Roo.id();
15822             
15823             if (this.cls) {
15824                 cfg.cls += ' ' + this.cls;
15825             }
15826             if (this.style) {
15827                 cfg.style = this.style;
15828             }
15829             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15830             
15831             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15832         }
15833         
15834         this.el.select('>button.close').on('click', this.hide, this);
15835         
15836     },
15837     
15838     show : function()
15839     {
15840         if (!this.rendered) {
15841             this.render();
15842         }
15843         
15844         this.el.show();
15845         
15846         this.fireEvent('show', this);
15847         
15848     },
15849     
15850     hide : function()
15851     {
15852         if (!this.rendered) {
15853             this.render();
15854         }
15855         
15856         this.el.hide();
15857         
15858         this.fireEvent('hide', this);
15859     },
15860     
15861     update : function()
15862     {
15863 //        var e = this.el.dom.firstChild;
15864 //        
15865 //        if(this.closable){
15866 //            e = e.nextSibling;
15867 //        }
15868 //        
15869 //        e.data = this.html || '';
15870
15871         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15872     }
15873    
15874 });
15875
15876  
15877
15878