0c97bc7c2087a53c152648f74aaa3650710ead03
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr]());
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr]());
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr]());
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         if (typeof (tree.menu) != 'undefined') {
249             tree.menu.parentType = cn.xtype;
250             tree.menu.triggerEl = cn.el;
251             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
252             
253         }
254         
255         if (!tree.items || !tree.items.length) {
256             cn.items = nitems;
257             return cn;
258         }
259         var items = tree.items;
260         delete tree.items;
261         
262         //Roo.log(items.length);
263             // add the items..
264         for(var i =0;i < items.length;i++) {
265             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
266         }
267         
268         cn.items = nitems;
269         
270         return cn;
271     }
272     
273     
274     
275     
276 });
277
278  /*
279  * - LGPL
280  *
281  * Body
282  * 
283  */
284
285 /**
286  * @class Roo.bootstrap.Body
287  * @extends Roo.bootstrap.Component
288  * Bootstrap Body class
289  * 
290  * @constructor
291  * Create a new body
292  * @param {Object} config The config object
293  */
294
295 Roo.bootstrap.Body = function(config){
296     Roo.bootstrap.Body.superclass.constructor.call(this, config);
297     this.el = Roo.get(document.body);
298 };
299
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
301       
302         autoCreate : {
303         cls: 'container'
304     },
305     onRender : function(ct, position){
306         
307         
308         //this.el.addClass([this.fieldClass, this.cls]);
309         
310     }
311     
312     
313  
314    
315 });
316
317  /*
318  * - LGPL
319  *
320  * button group
321  * 
322  */
323
324
325 /**
326  * @class Roo.bootstrap.ButtonGroup
327  * @extends Roo.bootstrap.Component
328  * Bootstrap ButtonGroup class
329  * @cfg {String} size lg | sm | xs (default empty normal)
330  * @cfg {String} align vertical | justified  (default none)
331  * @cfg {String} direction up | down (default down)
332  * @cfg {Boolean} toolbar false | true
333  * @cfg {Boolean} btn true | false
334  * 
335  * 
336  * @constructor
337  * Create a new Input
338  * @param {Object} config The config object
339  */
340
341 Roo.bootstrap.ButtonGroup = function(config){
342     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
343 };
344
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
346     
347     size: '',
348     align: '',
349     direction: '',
350     toolbar: false,
351     btn: true,
352
353     getAutoCreate : function(){
354         var cfg = {
355             cls: 'btn-group',
356             html : null
357         }
358         
359         cfg.html = this.html || cfg.html;
360         
361         if (this.toolbar) {
362             cfg = {
363                 cls: 'btn-toolbar',
364                 html: null
365             }
366             
367             return cfg;
368         }
369         
370         if (['vertical','justified'].indexOf(this.align)!==-1) {
371             cfg.cls = 'btn-group-' + this.align;
372             
373             if (this.align == 'justified') {
374                 console.log(this.items);
375             }
376         }
377         
378         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379             cfg.cls += ' btn-group-' + this.size;
380         }
381         
382         if (this.direction == 'up') {
383             cfg.cls += ' dropup' ;
384         }
385         
386         return cfg;
387     }
388    
389 });
390
391  /*
392  * - LGPL
393  *
394  * button
395  * 
396  */
397
398 /**
399  * @class Roo.bootstrap.Button
400  * @extends Roo.bootstrap.Component
401  * Bootstrap Button class
402  * @cfg {String} html The button content
403  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404  * @cfg {String} size empty | lg | sm | xs
405  * @cfg {String} tag empty | a | input | submit
406  * @cfg {String} href empty or href
407  * @cfg {Boolean} disabled false | true
408  * @cfg {Boolean} isClose false | true
409  * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410  * @cfg {String} badge text for badge
411  * @cfg {String} theme default (or empty) | glow
412  * @cfg {Boolean} inverse false | true
413  * @cfg {Boolean} toggle false | true
414  * @cfg {String} ontext text for on toggle state
415  * @cfg {String} offtext text for off toggle state
416  * @cfg {Boolean} defaulton true | false
417  * @cfg {Boolean} preventDefault (true | false) default true
418  * @cfg {Boolean} removeClass true | false remove the standard class..
419  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
420  * 
421  * @constructor
422  * Create a new button
423  * @param {Object} config The config object
424  */
425
426
427 Roo.bootstrap.Button = function(config){
428     Roo.bootstrap.Button.superclass.constructor.call(this, config);
429     this.addEvents({
430         // raw events
431         /**
432          * @event click
433          * When a butotn is pressed
434          * @param {Roo.EventObject} e
435          */
436         "click" : true,
437          /**
438          * @event toggle
439          * After the button has been toggles
440          * @param {Roo.EventObject} e
441          * @param {boolean} pressed (also available as button.pressed)
442          */
443         "toggle" : true
444     });
445 };
446
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
448     html: false,
449     active: false,
450     weight: '',
451     size: '',
452     tag: 'button',
453     href: '',
454     disabled: false,
455     isClose: false,
456     glyphicon: '',
457     badge: '',
458     theme: 'default',
459     inverse: false,
460     
461     toggle: false,
462     ontext: 'ON',
463     offtext: 'OFF',
464     defaulton: true,
465     preventDefault: true,
466     removeClass: false,
467     name: false,
468     target: false,
469     
470     
471     pressed : null,
472     
473     
474     getAutoCreate : function(){
475         
476         var cfg = {
477             tag : 'button',
478             cls : 'roo-button',
479             html: ''
480         };
481         
482         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
484             this.tag = 'button';
485         } else {
486             cfg.tag = this.tag;
487         }
488         cfg.html = this.html || cfg.html;
489         
490         if (this.toggle == true) {
491             cfg={
492                 tag: 'div',
493                 cls: 'slider-frame roo-button',
494                 cn: [
495                     {
496                         tag: 'span',
497                         'data-on-text':'ON',
498                         'data-off-text':'OFF',
499                         cls: 'slider-button',
500                         html: this.offtext
501                     }
502                 ]
503             };
504             
505             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506                 cfg.cls += ' '+this.weight;
507             }
508             
509             return cfg;
510         }
511         
512         if (this.isClose) {
513             cfg.cls += ' close';
514             
515             cfg["aria-hidden"] = true;
516             
517             cfg.html = "&times;";
518             
519             return cfg;
520         }
521         
522          
523         if (this.theme==='default') {
524             cfg.cls = 'btn roo-button';
525             
526             //if (this.parentType != 'Navbar') {
527             this.weight = this.weight.length ?  this.weight : 'default';
528             //}
529             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
530                 
531                 cfg.cls += ' btn-' + this.weight;
532             }
533         } else if (this.theme==='glow') {
534             
535             cfg.tag = 'a';
536             cfg.cls = 'btn-glow roo-button';
537             
538             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
539                 
540                 cfg.cls += ' ' + this.weight;
541             }
542         }
543    
544         
545         if (this.inverse) {
546             this.cls += ' inverse';
547         }
548         
549         
550         if (this.active) {
551             cfg.cls += ' active';
552         }
553         
554         if (this.disabled) {
555             cfg.disabled = 'disabled';
556         }
557         
558         if (this.items) {
559             Roo.log('changing to ul' );
560             cfg.tag = 'ul';
561             this.glyphicon = 'caret';
562         }
563         
564         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
565          
566         //gsRoo.log(this.parentType);
567         if (this.parentType === 'Navbar' && !this.parent().bar) {
568             Roo.log('changing to li?');
569             
570             cfg.tag = 'li';
571             
572             cfg.cls = '';
573             cfg.cn =  [{
574                 tag : 'a',
575                 cls : 'roo-button',
576                 html : this.html,
577                 href : this.href || '#'
578             }];
579             if (this.menu) {
580                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
581                 cfg.cls += ' dropdown';
582             }   
583             
584             delete cfg.html;
585             
586         }
587         
588        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
589         
590         if (this.glyphicon) {
591             cfg.html = ' ' + cfg.html;
592             
593             cfg.cn = [
594                 {
595                     tag: 'span',
596                     cls: 'glyphicon glyphicon-' + this.glyphicon
597                 }
598             ];
599         }
600         
601         if (this.badge) {
602             cfg.html += ' ';
603             
604             cfg.tag = 'a';
605             
606 //            cfg.cls='btn roo-button';
607             
608             cfg.href=this.href;
609             
610             var value = cfg.html;
611             
612             if(this.glyphicon){
613                 value = {
614                             tag: 'span',
615                             cls: 'glyphicon glyphicon-' + this.glyphicon,
616                             html: this.html
617                         };
618                 
619             }
620             
621             cfg.cn = [
622                 value,
623                 {
624                     tag: 'span',
625                     cls: 'badge',
626                     html: this.badge
627                 }
628             ];
629             
630             cfg.html='';
631         }
632         
633         if (this.menu) {
634             cfg.cls += ' dropdown';
635             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
636         }
637         
638         if (cfg.tag !== 'a' && this.href !== '') {
639             throw "Tag must be a to set href.";
640         } else if (this.href.length > 0) {
641             cfg.href = this.href;
642         }
643         
644         if(this.removeClass){
645             cfg.cls = '';
646         }
647         
648         if(this.target){
649             cfg.target = this.target;
650         }
651         
652         return cfg;
653     },
654     initEvents: function() {
655        // Roo.log('init events?');
656 //        Roo.log(this.el.dom);
657        if (this.el.hasClass('roo-button')) {
658             this.el.on('click', this.onClick, this);
659        } else {
660             this.el.select('.roo-button').on('click', this.onClick, this);
661        }
662        
663        
664         
665     },
666     onClick : function(e)
667     {
668         if (this.disabled) {
669             return;
670         }
671         
672         Roo.log('button on click ');
673         if(this.preventDefault){
674             e.preventDefault();
675         }
676         if (this.pressed === true || this.pressed === false) {
677             this.pressed = !this.pressed;
678             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679             this.fireEvent('toggle', this, e, this.pressed);
680         }
681         
682         
683         this.fireEvent('click', this, e);
684     },
685     
686     /**
687      * Enables this button
688      */
689     enable : function()
690     {
691         this.disabled = false;
692         this.el.removeClass('disabled');
693     },
694     
695     /**
696      * Disable this button
697      */
698     disable : function()
699     {
700         this.disabled = true;
701         this.el.addClass('disabled');
702     },
703      /**
704      * sets the active state on/off, 
705      * @param {Boolean} state (optional) Force a particular state
706      */
707     setActive : function(v) {
708         
709         this.el[v ? 'addClass' : 'removeClass']('active');
710     },
711      /**
712      * toggles the current active state 
713      */
714     toggleActive : function()
715     {
716        var active = this.el.hasClass('active');
717        this.setActive(!active);
718        
719         
720     }
721     
722     
723     
724 });
725
726  /*
727  * - LGPL
728  *
729  * column
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.Column
735  * @extends Roo.bootstrap.Component
736  * Bootstrap Column class
737  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739  * @cfg {Number} md colspan out of 12 for computer-sized screens
740  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741  * @cfg {String} html content of column.
742  * 
743  * @constructor
744  * Create a new Column
745  * @param {Object} config The config object
746  */
747
748 Roo.bootstrap.Column = function(config){
749     Roo.bootstrap.Column.superclass.constructor.call(this, config);
750 };
751
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
753     
754     xs: null,
755     sm: null,
756     md: null,
757     lg: null,
758     html: '',
759     offset: 0,
760     
761     getAutoCreate : function(){
762         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
763         
764         cfg = {
765             tag: 'div',
766             cls: 'column'
767         };
768         
769         var settings=this;
770         ['xs','sm','md','lg'].map(function(size){
771             if (settings[size]) {
772                 cfg.cls += ' col-' + size + '-' + settings[size];
773             }
774         });
775         if (this.html.length) {
776             cfg.html = this.html;
777         }
778         
779         return cfg;
780     }
781    
782 });
783
784  
785
786  /*
787  * - LGPL
788  *
789  * page container.
790  * 
791  */
792
793
794 /**
795  * @class Roo.bootstrap.Container
796  * @extends Roo.bootstrap.Component
797  * Bootstrap Container class
798  * @cfg {Boolean} jumbotron is it a jumbotron element
799  * @cfg {String} html content of element
800  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802  * @cfg {String} header content of header (for panel)
803  * @cfg {String} footer content of footer (for panel)
804  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
805  *     
806  * @constructor
807  * Create a new Container
808  * @param {Object} config The config object
809  */
810
811 Roo.bootstrap.Container = function(config){
812     Roo.bootstrap.Container.superclass.constructor.call(this, config);
813 };
814
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
816     
817     jumbotron : false,
818     well: '',
819     panel : '',
820     header: '',
821     footer : '',
822     sticky: '',
823   
824      
825     getChildContainer : function() {
826         
827         if(!this.el){
828             return false;
829         }
830         
831         if (this.panel.length) {
832             return this.el.select('.panel-body',true).first();
833         }
834         
835         return this.el;
836     },
837     
838     
839     getAutoCreate : function(){
840         
841         var cfg = {
842             html : '',
843             cls : ''
844         };
845         if (this.jumbotron) {
846             cfg.cls = 'jumbotron';
847         }
848         if (this.cls) {
849             cfg.cls = this.cls + '';
850         }
851         
852         if (this.sticky.length) {
853             
854             var bd = Roo.get(document.body);
855             if (!bd.hasClass('bootstrap-sticky')) {
856                 bd.addClass('bootstrap-sticky');
857                 Roo.select('html',true).setStyle('height', '100%');
858             }
859              
860             cfg.cls += 'bootstrap-sticky-' + this.sticky;
861         }
862         
863         
864         if (this.well.length) {
865             switch (this.well) {
866                 case 'lg':
867                 case 'sm':
868                     cfg.cls +=' well well-' +this.well;
869                     break;
870                 default:
871                     cfg.cls +=' well';
872                     break;
873             }
874         }
875         
876         var body = cfg;
877         
878         if (this.panel.length) {
879             cfg.cls += ' panel panel-' + this.panel;
880             cfg.cn = [];
881             if (this.header.length) {
882                 cfg.cn.push({
883                     
884                     cls : 'panel-heading',
885                     cn : [{
886                         tag: 'h3',
887                         cls : 'panel-title',
888                         html : this.header
889                     }]
890                     
891                 });
892             }
893             body = false;
894             cfg.cn.push({
895                 cls : 'panel-body',
896                 html : this.html
897             });
898             
899             
900             if (this.footer.length) {
901                 cfg.cn.push({
902                     cls : 'panel-footer',
903                     html : this.footer
904                     
905                 });
906             }
907             
908         }
909         if (body) {
910             body.html = this.html || cfg.html;
911         }
912         if (!cfg.cls.length) {
913             cfg.cls =  'container';
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  /*
922  * - LGPL
923  *
924  * image
925  * 
926  */
927
928
929 /**
930  * @class Roo.bootstrap.Img
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Img class
933  * @cfg {Boolean} imgResponsive false | true
934  * @cfg {String} border rounded | circle | thumbnail
935  * @cfg {String} src image source
936  * @cfg {String} alt image alternative text
937  * @cfg {String} href a tag href
938  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
939  * 
940  * @constructor
941  * Create a new Input
942  * @param {Object} config The config object
943  */
944
945 Roo.bootstrap.Img = function(config){
946     Roo.bootstrap.Img.superclass.constructor.call(this, config);
947     
948     this.addEvents({
949         // img events
950         /**
951          * @event click
952          * The img click event for the img.
953          * @param {Roo.EventObject} e
954          */
955         "click" : true
956     });
957 };
958
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
960     
961     imgResponsive: true,
962     border: '',
963     src: '',
964     href: false,
965     target: false,
966
967     getAutoCreate : function(){
968         
969         var cfg = {
970             tag: 'img',
971             cls: 'img-responsive',
972             html : null
973         }
974         
975         cfg.html = this.html || cfg.html;
976         
977         cfg.src = this.src || cfg.src;
978         
979         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980             cfg.cls += ' img-' + this.border;
981         }
982         
983         if(this.alt){
984             cfg.alt = this.alt;
985         }
986         
987         if(this.href){
988             var a = {
989                 tag: 'a',
990                 href: this.href,
991                 cn: [
992                     cfg
993                 ]
994             }
995             
996             if(this.target){
997                 a.target = this.target;
998             }
999             
1000         }
1001         
1002         
1003         return (this.href) ? a : cfg;
1004     },
1005     
1006     initEvents: function() {
1007         
1008         if(!this.href){
1009             this.el.on('click', this.onClick, this);
1010         }
1011     },
1012     
1013     onClick : function(e)
1014     {
1015         Roo.log('img onclick');
1016         this.fireEvent('click', this, e);
1017     }
1018    
1019 });
1020
1021  /*
1022  * - LGPL
1023  *
1024  * header
1025  * 
1026  */
1027
1028 /**
1029  * @class Roo.bootstrap.Header
1030  * @extends Roo.bootstrap.Component
1031  * Bootstrap Header class
1032  * @cfg {String} html content of header
1033  * @cfg {Number} level (1|2|3|4|5|6) default 1
1034  * 
1035  * @constructor
1036  * Create a new Header
1037  * @param {Object} config The config object
1038  */
1039
1040
1041 Roo.bootstrap.Header  = function(config){
1042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1046     
1047     //href : false,
1048     html : false,
1049     level : 1,
1050     
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag: 'h' + (1 *this.level),
1057             html: this.html || 'fill in html'
1058         } ;
1059         
1060         return cfg;
1061     }
1062    
1063 });
1064
1065  
1066
1067  /*
1068  * Based on:
1069  * Ext JS Library 1.1.1
1070  * Copyright(c) 2006-2007, Ext JS, LLC.
1071  *
1072  * Originally Released Under LGPL - original licence link has changed is not relivant.
1073  *
1074  * Fork - LGPL
1075  * <script type="text/javascript">
1076  */
1077  
1078 /**
1079  * @class Roo.bootstrap.MenuMgr
1080  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1081  * @singleton
1082  */
1083 Roo.bootstrap.MenuMgr = function(){
1084    var menus, active, groups = {}, attached = false, lastShow = new Date();
1085
1086    // private - called when first menu is created
1087    function init(){
1088        menus = {};
1089        active = new Roo.util.MixedCollection();
1090        Roo.get(document).addKeyListener(27, function(){
1091            if(active.length > 0){
1092                hideAll();
1093            }
1094        });
1095    }
1096
1097    // private
1098    function hideAll(){
1099        if(active && active.length > 0){
1100            var c = active.clone();
1101            c.each(function(m){
1102                m.hide();
1103            });
1104        }
1105    }
1106
1107    // private
1108    function onHide(m){
1109        active.remove(m);
1110        if(active.length < 1){
1111            Roo.get(document).un("mouseup", onMouseDown);
1112             
1113            attached = false;
1114        }
1115    }
1116
1117    // private
1118    function onShow(m){
1119        var last = active.last();
1120        lastShow = new Date();
1121        active.add(m);
1122        if(!attached){
1123           Roo.get(document).on("mouseup", onMouseDown);
1124            
1125            attached = true;
1126        }
1127        if(m.parentMenu){
1128           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129           m.parentMenu.activeChild = m;
1130        }else if(last && last.isVisible()){
1131           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1132        }
1133    }
1134
1135    // private
1136    function onBeforeHide(m){
1137        if(m.activeChild){
1138            m.activeChild.hide();
1139        }
1140        if(m.autoHideTimer){
1141            clearTimeout(m.autoHideTimer);
1142            delete m.autoHideTimer;
1143        }
1144    }
1145
1146    // private
1147    function onBeforeShow(m){
1148        var pm = m.parentMenu;
1149        if(!pm && !m.allowOtherMenus){
1150            hideAll();
1151        }else if(pm && pm.activeChild && active != m){
1152            pm.activeChild.hide();
1153        }
1154    }
1155
1156    // private
1157    function onMouseDown(e){
1158         Roo.log("on MouseDown");
1159         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1160            hideAll();
1161         }
1162         
1163         
1164    }
1165
1166    // private
1167    function onBeforeCheck(mi, state){
1168        if(state){
1169            var g = groups[mi.group];
1170            for(var i = 0, l = g.length; i < l; i++){
1171                if(g[i] != mi){
1172                    g[i].setChecked(false);
1173                }
1174            }
1175        }
1176    }
1177
1178    return {
1179
1180        /**
1181         * Hides all menus that are currently visible
1182         */
1183        hideAll : function(){
1184             hideAll();  
1185        },
1186
1187        // private
1188        register : function(menu){
1189            if(!menus){
1190                init();
1191            }
1192            menus[menu.id] = menu;
1193            menu.on("beforehide", onBeforeHide);
1194            menu.on("hide", onHide);
1195            menu.on("beforeshow", onBeforeShow);
1196            menu.on("show", onShow);
1197            var g = menu.group;
1198            if(g && menu.events["checkchange"]){
1199                if(!groups[g]){
1200                    groups[g] = [];
1201                }
1202                groups[g].push(menu);
1203                menu.on("checkchange", onCheck);
1204            }
1205        },
1206
1207         /**
1208          * Returns a {@link Roo.menu.Menu} object
1209          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210          * be used to generate and return a new Menu instance.
1211          */
1212        get : function(menu){
1213            if(typeof menu == "string"){ // menu id
1214                return menus[menu];
1215            }else if(menu.events){  // menu instance
1216                return menu;
1217            }
1218            /*else if(typeof menu.length == 'number'){ // array of menu items?
1219                return new Roo.bootstrap.Menu({items:menu});
1220            }else{ // otherwise, must be a config
1221                return new Roo.bootstrap.Menu(menu);
1222            }
1223            */
1224            return false;
1225        },
1226
1227        // private
1228        unregister : function(menu){
1229            delete menus[menu.id];
1230            menu.un("beforehide", onBeforeHide);
1231            menu.un("hide", onHide);
1232            menu.un("beforeshow", onBeforeShow);
1233            menu.un("show", onShow);
1234            var g = menu.group;
1235            if(g && menu.events["checkchange"]){
1236                groups[g].remove(menu);
1237                menu.un("checkchange", onCheck);
1238            }
1239        },
1240
1241        // private
1242        registerCheckable : function(menuItem){
1243            var g = menuItem.group;
1244            if(g){
1245                if(!groups[g]){
1246                    groups[g] = [];
1247                }
1248                groups[g].push(menuItem);
1249                menuItem.on("beforecheckchange", onBeforeCheck);
1250            }
1251        },
1252
1253        // private
1254        unregisterCheckable : function(menuItem){
1255            var g = menuItem.group;
1256            if(g){
1257                groups[g].remove(menuItem);
1258                menuItem.un("beforecheckchange", onBeforeCheck);
1259            }
1260        }
1261    };
1262 }();/*
1263  * - LGPL
1264  *
1265  * menu
1266  * 
1267  */
1268
1269 /**
1270  * @class Roo.bootstrap.Menu
1271  * @extends Roo.bootstrap.Component
1272  * Bootstrap Menu class - container for MenuItems
1273  * @cfg {String} type type of menu
1274  * 
1275  * @constructor
1276  * Create a new Menu
1277  * @param {Object} config The config object
1278  */
1279
1280
1281 Roo.bootstrap.Menu = function(config){
1282     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283     if (this.registerMenu) {
1284         Roo.bootstrap.MenuMgr.register(this);
1285     }
1286     this.addEvents({
1287         /**
1288          * @event beforeshow
1289          * Fires before this menu is displayed
1290          * @param {Roo.menu.Menu} this
1291          */
1292         beforeshow : true,
1293         /**
1294          * @event beforehide
1295          * Fires before this menu is hidden
1296          * @param {Roo.menu.Menu} this
1297          */
1298         beforehide : true,
1299         /**
1300          * @event show
1301          * Fires after this menu is displayed
1302          * @param {Roo.menu.Menu} this
1303          */
1304         show : true,
1305         /**
1306          * @event hide
1307          * Fires after this menu is hidden
1308          * @param {Roo.menu.Menu} this
1309          */
1310         hide : true,
1311         /**
1312          * @event click
1313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314          * @param {Roo.menu.Menu} this
1315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316          * @param {Roo.EventObject} e
1317          */
1318         click : true,
1319         /**
1320          * @event mouseover
1321          * Fires when the mouse is hovering over this menu
1322          * @param {Roo.menu.Menu} this
1323          * @param {Roo.EventObject} e
1324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1325          */
1326         mouseover : true,
1327         /**
1328          * @event mouseout
1329          * Fires when the mouse exits this menu
1330          * @param {Roo.menu.Menu} this
1331          * @param {Roo.EventObject} e
1332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1333          */
1334         mouseout : true,
1335         /**
1336          * @event itemclick
1337          * Fires when a menu item contained in this menu is clicked
1338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339          * @param {Roo.EventObject} e
1340          */
1341         itemclick: true
1342     });
1343     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1347     
1348    /// html : false,
1349     //align : '',
1350     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1351     type: false,
1352     /**
1353      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1354      */
1355     registerMenu : true,
1356     
1357     menuItems :false, // stores the menu items..
1358     
1359     hidden:true,
1360     
1361     parentMenu : false,
1362     
1363     getChildContainer : function() {
1364         return this.el;  
1365     },
1366     
1367     getAutoCreate : function(){
1368          
1369         //if (['right'].indexOf(this.align)!==-1) {
1370         //    cfg.cn[1].cls += ' pull-right'
1371         //}
1372         var cfg = {
1373             tag : 'ul',
1374             cls : 'dropdown-menu' ,
1375             style : 'z-index:1000'
1376             
1377         }
1378         
1379         if (this.type === 'submenu') {
1380             cfg.cls = 'submenu active'
1381         }
1382         
1383         return cfg;
1384     },
1385     initEvents : function() {
1386         
1387        // Roo.log("ADD event");
1388        // Roo.log(this.triggerEl.dom);
1389         this.triggerEl.on('click', this.onTriggerPress, this);
1390         this.triggerEl.addClass('dropdown-toggle');
1391         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1392
1393         this.el.on("mouseover", this.onMouseOver, this);
1394         this.el.on("mouseout", this.onMouseOut, this);
1395         
1396         
1397     },
1398     findTargetItem : function(e){
1399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1400         if(!t){
1401             return false;
1402         }
1403         //Roo.log(t);         Roo.log(t.id);
1404         if(t && t.id){
1405             //Roo.log(this.menuitems);
1406             return this.menuitems.get(t.id);
1407             
1408             //return this.items.get(t.menuItemId);
1409         }
1410         
1411         return false;
1412     },
1413     onClick : function(e){
1414         Roo.log("menu.onClick");
1415         var t = this.findTargetItem(e);
1416         if(!t){
1417             return;
1418         }
1419         Roo.log(e);
1420         /*
1421         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1422             if(t == this.activeItem && t.shouldDeactivate(e)){
1423                 this.activeItem.deactivate();
1424                 delete this.activeItem;
1425                 return;
1426             }
1427             if(t.canActivate){
1428                 this.setActiveItem(t, true);
1429             }
1430             return;
1431             
1432             
1433         }
1434         */
1435         Roo.log('pass click event');
1436         
1437         t.onClick(e);
1438         
1439         this.fireEvent("click", this, t, e);
1440         
1441         this.hide();
1442     },
1443      onMouseOver : function(e){
1444         var t  = this.findTargetItem(e);
1445         //Roo.log(t);
1446         //if(t){
1447         //    if(t.canActivate && !t.disabled){
1448         //        this.setActiveItem(t, true);
1449         //    }
1450         //}
1451         
1452         this.fireEvent("mouseover", this, e, t);
1453     },
1454     isVisible : function(){
1455         return !this.hidden;
1456     },
1457      onMouseOut : function(e){
1458         var t  = this.findTargetItem(e);
1459         
1460         //if(t ){
1461         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1462         //        this.activeItem.deactivate();
1463         //        delete this.activeItem;
1464         //    }
1465         //}
1466         this.fireEvent("mouseout", this, e, t);
1467     },
1468     
1469     
1470     /**
1471      * Displays this menu relative to another element
1472      * @param {String/HTMLElement/Roo.Element} element The element to align to
1473      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474      * the element (defaults to this.defaultAlign)
1475      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1476      */
1477     show : function(el, pos, parentMenu){
1478         this.parentMenu = parentMenu;
1479         if(!this.el){
1480             this.render();
1481         }
1482         this.fireEvent("beforeshow", this);
1483         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1484     },
1485      /**
1486      * Displays this menu at a specific xy position
1487      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489      */
1490     showAt : function(xy, parentMenu, /* private: */_e){
1491         this.parentMenu = parentMenu;
1492         if(!this.el){
1493             this.render();
1494         }
1495         if(_e !== false){
1496             this.fireEvent("beforeshow", this);
1497             
1498             //xy = this.el.adjustForConstraints(xy);
1499         }
1500         //this.el.setXY(xy);
1501         //this.el.show();
1502         this.hideMenuItems();
1503         this.hidden = false;
1504         this.triggerEl.addClass('open');
1505         this.focus();
1506         this.fireEvent("show", this);
1507     },
1508     
1509     focus : function(){
1510         return;
1511         if(!this.hidden){
1512             this.doFocus.defer(50, this);
1513         }
1514     },
1515
1516     doFocus : function(){
1517         if(!this.hidden){
1518             this.focusEl.focus();
1519         }
1520     },
1521
1522     /**
1523      * Hides this menu and optionally all parent menus
1524      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1525      */
1526     hide : function(deep){
1527         
1528         this.hideMenuItems();
1529         if(this.el && this.isVisible()){
1530             this.fireEvent("beforehide", this);
1531             if(this.activeItem){
1532                 this.activeItem.deactivate();
1533                 this.activeItem = null;
1534             }
1535             this.triggerEl.removeClass('open');;
1536             this.hidden = true;
1537             this.fireEvent("hide", this);
1538         }
1539         if(deep === true && this.parentMenu){
1540             this.parentMenu.hide(true);
1541         }
1542     },
1543     
1544     onTriggerPress  : function(e)
1545     {
1546         
1547         Roo.log('trigger press');
1548         //Roo.log(e.getTarget());
1549        // Roo.log(this.triggerEl.dom);
1550         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1551             return;
1552         }
1553         if (this.isVisible()) {
1554             Roo.log('hide');
1555             this.hide();
1556         } else {
1557             this.show(this.triggerEl, false, false);
1558         }
1559         
1560         
1561     },
1562     
1563          
1564        
1565     
1566     hideMenuItems : function()
1567     {
1568         //$(backdrop).remove()
1569         Roo.select('.open',true).each(function(aa) {
1570             
1571             aa.removeClass('open');
1572           //var parent = getParent($(this))
1573           //var relatedTarget = { relatedTarget: this }
1574           
1575            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576           //if (e.isDefaultPrevented()) return
1577            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1578         })
1579     },
1580     addxtypeChild : function (tree, cntr) {
1581         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1582           
1583         this.menuitems.add(comp);
1584         return comp;
1585
1586     },
1587     getEl : function()
1588     {
1589         Roo.log(this.el);
1590         return this.el;
1591     }
1592 });
1593
1594  
1595  /*
1596  * - LGPL
1597  *
1598  * menu item
1599  * 
1600  */
1601
1602
1603 /**
1604  * @class Roo.bootstrap.MenuItem
1605  * @extends Roo.bootstrap.Component
1606  * Bootstrap MenuItem class
1607  * @cfg {String} html the menu label
1608  * @cfg {String} href the link
1609  * @cfg {Boolean} preventDefault (true | false) default true
1610  * 
1611  * 
1612  * @constructor
1613  * Create a new MenuItem
1614  * @param {Object} config The config object
1615  */
1616
1617
1618 Roo.bootstrap.MenuItem = function(config){
1619     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1620     this.addEvents({
1621         // raw events
1622         /**
1623          * @event click
1624          * The raw click event for the entire grid.
1625          * @param {Roo.EventObject} e
1626          */
1627         "click" : true
1628     });
1629 };
1630
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1632     
1633     href : false,
1634     html : false,
1635     preventDefault: true,
1636     
1637     getAutoCreate : function(){
1638         var cfg= {
1639             tag: 'li',
1640         cls: 'dropdown-menu-item',
1641             cn: [
1642             {
1643                 tag : 'a',
1644                 href : '#',
1645                 html : 'Link'
1646             }
1647             ]
1648     };
1649         
1650         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1652         return cfg;
1653     },
1654     
1655     initEvents: function() {
1656         
1657         //this.el.select('a').on('click', this.onClick, this);
1658         
1659     },
1660     onClick : function(e)
1661     {
1662         Roo.log('item on click ');
1663         //if(this.preventDefault){
1664         //    e.preventDefault();
1665         //}
1666         //this.parent().hideMenuItems();
1667         
1668         this.fireEvent('click', this, e);
1669     },
1670     getEl : function()
1671     {
1672         return this.el;
1673     }
1674 });
1675
1676  
1677
1678  /*
1679  * - LGPL
1680  *
1681  * menu separator
1682  * 
1683  */
1684
1685
1686 /**
1687  * @class Roo.bootstrap.MenuSeparator
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap MenuSeparator class
1690  * 
1691  * @constructor
1692  * Create a new MenuItem
1693  * @param {Object} config The config object
1694  */
1695
1696
1697 Roo.bootstrap.MenuSeparator = function(config){
1698     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1699 };
1700
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1702     
1703     getAutoCreate : function(){
1704         var cfg = {
1705             cls: 'divider',
1706             tag : 'li'
1707         };
1708         
1709         return cfg;
1710     }
1711    
1712 });
1713
1714  
1715
1716  
1717 /*
1718 <div class="modal fade">
1719   <div class="modal-dialog">
1720     <div class="modal-content">
1721       <div class="modal-header">
1722         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1723         <h4 class="modal-title">Modal title</h4>
1724       </div>
1725       <div class="modal-body">
1726         <p>One fine body&hellip;</p>
1727       </div>
1728       <div class="modal-footer">
1729         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730         <button type="button" class="btn btn-primary">Save changes</button>
1731       </div>
1732     </div><!-- /.modal-content -->
1733   </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1735 */
1736 /*
1737  * - LGPL
1738  *
1739  * page contgainer.
1740  * 
1741  */
1742
1743 /**
1744  * @class Roo.bootstrap.Modal
1745  * @extends Roo.bootstrap.Component
1746  * Bootstrap Modal class
1747  * @cfg {String} title Title of dialog
1748  * @cfg {Array} buttons Array of buttons or standard button set..
1749  * 
1750  * @constructor
1751  * Create a new Modal Dialog
1752  * @param {Object} config The config object
1753  */
1754
1755 Roo.bootstrap.Modal = function(config){
1756     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1757     this.addEvents({
1758         // raw events
1759         /**
1760          * @event btnclick
1761          * The raw btnclick event for the button
1762          * @param {Roo.EventObject} e
1763          */
1764         "btnclick" : true
1765     });
1766 };
1767
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1769     
1770     title : 'test dialog',
1771    
1772     buttons : false,
1773     
1774     onRender : function(ct, position)
1775     {
1776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1777      
1778         if(!this.el){
1779             var cfg = Roo.apply({},  this.getAutoCreate());
1780             cfg.id = Roo.id();
1781             //if(!cfg.name){
1782             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1783             //}
1784             //if (!cfg.name.length) {
1785             //    delete cfg.name;
1786            // }
1787             if (this.cls) {
1788                 cfg.cls += ' ' + this.cls;
1789             }
1790             if (this.style) {
1791                 cfg.style = this.style;
1792             }
1793             this.el = Roo.get(document.body).createChild(cfg, position);
1794         }
1795         //var type = this.el.dom.type;
1796         
1797         if(this.tabIndex !== undefined){
1798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1799         }
1800         
1801         
1802         
1803         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804         this.maskEl.enableDisplayMode("block");
1805         this.maskEl.hide();
1806         //this.el.addClass("x-dlg-modal");
1807     
1808         if (this.buttons) {
1809             Roo.each(this.buttons, function(bb) {
1810                 b = Roo.apply({}, bb);
1811                 b.xns = b.xns || Roo.bootstrap;
1812                 b.xtype = b.xtype || 'Button';
1813                 if (typeof(b.listeners) == 'undefined') {
1814                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1815                 }
1816                 
1817                 var btn = Roo.factory(b);
1818                 
1819                 btn.onRender(this.el.select('.modal-footer').first());
1820                 
1821             },this);
1822         }
1823         // render the children.
1824         var nitems = [];
1825         
1826         if(typeof(this.items) != 'undefined'){
1827             var items = this.items;
1828             delete this.items;
1829
1830             for(var i =0;i < items.length;i++) {
1831                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1832             }
1833         }
1834         
1835         this.items = nitems;
1836         this.initEvents();
1837         //this.el.addClass([this.fieldClass, this.cls]);
1838         
1839     },
1840     getAutoCreate : function(){
1841         
1842         
1843         var bdy = {
1844                 cls : 'modal-body',
1845                 html : this.html || ''
1846         };
1847         
1848          
1849         return modal = {
1850             cls: "modal fade",
1851             cn : [
1852                 {
1853                     cls: "modal-dialog",
1854                     cn : [
1855                         {
1856                             cls : "modal-content",
1857                             cn : [
1858                                 {
1859                                     cls : 'modal-header',
1860                                     cn : [
1861                                         {
1862                                             tag: 'button',
1863                                             cls : 'close',
1864                                             html : '&times'
1865                                         },
1866                                         {
1867                                             tag: 'h4',
1868                                             cls : 'modal-title',
1869                                             html : this.title
1870                                         }
1871                                     
1872                                     ]
1873                                 },
1874                                 bdy,
1875                                 {
1876                                     cls : 'modal-footer' 
1877                                 }
1878                                 
1879                                 
1880                             ]
1881                             
1882                         }
1883                     ]
1884                         
1885                 }
1886             ]
1887             
1888             
1889         };
1890           
1891     },
1892     getChildContainer : function() {
1893          
1894          return this.el.select('.modal-body',true).first();
1895         
1896     },
1897     getButtonContainer : function() {
1898          return this.el.select('.modal-footer',true).first();
1899         
1900     },
1901     initEvents : function()
1902     {
1903         this.el.select('.modal-header .close').on('click', this.hide, this);
1904 //        
1905 //        this.addxtype(this);
1906     },
1907     show : function() {
1908         
1909         if (!this.rendered) {
1910             this.render();
1911         }
1912        
1913         this.el.addClass('on');
1914         this.el.removeClass('fade');
1915         this.el.setStyle('display', 'block');
1916         Roo.get(document.body).addClass("x-body-masked");
1917         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1918         this.maskEl.show();
1919         this.el.setStyle('zIndex', '10001');
1920         this.fireEvent('show', this);
1921         
1922         
1923     },
1924     hide : function()
1925     {
1926         Roo.log('Modal hide?!');
1927         this.maskEl.hide();
1928         Roo.get(document.body).removeClass("x-body-masked");
1929         this.el.removeClass('on');
1930         this.el.addClass('fade');
1931         this.el.setStyle('display', 'none');
1932         this.fireEvent('hide', this);
1933     },
1934     onButtonClick: function(btn,e)
1935     {
1936         //Roo.log([a,b,c]);
1937         this.fireEvent('btnclick', btn.name, e);
1938     }
1939 });
1940
1941
1942 Roo.apply(Roo.bootstrap.Modal,  {
1943     /**
1944          * Button config that displays a single OK button
1945          * @type Object
1946          */
1947         OK :  [{
1948             name : 'ok',
1949             weight : 'primary',
1950             html : 'OK'
1951         }], 
1952         /**
1953          * Button config that displays Yes and No buttons
1954          * @type Object
1955          */
1956         YESNO : [
1957             {
1958                 name  : 'no',
1959                 html : 'No'
1960             },
1961             {
1962                 name  :'yes',
1963                 weight : 'primary',
1964                 html : 'Yes'
1965             }
1966         ],
1967         
1968         /**
1969          * Button config that displays OK and Cancel buttons
1970          * @type Object
1971          */
1972         OKCANCEL : [
1973             {
1974                name : 'cancel',
1975                 html : 'Cancel'
1976             },
1977             {
1978                 name : 'ok',
1979                 weight : 'primary',
1980                 html : 'OK'
1981             }
1982         ],
1983         /**
1984          * Button config that displays Yes, No and Cancel buttons
1985          * @type Object
1986          */
1987         YESNOCANCEL : [
1988             {
1989                 name : 'yes',
1990                 weight : 'primary',
1991                 html : 'Yes'
1992             },
1993             {
1994                 name : 'no',
1995                 html : 'No'
1996             },
1997             {
1998                 name : 'cancel',
1999                 html : 'Cancel'
2000             }
2001         ]
2002 });
2003  /*
2004  * - LGPL
2005  *
2006  * navbar
2007  * 
2008  */
2009
2010 /**
2011  * @class Roo.bootstrap.Navbar
2012  * @extends Roo.bootstrap.Component
2013  * Bootstrap Navbar class
2014  * @cfg {Boolean} sidebar has side bar
2015  * @cfg {Boolean} bar is a bar?
2016  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017  * @cfg {String} brand what is brand
2018  * @cfg {Boolean} inverse is inverted color
2019  * @cfg {String} type (nav | pills | tabs)
2020  * @cfg {Boolean} arrangement stacked | justified
2021  * @cfg {String} align (left | right) alignment
2022  * @cfg {String} brand_href href of the brand
2023  * @cfg {Boolean} main (true|false) main nav bar? default false
2024  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2025  *
2026  * 
2027  * @constructor
2028  * Create a new Navbar
2029  * @param {Object} config The config object
2030  */
2031
2032
2033 Roo.bootstrap.Navbar = function(config){
2034     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2035 };
2036
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2038     
2039     sidebar: false,
2040     
2041     bar: false,
2042     brand: '',
2043     inverse: false,
2044     position: '',
2045     align : false,
2046     type: 'nav',
2047     arrangement: '',
2048     brand_href: false,
2049     main : false,
2050     loadMask : false,
2051     
2052     getAutoCreate : function(){
2053         var cfg = {
2054             cls : 'navbar'
2055         };
2056         
2057         if (this.sidebar === true) {
2058             cfg = {
2059                 tag: 'div',
2060                 cls: 'sidebar-nav'
2061             };
2062             return cfg;
2063         }
2064         
2065         if (this.bar === true) {
2066             cfg = {
2067                 tag: 'nav',
2068                 cls: 'navbar',
2069                 role: 'navigation',
2070                 cn: [
2071                     {
2072                         tag: 'div',
2073                         cls: 'navbar-header',
2074                         cn: [
2075                             {
2076                             tag: 'button',
2077                             type: 'button',
2078                             cls: 'navbar-toggle',
2079                             'data-toggle': 'collapse',
2080                             cn: [
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'sr-only',
2084                                     html: 'Toggle navigation'
2085                                 },
2086                                 {
2087                                     tag: 'span',
2088                                     cls: 'icon-bar'
2089                                 },
2090                                 {
2091                                     tag: 'span',
2092                                     cls: 'icon-bar'
2093                                 },
2094                                 {
2095                                     tag: 'span',
2096                                     cls: 'icon-bar'
2097                                 }
2098                             ]
2099                             }
2100                         ]
2101                     },
2102                     {
2103                     tag: 'div',
2104                     cls: 'collapse navbar-collapse'
2105                     }
2106                 ]
2107             };
2108             
2109             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2110             
2111             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112                 cfg.cls += ' navbar-' + this.position;
2113                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2114             }
2115             
2116             if (this.brand !== '') {
2117                 cfg.cn[0].cn.push({
2118                     tag: 'a',
2119                     href: this.brand_href ? this.brand_href : '#',
2120                     cls: 'navbar-brand',
2121                     cn: [
2122                     this.brand
2123                     ]
2124                 });
2125             }
2126             
2127             if(this.main){
2128                 cfg.cls += ' main-nav';
2129             }
2130             
2131             
2132             return cfg;
2133         
2134         } else if (this.bar === false) {
2135             
2136         } else {
2137             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2138         }
2139         
2140         cfg.cn = [
2141             {
2142                 cls: 'nav',
2143                 tag : 'ul'
2144             }
2145         ];
2146         
2147         if (['tabs','pills'].indexOf(this.type)!==-1) {
2148             cfg.cn[0].cls += ' nav-' + this.type
2149         } else {
2150             if (this.type!=='nav') {
2151             Roo.log('nav type must be nav/tabs/pills')
2152             }
2153             cfg.cn[0].cls += ' navbar-nav'
2154         }
2155         
2156         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157             cfg.cn[0].cls += ' nav-' + this.arrangement;
2158         }
2159         
2160         if (this.align === 'right') {
2161             cfg.cn[0].cls += ' navbar-right';
2162         }
2163         if (this.inverse) {
2164             cfg.cls += ' navbar-inverse';
2165             
2166         }
2167         
2168         
2169         return cfg;
2170     },
2171     
2172     initEvents :function ()
2173     {
2174         //Roo.log(this.el.select('.navbar-toggle',true));
2175         this.el.select('.navbar-toggle',true).on('click', function() {
2176            // Roo.log('click');
2177             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2178         }, this);
2179         
2180         var mark = {
2181             tag: "div",
2182             cls:"x-dlg-mask"
2183         }
2184         
2185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2186         
2187         var size = this.el.getSize();
2188         this.maskEl.setSize(size.width, size.height);
2189         this.maskEl.enableDisplayMode("block");
2190         this.maskEl.hide();
2191         
2192         if(this.loadMask){
2193             this.maskEl.show();
2194         }
2195     },
2196     
2197     
2198     getChildContainer : function()
2199     {
2200         if (this.bar === true) {
2201             return this.el.select('.collapse',true).first();
2202         }
2203         
2204         return this.el;
2205     },
2206     
2207     mask : function()
2208     {
2209         this.maskEl.show();
2210     },
2211     
2212     unmask : function()
2213     {
2214         this.maskEl.hide();
2215     }
2216    
2217 });
2218
2219  
2220
2221  /*
2222  * - LGPL
2223  *
2224  * nav group
2225  * 
2226  */
2227
2228 /**
2229  * @class Roo.bootstrap.NavGroup
2230  * @extends Roo.bootstrap.Component
2231  * Bootstrap NavGroup class
2232  * @cfg {String} align left | right
2233  * @cfg {Boolean} inverse false | true
2234  * @cfg {String} type (nav|pills|tab) default nav
2235  * 
2236  * @constructor
2237  * Create a new nav group
2238  * @param {Object} config The config object
2239  */
2240
2241 Roo.bootstrap.NavGroup = function(config){
2242     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2243 };
2244
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2246     
2247     align: '',
2248     inverse: false,
2249     form: false,
2250     type: 'nav',
2251     
2252     getAutoCreate : function(){
2253         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2254         
2255         cfg = {
2256             tag : 'ul',
2257             cls: 'nav' 
2258         }
2259         
2260         if (['tabs','pills'].indexOf(this.type)!==-1) {
2261             cfg.cls += ' nav-' + this.type
2262         } else {
2263             if (this.type!=='nav') {
2264                 Roo.log('nav type must be nav/tabs/pills')
2265             }
2266             cfg.cls += ' navbar-nav'
2267         }
2268         
2269         if (this.parent().sidebar === true) {
2270             cfg = {
2271                 tag: 'ul',
2272                 cls: 'dashboard-menu'
2273             }
2274             
2275             return cfg;
2276         }
2277         
2278         if (this.form === true) {
2279             cfg = {
2280                 tag: 'form',
2281                 cls: 'navbar-form'
2282             }
2283             
2284             if (this.align === 'right') {
2285                 cfg.cls += ' navbar-right';
2286             } else {
2287                 cfg.cls += ' navbar-left';
2288             }
2289         }
2290         
2291         if (this.align === 'right') {
2292             cfg.cls += ' navbar-right';
2293         }
2294         
2295         if (this.inverse) {
2296             cfg.cls += ' navbar-inverse';
2297             
2298         }
2299         
2300         
2301         return cfg;
2302     }
2303    
2304 });
2305
2306  
2307
2308  /*
2309  * - LGPL
2310  *
2311  * row
2312  * 
2313  */
2314
2315 /**
2316  * @class Roo.bootstrap.Navbar.Item
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap Navbar.Button class
2319  * @cfg {String} href  link to
2320  * @cfg {String} html content of button
2321  * @cfg {String} badge text inside badge
2322  * @cfg {String} glyphicon name of glyphicon
2323  * @cfg {String} icon name of font awesome icon
2324  * @cfg {Boolena} active Is item active
2325  * @cfg {Boolean} preventDefault (true | false) default false
2326   
2327  * @constructor
2328  * Create a new Navbar Button
2329  * @param {Object} config The config object
2330  */
2331 Roo.bootstrap.Navbar.Item = function(config){
2332     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2333     this.addEvents({
2334         // raw events
2335         /**
2336          * @event click
2337          * The raw click event for the entire grid.
2338          * @param {Roo.EventObject} e
2339          */
2340         "click" : true
2341     });
2342 };
2343
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2345     
2346     href: false,
2347     html: '',
2348     badge: '',
2349     icon: false,
2350     glyphicon: false,
2351     icon: false,
2352     active: false,
2353     preventDefault : false,
2354     
2355     getAutoCreate : function(){
2356         
2357         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2358         
2359         if (this.parent().parent().sidebar === true) {
2360             cfg = {
2361                 tag: 'li',
2362                 cls: '',
2363                 cn: [
2364                     {
2365                         tag: 'p',
2366                         cls: ''
2367                     }
2368                 ]
2369             }
2370             
2371             if (this.html) {
2372                 cfg.cn[0].html = this.html;
2373             }
2374             
2375             if (this.active) {
2376                 this.cls += ' active';
2377             }
2378             
2379             if (this.menu) {
2380                 cfg.cn[0].cls += ' dropdown-toggle';
2381                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2382             }
2383             
2384             if (this.href) {
2385                 cfg.cn[0].tag = 'a',
2386                 cfg.cn[0].href = this.href;
2387             }
2388             
2389             if (this.glyphicon) {
2390                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2391             }
2392             
2393             if (this.icon) {
2394                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2395             }
2396             
2397             return cfg;
2398         }
2399         
2400         cfg = {
2401             tag: 'li',
2402             cls: 'nav-item'
2403         }
2404         
2405         if (this.active) {
2406             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2407         }
2408             
2409         cfg.cn = [
2410             {
2411                 tag: 'p',
2412                 html: 'Text'
2413             }
2414         ];
2415         
2416         if (this.glyphicon) {
2417             if(cfg.html){cfg.html = ' ' + this.html};
2418             cfg.cn=[
2419                 {
2420                     tag: 'span',
2421                     cls: 'glyphicon glyphicon-' + this.glyphicon
2422                 }
2423             ];
2424         }
2425         
2426         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2427         
2428         if (this.menu) {
2429             cfg.cn[0].tag='a';
2430             cfg.cn[0].href='#';
2431             cfg.cn[0].html += " <span class='caret'></span>";
2432         //}else if (!this.href) {
2433         //    cfg.cn[0].tag='p';
2434         //    cfg.cn[0].cls='navbar-text';
2435         } else {
2436             cfg.cn[0].tag='a';
2437             cfg.cn[0].href=this.href||'#';
2438             cfg.cn[0].html=this.html;
2439         }
2440         
2441         if (this.badge !== '') {
2442             
2443             cfg.cn[0].cn=[
2444                 cfg.cn[0].html + ' ',
2445                 {
2446                     tag: 'span',
2447                     cls: 'badge',
2448                     html: this.badge
2449                 }
2450             ];
2451             cfg.cn[0].html=''
2452         }
2453          
2454         if (this.icon) {
2455             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2456         }
2457         
2458         return cfg;
2459     },
2460     initEvents: function() {
2461        // Roo.log('init events?');
2462        // Roo.log(this.el.dom);
2463         this.el.select('a',true).on('click', this.onClick, this);
2464     },
2465     
2466     onClick : function(e)
2467     {
2468         if(this.preventDefault){
2469             e.preventDefault();
2470         }
2471         
2472         if(this.fireEvent('click', this, e) === false){
2473             return;
2474         };
2475         
2476         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477             this.onTabsClick(e);
2478         } 
2479     },
2480     
2481     onTabsClick : function(e)
2482     {
2483         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484             v.removeClass('active');
2485         })
2486
2487         this.el.addClass('active');
2488
2489         if(this.href && this.href.substring(0,1) == '#'){
2490             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2491
2492             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493                 v.removeClass('active');
2494             });
2495
2496             tab.addClass('active');
2497         }
2498     }
2499    
2500 });
2501  
2502
2503  /*
2504  * - LGPL
2505  *
2506  * row
2507  * 
2508  */
2509
2510 /**
2511  * @class Roo.bootstrap.Row
2512  * @extends Roo.bootstrap.Component
2513  * Bootstrap Row class (contains columns...)
2514  * 
2515  * @constructor
2516  * Create a new Row
2517  * @param {Object} config The config object
2518  */
2519
2520 Roo.bootstrap.Row = function(config){
2521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2525     
2526     getAutoCreate : function(){
2527        return {
2528             cls: 'row clearfix'
2529        };
2530     }
2531     
2532     
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * element
2541  * 
2542  */
2543
2544 /**
2545  * @class Roo.bootstrap.Element
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap Element class
2548  * @cfg {String} html contents of the element
2549  * @cfg {String} tag tag of the element
2550  * @cfg {String} cls class of the element
2551  * 
2552  * @constructor
2553  * Create a new Element
2554  * @param {Object} config The config object
2555  */
2556
2557 Roo.bootstrap.Element = function(config){
2558     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2559 };
2560
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2562     
2563     tag: 'div',
2564     cls: '',
2565     html: '',
2566      
2567     
2568     getAutoCreate : function(){
2569         
2570         var cfg = {
2571             tag: this.tag,
2572             cls: this.cls,
2573             html: this.html
2574         }
2575         
2576         
2577         
2578         return cfg;
2579     }
2580    
2581 });
2582
2583  
2584
2585  /*
2586  * - LGPL
2587  *
2588  * pagination
2589  * 
2590  */
2591
2592 /**
2593  * @class Roo.bootstrap.Pagination
2594  * @extends Roo.bootstrap.Component
2595  * Bootstrap Pagination class
2596  * @cfg {String} size xs | sm | md | lg
2597  * @cfg {Boolean} inverse false | true
2598  * 
2599  * @constructor
2600  * Create a new Pagination
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Pagination = function(config){
2605     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2606 };
2607
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2609     
2610     cls: false,
2611     size: false,
2612     inverse: false,
2613     
2614     getAutoCreate : function(){
2615         var cfg = {
2616             tag: 'ul',
2617                 cls: 'pagination'
2618         };
2619         if (this.inverse) {
2620             cfg.cls += ' inverse';
2621         }
2622         if (this.html) {
2623             cfg.html=this.html;
2624         }
2625         if (this.cls) {
2626             cfg.cls += " " + this.cls;
2627         }
2628         return cfg;
2629     }
2630    
2631 });
2632
2633  
2634
2635  /*
2636  * - LGPL
2637  *
2638  * Pagination item
2639  * 
2640  */
2641
2642
2643 /**
2644  * @class Roo.bootstrap.PaginationItem
2645  * @extends Roo.bootstrap.Component
2646  * Bootstrap PaginationItem class
2647  * @cfg {String} html text
2648  * @cfg {String} href the link
2649  * @cfg {Boolean} preventDefault (true | false) default true
2650  * @cfg {Boolean} active (true | false) default false
2651  * 
2652  * 
2653  * @constructor
2654  * Create a new PaginationItem
2655  * @param {Object} config The config object
2656  */
2657
2658
2659 Roo.bootstrap.PaginationItem = function(config){
2660     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2661     this.addEvents({
2662         // raw events
2663         /**
2664          * @event click
2665          * The raw click event for the entire grid.
2666          * @param {Roo.EventObject} e
2667          */
2668         "click" : true
2669     });
2670 };
2671
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2673     
2674     href : false,
2675     html : false,
2676     preventDefault: true,
2677     active : false,
2678     cls : false,
2679     
2680     getAutoCreate : function(){
2681         var cfg= {
2682             tag: 'li',
2683             cn: [
2684                 {
2685                     tag : 'a',
2686                     href : this.href ? this.href : '#',
2687                     html : this.html ? this.html : ''
2688                 }
2689             ]
2690         };
2691         
2692         if(this.cls){
2693             cfg.cls = this.cls;
2694         }
2695         
2696         if(this.active){
2697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2698         }
2699         
2700         return cfg;
2701     },
2702     
2703     initEvents: function() {
2704         
2705         this.el.on('click', this.onClick, this);
2706         
2707     },
2708     onClick : function(e)
2709     {
2710         Roo.log('PaginationItem on click ');
2711         if(this.preventDefault){
2712             e.preventDefault();
2713         }
2714         
2715         this.fireEvent('click', this, e);
2716     }
2717    
2718 });
2719
2720  
2721
2722  /*
2723  * - LGPL
2724  *
2725  * slider
2726  * 
2727  */
2728
2729
2730 /**
2731  * @class Roo.bootstrap.Slider
2732  * @extends Roo.bootstrap.Component
2733  * Bootstrap Slider class
2734  *    
2735  * @constructor
2736  * Create a new Slider
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.Slider = function(config){
2741     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2745     
2746     getAutoCreate : function(){
2747         
2748         var cfg = {
2749             tag: 'div',
2750             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2751             cn: [
2752                 {
2753                     tag: 'a',
2754                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2755                 }
2756             ]
2757         }
2758         
2759         return cfg;
2760     }
2761    
2762 });
2763
2764  /*
2765  * - LGPL
2766  *
2767  * table
2768  * 
2769  */
2770
2771 /**
2772  * @class Roo.bootstrap.Table
2773  * @extends Roo.bootstrap.Component
2774  * Bootstrap Table class
2775  * @cfg {String} cls table class
2776  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777  * @cfg {String} bgcolor Specifies the background color for a table
2778  * @cfg {Number} border Specifies whether the table cells should have borders or not
2779  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780  * @cfg {Number} cellspacing Specifies the space between cells
2781  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783  * @cfg {String} sortable Specifies that the table should be sortable
2784  * @cfg {String} summary Specifies a summary of the content of a table
2785  * @cfg {Number} width Specifies the width of a table
2786  * 
2787  * @cfg {boolean} striped Should the rows be alternative striped
2788  * @cfg {boolean} bordered Add borders to the table
2789  * @cfg {boolean} hover Add hover highlighting
2790  * @cfg {boolean} condensed Format condensed
2791  * @cfg {boolean} responsive Format condensed
2792  *
2793  
2794  
2795  * 
2796  * @constructor
2797  * Create a new Table
2798  * @param {Object} config The config object
2799  */
2800
2801 Roo.bootstrap.Table = function(config){
2802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2803     
2804     if (this.sm) {
2805         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806         this.sm = this.selModel;
2807         this.sm.xmodule = this.xmodule || false;
2808     }
2809     if (this.cm && typeof(this.cm.config) == 'undefined') {
2810         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811         this.cm = this.colModel;
2812         this.cm.xmodule = this.xmodule || false;
2813     }
2814     if (this.store) {
2815         this.store= Roo.factory(this.store, Roo.data);
2816         this.ds = this.store;
2817         this.ds.xmodule = this.xmodule || false;
2818          
2819     }
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2823     
2824     cls: false,
2825     align: false,
2826     bgcolor: false,
2827     border: false,
2828     cellpadding: false,
2829     cellspacing: false,
2830     frame: false,
2831     rules: false,
2832     sortable: false,
2833     summary: false,
2834     width: false,
2835     striped : false,
2836     bordered: false,
2837     hover:  false,
2838     condensed : false,
2839     responsive : false,
2840     sm : false,
2841     cm : false,
2842     store : false,
2843     
2844     getAutoCreate : function(){
2845         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2846         
2847         cfg = {
2848             tag: 'table',
2849             cls : 'table',
2850             cn : []
2851         }
2852             
2853         if (this.striped) {
2854             cfg.cls += ' table-striped';
2855         }
2856         if (this.hover) {
2857             cfg.cls += ' table-hover';
2858         }
2859         if (this.bordered) {
2860             cfg.cls += ' table-bordered';
2861         }
2862         if (this.condensed) {
2863             cfg.cls += ' table-condensed';
2864         }
2865         if (this.responsive) {
2866             cfg.cls += ' table-responsive';
2867         }
2868         
2869           
2870         
2871         
2872         if (this.cls) {
2873             cfg.cls+=  ' ' +this.cls;
2874         }
2875         
2876         // this lot should be simplifed...
2877         
2878         if (this.align) {
2879             cfg.align=this.align;
2880         }
2881         if (this.bgcolor) {
2882             cfg.bgcolor=this.bgcolor;
2883         }
2884         if (this.border) {
2885             cfg.border=this.border;
2886         }
2887         if (this.cellpadding) {
2888             cfg.cellpadding=this.cellpadding;
2889         }
2890         if (this.cellspacing) {
2891             cfg.cellspacing=this.cellspacing;
2892         }
2893         if (this.frame) {
2894             cfg.frame=this.frame;
2895         }
2896         if (this.rules) {
2897             cfg.rules=this.rules;
2898         }
2899         if (this.sortable) {
2900             cfg.sortable=this.sortable;
2901         }
2902         if (this.summary) {
2903             cfg.summary=this.summary;
2904         }
2905         if (this.width) {
2906             cfg.width=this.width;
2907         }
2908         
2909         if(this.store || this.cm){
2910             cfg.cn.push(this.renderHeader());
2911             cfg.cn.push(this.renderBody());
2912             cfg.cn.push(this.renderFooter());
2913             
2914             cfg.cls+=  ' TableGrid';
2915         }
2916         
2917         return cfg;
2918     },
2919 //    
2920 //    initTableGrid : function()
2921 //    {
2922 //        var cfg = {};
2923 //        
2924 //        var header = {
2925 //            tag: 'thead',
2926 //            cn : []
2927 //        };
2928 //        
2929 //        var cm = this.cm;
2930 //        
2931 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2932 //            header.cn.push({
2933 //                tag: 'th',
2934 //                html: cm.getColumnHeader(i)
2935 //            })
2936 //        }
2937 //        
2938 //        cfg.push(header);
2939 //        
2940 //        return cfg;
2941 //        
2942 //        
2943 //    },
2944     
2945     initEvents : function()
2946     {   
2947         if(!this.store || !this.cm){
2948             return;
2949         }
2950         
2951         Roo.log('initEvents with ds!!!!');
2952         
2953         var _this = this;
2954         
2955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956             e.on('click', _this.sort, _this);
2957         });
2958 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 //        this.maskEl.enableDisplayMode("block");
2960 //        this.maskEl.show();
2961         
2962         this.store.on('load', this.onLoad, this);
2963         this.store.on('beforeload', this.onBeforeLoad, this);
2964         
2965         this.store.load();
2966         
2967         
2968         
2969     },
2970     
2971     sort : function(e,el)
2972     {
2973         var col = Roo.get(el)
2974         var sort = col.attr('sort');
2975         var dir = 'ASC';
2976         
2977         if(col.hasClass('glyphicon-arrow-up')){
2978             dir = 'DESC';
2979         }
2980         
2981         this.store.sortInfo = {field : sort, direction : dir};
2982         
2983         this.store.load();
2984     },
2985     
2986     renderHeader : function()
2987     {
2988         var header = {
2989             tag: 'thead',
2990             cn : []
2991         };
2992         
2993         var cm = this.cm;
2994         
2995         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2996             
2997             var config = cm.config[i];
2998             
2999             var c = {
3000                 tag: 'th',
3001                 html: cm.getColumnHeader(i)
3002             };
3003             
3004             if(typeof(config.dataIndex) != 'undefined'){
3005                 c.sort = config.dataIndex;
3006             }
3007             
3008             if(typeof(config.sortable) != 'undefined' && config.sortable){
3009                 c.cls = 'sortable';
3010             }
3011             
3012             header.cn.push(c)
3013         }
3014         
3015         return header;
3016     },
3017     
3018     renderBody : function()
3019     {
3020         var body = {
3021             tag: 'tbody',
3022             cn : []
3023         };
3024         
3025         return body;
3026     },
3027     
3028     renderFooter : function()
3029     {
3030         var footer = {
3031             tag: 'tfoot',
3032             cn : []
3033         };
3034         
3035         return footer;
3036     },
3037     
3038     onLoad : function()
3039     {
3040         Roo.log('ds onload');
3041         
3042         var cm = this.cm;
3043         
3044         var tbody = this.el.select('tbody', true).first();
3045         
3046         var renders = [];
3047         
3048         if(this.store.getCount() > 0){
3049             this.store.data.each(function(d){
3050                 var row = {
3051                     tag : 'tr',
3052                     cn : []
3053                 };
3054                 
3055                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3056                     var renderer = cm.getRenderer(i);
3057                     var config = cm.config[i];
3058                     var value = '';
3059                     var id = Roo.id();
3060                     
3061                     if(typeof(renderer) !== 'undefined'){
3062                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
3063                     }
3064                     
3065                     if(typeof(value) === 'object'){
3066                         renders.push({
3067                             id : id,
3068                             cfg : value 
3069                         })
3070                     }
3071                     
3072                     var td = {
3073                         tag: 'td',
3074                         id: id,
3075                         html: (typeof(value) === 'object') ? '' : value
3076                     };
3077                     
3078                     if(typeof(config.width) != 'undefined'){
3079                         td.width = config.width;
3080                     }
3081                     
3082                     row.cn.push(td);
3083                    
3084                 }
3085                 
3086                 tbody.createChild(row);
3087                 
3088             });
3089         }
3090         
3091         
3092         if(renders.length){
3093             var _this = this;
3094             Roo.each(renders, function(r){
3095                 _this.renderColumn(r);
3096             })
3097         }
3098 //        
3099 //        if(this.loadMask){
3100 //            this.maskEl.hide();
3101 //        }
3102     },
3103     
3104     onBeforeLoad : function()
3105     {
3106         Roo.log('ds onBeforeLoad');
3107         
3108         this.clear();
3109         
3110 //        if(this.loadMask){
3111 //            this.maskEl.show();
3112 //        }
3113     },
3114     
3115     clear : function()
3116     {
3117         this.el.select('tbody', true).first().dom.innerHTML = '';
3118     },
3119     
3120     getSelectionModel : function(){
3121         if(!this.selModel){
3122             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3123         }
3124         return this.selModel;
3125     },
3126     
3127     renderColumn : function(r)
3128     {
3129         var _this = this;
3130         r.cfg.render(Roo.get(r.id));
3131         
3132         if(r.cfg.cn){
3133             Roo.each(r.cfg.cn, function(c){
3134                 var child = {
3135                     id: r.id,
3136                     cfg: c
3137                 }
3138                 _this.renderColumn(child);
3139             })
3140         }
3141     }
3142    
3143 });
3144
3145  
3146
3147  /*
3148  * - LGPL
3149  *
3150  * table cell
3151  * 
3152  */
3153
3154 /**
3155  * @class Roo.bootstrap.TableCell
3156  * @extends Roo.bootstrap.Component
3157  * Bootstrap TableCell class
3158  * @cfg {String} html cell contain text
3159  * @cfg {String} cls cell class
3160  * @cfg {String} tag cell tag (td|th) default td
3161  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3162  * @cfg {String} align Aligns the content in a cell
3163  * @cfg {String} axis Categorizes cells
3164  * @cfg {String} bgcolor Specifies the background color of a cell
3165  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3166  * @cfg {Number} colspan Specifies the number of columns a cell should span
3167  * @cfg {String} headers Specifies one or more header cells a cell is related to
3168  * @cfg {Number} height Sets the height of a cell
3169  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3170  * @cfg {Number} rowspan Sets the number of rows a cell should span
3171  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3172  * @cfg {String} valign Vertical aligns the content in a cell
3173  * @cfg {Number} width Specifies the width of a cell
3174  * 
3175  * @constructor
3176  * Create a new TableCell
3177  * @param {Object} config The config object
3178  */
3179
3180 Roo.bootstrap.TableCell = function(config){
3181     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3182 };
3183
3184 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3185     
3186     html: false,
3187     cls: false,
3188     tag: false,
3189     abbr: false,
3190     align: false,
3191     axis: false,
3192     bgcolor: false,
3193     charoff: false,
3194     colspan: false,
3195     headers: false,
3196     height: false,
3197     nowrap: false,
3198     rowspan: false,
3199     scope: false,
3200     valign: false,
3201     width: false,
3202     
3203     
3204     getAutoCreate : function(){
3205         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3206         
3207         cfg = {
3208             tag: 'td'
3209         }
3210         
3211         if(this.tag){
3212             cfg.tag = this.tag;
3213         }
3214         
3215         if (this.html) {
3216             cfg.html=this.html
3217         }
3218         if (this.cls) {
3219             cfg.cls=this.cls
3220         }
3221         if (this.abbr) {
3222             cfg.abbr=this.abbr
3223         }
3224         if (this.align) {
3225             cfg.align=this.align
3226         }
3227         if (this.axis) {
3228             cfg.axis=this.axis
3229         }
3230         if (this.bgcolor) {
3231             cfg.bgcolor=this.bgcolor
3232         }
3233         if (this.charoff) {
3234             cfg.charoff=this.charoff
3235         }
3236         if (this.colspan) {
3237             cfg.colspan=this.colspan
3238         }
3239         if (this.headers) {
3240             cfg.headers=this.headers
3241         }
3242         if (this.height) {
3243             cfg.height=this.height
3244         }
3245         if (this.nowrap) {
3246             cfg.nowrap=this.nowrap
3247         }
3248         if (this.rowspan) {
3249             cfg.rowspan=this.rowspan
3250         }
3251         if (this.scope) {
3252             cfg.scope=this.scope
3253         }
3254         if (this.valign) {
3255             cfg.valign=this.valign
3256         }
3257         if (this.width) {
3258             cfg.width=this.width
3259         }
3260         
3261         
3262         return cfg;
3263     }
3264    
3265 });
3266
3267  
3268
3269  /*
3270  * - LGPL
3271  *
3272  * table row
3273  * 
3274  */
3275
3276 /**
3277  * @class Roo.bootstrap.TableRow
3278  * @extends Roo.bootstrap.Component
3279  * Bootstrap TableRow class
3280  * @cfg {String} cls row class
3281  * @cfg {String} align Aligns the content in a table row
3282  * @cfg {String} bgcolor Specifies a background color for a table row
3283  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3284  * @cfg {String} valign Vertical aligns the content in a table row
3285  * 
3286  * @constructor
3287  * Create a new TableRow
3288  * @param {Object} config The config object
3289  */
3290
3291 Roo.bootstrap.TableRow = function(config){
3292     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3293 };
3294
3295 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3296     
3297     cls: false,
3298     align: false,
3299     bgcolor: false,
3300     charoff: false,
3301     valign: false,
3302     
3303     getAutoCreate : function(){
3304         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3305         
3306         cfg = {
3307             tag: 'tr'
3308         }
3309             
3310         if(this.cls){
3311             cfg.cls = this.cls;
3312         }
3313         if(this.align){
3314             cfg.align = this.align;
3315         }
3316         if(this.bgcolor){
3317             cfg.bgcolor = this.bgcolor;
3318         }
3319         if(this.charoff){
3320             cfg.charoff = this.charoff;
3321         }
3322         if(this.valign){
3323             cfg.valign = this.valign;
3324         }
3325         
3326         return cfg;
3327     }
3328    
3329 });
3330
3331  
3332
3333  /*
3334  * - LGPL
3335  *
3336  * table body
3337  * 
3338  */
3339
3340 /**
3341  * @class Roo.bootstrap.TableBody
3342  * @extends Roo.bootstrap.Component
3343  * Bootstrap TableBody class
3344  * @cfg {String} cls element class
3345  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3346  * @cfg {String} align Aligns the content inside the element
3347  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3348  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3349  * 
3350  * @constructor
3351  * Create a new TableBody
3352  * @param {Object} config The config object
3353  */
3354
3355 Roo.bootstrap.TableBody = function(config){
3356     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3357 };
3358
3359 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3360     
3361     cls: false,
3362     tag: false,
3363     align: false,
3364     charoff: false,
3365     valign: false,
3366     
3367     getAutoCreate : function(){
3368         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3369         
3370         cfg = {
3371             tag: 'tbody'
3372         }
3373             
3374         if (this.cls) {
3375             cfg.cls=this.cls
3376         }
3377         if(this.tag){
3378             cfg.tag = this.tag;
3379         }
3380         
3381         if(this.align){
3382             cfg.align = this.align;
3383         }
3384         if(this.charoff){
3385             cfg.charoff = this.charoff;
3386         }
3387         if(this.valign){
3388             cfg.valign = this.valign;
3389         }
3390         
3391         return cfg;
3392     }
3393     
3394     
3395 //    initEvents : function()
3396 //    {
3397 //        
3398 //        if(!this.store){
3399 //            return;
3400 //        }
3401 //        
3402 //        this.store = Roo.factory(this.store, Roo.data);
3403 //        this.store.on('load', this.onLoad, this);
3404 //        
3405 //        this.store.load();
3406 //        
3407 //    },
3408 //    
3409 //    onLoad: function () 
3410 //    {   
3411 //        this.fireEvent('load', this);
3412 //    }
3413 //    
3414 //   
3415 });
3416
3417  
3418
3419  /*
3420  * Based on:
3421  * Ext JS Library 1.1.1
3422  * Copyright(c) 2006-2007, Ext JS, LLC.
3423  *
3424  * Originally Released Under LGPL - original licence link has changed is not relivant.
3425  *
3426  * Fork - LGPL
3427  * <script type="text/javascript">
3428  */
3429
3430 // as we use this in bootstrap.
3431 Roo.namespace('Roo.form');
3432  /**
3433  * @class Roo.form.Action
3434  * Internal Class used to handle form actions
3435  * @constructor
3436  * @param {Roo.form.BasicForm} el The form element or its id
3437  * @param {Object} config Configuration options
3438  */
3439
3440  
3441  
3442 // define the action interface
3443 Roo.form.Action = function(form, options){
3444     this.form = form;
3445     this.options = options || {};
3446 };
3447 /**
3448  * Client Validation Failed
3449  * @const 
3450  */
3451 Roo.form.Action.CLIENT_INVALID = 'client';
3452 /**
3453  * Server Validation Failed
3454  * @const 
3455  */
3456 Roo.form.Action.SERVER_INVALID = 'server';
3457  /**
3458  * Connect to Server Failed
3459  * @const 
3460  */
3461 Roo.form.Action.CONNECT_FAILURE = 'connect';
3462 /**
3463  * Reading Data from Server Failed
3464  * @const 
3465  */
3466 Roo.form.Action.LOAD_FAILURE = 'load';
3467
3468 Roo.form.Action.prototype = {
3469     type : 'default',
3470     failureType : undefined,
3471     response : undefined,
3472     result : undefined,
3473
3474     // interface method
3475     run : function(options){
3476
3477     },
3478
3479     // interface method
3480     success : function(response){
3481
3482     },
3483
3484     // interface method
3485     handleResponse : function(response){
3486
3487     },
3488
3489     // default connection failure
3490     failure : function(response){
3491         
3492         this.response = response;
3493         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3494         this.form.afterAction(this, false);
3495     },
3496
3497     processResponse : function(response){
3498         this.response = response;
3499         if(!response.responseText){
3500             return true;
3501         }
3502         this.result = this.handleResponse(response);
3503         return this.result;
3504     },
3505
3506     // utility functions used internally
3507     getUrl : function(appendParams){
3508         var url = this.options.url || this.form.url || this.form.el.dom.action;
3509         if(appendParams){
3510             var p = this.getParams();
3511             if(p){
3512                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3513             }
3514         }
3515         return url;
3516     },
3517
3518     getMethod : function(){
3519         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3520     },
3521
3522     getParams : function(){
3523         var bp = this.form.baseParams;
3524         var p = this.options.params;
3525         if(p){
3526             if(typeof p == "object"){
3527                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3528             }else if(typeof p == 'string' && bp){
3529                 p += '&' + Roo.urlEncode(bp);
3530             }
3531         }else if(bp){
3532             p = Roo.urlEncode(bp);
3533         }
3534         return p;
3535     },
3536
3537     createCallback : function(){
3538         return {
3539             success: this.success,
3540             failure: this.failure,
3541             scope: this,
3542             timeout: (this.form.timeout*1000),
3543             upload: this.form.fileUpload ? this.success : undefined
3544         };
3545     }
3546 };
3547
3548 Roo.form.Action.Submit = function(form, options){
3549     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3550 };
3551
3552 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3553     type : 'submit',
3554
3555     haveProgress : false,
3556     uploadComplete : false,
3557     
3558     // uploadProgress indicator.
3559     uploadProgress : function()
3560     {
3561         if (!this.form.progressUrl) {
3562             return;
3563         }
3564         
3565         if (!this.haveProgress) {
3566             Roo.MessageBox.progress("Uploading", "Uploading");
3567         }
3568         if (this.uploadComplete) {
3569            Roo.MessageBox.hide();
3570            return;
3571         }
3572         
3573         this.haveProgress = true;
3574    
3575         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3576         
3577         var c = new Roo.data.Connection();
3578         c.request({
3579             url : this.form.progressUrl,
3580             params: {
3581                 id : uid
3582             },
3583             method: 'GET',
3584             success : function(req){
3585                //console.log(data);
3586                 var rdata = false;
3587                 var edata;
3588                 try  {
3589                    rdata = Roo.decode(req.responseText)
3590                 } catch (e) {
3591                     Roo.log("Invalid data from server..");
3592                     Roo.log(edata);
3593                     return;
3594                 }
3595                 if (!rdata || !rdata.success) {
3596                     Roo.log(rdata);
3597                     Roo.MessageBox.alert(Roo.encode(rdata));
3598                     return;
3599                 }
3600                 var data = rdata.data;
3601                 
3602                 if (this.uploadComplete) {
3603                    Roo.MessageBox.hide();
3604                    return;
3605                 }
3606                    
3607                 if (data){
3608                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3609                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3610                     );
3611                 }
3612                 this.uploadProgress.defer(2000,this);
3613             },
3614        
3615             failure: function(data) {
3616                 Roo.log('progress url failed ');
3617                 Roo.log(data);
3618             },
3619             scope : this
3620         });
3621            
3622     },
3623     
3624     
3625     run : function()
3626     {
3627         // run get Values on the form, so it syncs any secondary forms.
3628         this.form.getValues();
3629         
3630         var o = this.options;
3631         var method = this.getMethod();
3632         var isPost = method == 'POST';
3633         if(o.clientValidation === false || this.form.isValid()){
3634             
3635             if (this.form.progressUrl) {
3636                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3637                     (new Date() * 1) + '' + Math.random());
3638                     
3639             } 
3640             
3641             
3642             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3643                 form:this.form.el.dom,
3644                 url:this.getUrl(!isPost),
3645                 method: method,
3646                 params:isPost ? this.getParams() : null,
3647                 isUpload: this.form.fileUpload
3648             }));
3649             
3650             this.uploadProgress();
3651
3652         }else if (o.clientValidation !== false){ // client validation failed
3653             this.failureType = Roo.form.Action.CLIENT_INVALID;
3654             this.form.afterAction(this, false);
3655         }
3656     },
3657
3658     success : function(response)
3659     {
3660         this.uploadComplete= true;
3661         if (this.haveProgress) {
3662             Roo.MessageBox.hide();
3663         }
3664         
3665         
3666         var result = this.processResponse(response);
3667         if(result === true || result.success){
3668             this.form.afterAction(this, true);
3669             return;
3670         }
3671         if(result.errors){
3672             this.form.markInvalid(result.errors);
3673             this.failureType = Roo.form.Action.SERVER_INVALID;
3674         }
3675         this.form.afterAction(this, false);
3676     },
3677     failure : function(response)
3678     {
3679         this.uploadComplete= true;
3680         if (this.haveProgress) {
3681             Roo.MessageBox.hide();
3682         }
3683         
3684         this.response = response;
3685         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3686         this.form.afterAction(this, false);
3687     },
3688     
3689     handleResponse : function(response){
3690         if(this.form.errorReader){
3691             var rs = this.form.errorReader.read(response);
3692             var errors = [];
3693             if(rs.records){
3694                 for(var i = 0, len = rs.records.length; i < len; i++) {
3695                     var r = rs.records[i];
3696                     errors[i] = r.data;
3697                 }
3698             }
3699             if(errors.length < 1){
3700                 errors = null;
3701             }
3702             return {
3703                 success : rs.success,
3704                 errors : errors
3705             };
3706         }
3707         var ret = false;
3708         try {
3709             ret = Roo.decode(response.responseText);
3710         } catch (e) {
3711             ret = {
3712                 success: false,
3713                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3714                 errors : []
3715             };
3716         }
3717         return ret;
3718         
3719     }
3720 });
3721
3722
3723 Roo.form.Action.Load = function(form, options){
3724     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3725     this.reader = this.form.reader;
3726 };
3727
3728 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3729     type : 'load',
3730
3731     run : function(){
3732         
3733         Roo.Ajax.request(Roo.apply(
3734                 this.createCallback(), {
3735                     method:this.getMethod(),
3736                     url:this.getUrl(false),
3737                     params:this.getParams()
3738         }));
3739     },
3740
3741     success : function(response){
3742         
3743         var result = this.processResponse(response);
3744         if(result === true || !result.success || !result.data){
3745             this.failureType = Roo.form.Action.LOAD_FAILURE;
3746             this.form.afterAction(this, false);
3747             return;
3748         }
3749         this.form.clearInvalid();
3750         this.form.setValues(result.data);
3751         this.form.afterAction(this, true);
3752     },
3753
3754     handleResponse : function(response){
3755         if(this.form.reader){
3756             var rs = this.form.reader.read(response);
3757             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3758             return {
3759                 success : rs.success,
3760                 data : data
3761             };
3762         }
3763         return Roo.decode(response.responseText);
3764     }
3765 });
3766
3767 Roo.form.Action.ACTION_TYPES = {
3768     'load' : Roo.form.Action.Load,
3769     'submit' : Roo.form.Action.Submit
3770 };/*
3771  * - LGPL
3772  *
3773  * form
3774  * 
3775  */
3776
3777 /**
3778  * @class Roo.bootstrap.Form
3779  * @extends Roo.bootstrap.Component
3780  * Bootstrap Form class
3781  * @cfg {String} method  GET | POST (default POST)
3782  * @cfg {String} labelAlign top | left (default top)
3783   * @cfg {String} align left  | right - for navbars
3784
3785  * 
3786  * @constructor
3787  * Create a new Form
3788  * @param {Object} config The config object
3789  */
3790
3791
3792 Roo.bootstrap.Form = function(config){
3793     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3794     this.addEvents({
3795         /**
3796          * @event clientvalidation
3797          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3798          * @param {Form} this
3799          * @param {Boolean} valid true if the form has passed client-side validation
3800          */
3801         clientvalidation: true,
3802         /**
3803          * @event beforeaction
3804          * Fires before any action is performed. Return false to cancel the action.
3805          * @param {Form} this
3806          * @param {Action} action The action to be performed
3807          */
3808         beforeaction: true,
3809         /**
3810          * @event actionfailed
3811          * Fires when an action fails.
3812          * @param {Form} this
3813          * @param {Action} action The action that failed
3814          */
3815         actionfailed : true,
3816         /**
3817          * @event actioncomplete
3818          * Fires when an action is completed.
3819          * @param {Form} this
3820          * @param {Action} action The action that completed
3821          */
3822         actioncomplete : true
3823     });
3824     
3825 };
3826
3827 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3828       
3829      /**
3830      * @cfg {String} method
3831      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3832      */
3833     method : 'POST',
3834     /**
3835      * @cfg {String} url
3836      * The URL to use for form actions if one isn't supplied in the action options.
3837      */
3838     /**
3839      * @cfg {Boolean} fileUpload
3840      * Set to true if this form is a file upload.
3841      */
3842      
3843     /**
3844      * @cfg {Object} baseParams
3845      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3846      */
3847       
3848     /**
3849      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3850      */
3851     timeout: 30,
3852     /**
3853      * @cfg {Sting} align (left|right) for navbar forms
3854      */
3855     align : 'left',
3856
3857     // private
3858     activeAction : null,
3859  
3860     /**
3861      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3862      * element by passing it or its id or mask the form itself by passing in true.
3863      * @type Mixed
3864      */
3865     waitMsgTarget : false,
3866     
3867      
3868     
3869     /**
3870      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3871      * element by passing it or its id or mask the form itself by passing in true.
3872      * @type Mixed
3873      */
3874     
3875     getAutoCreate : function(){
3876         
3877         var cfg = {
3878             tag: 'form',
3879             method : this.method || 'POST',
3880             id : this.id || Roo.id(),
3881             cls : ''
3882         }
3883         if (this.parent().xtype.match(/^Nav/)) {
3884             cfg.cls = 'navbar-form navbar-' + this.align;
3885             
3886         }
3887         
3888         if (this.labelAlign == 'left' ) {
3889             cfg.cls += ' form-horizontal';
3890         }
3891         
3892         
3893         return cfg;
3894     },
3895     initEvents : function()
3896     {
3897         this.el.on('submit', this.onSubmit, this);
3898         
3899         
3900     },
3901     // private
3902     onSubmit : function(e){
3903         e.stopEvent();
3904     },
3905     
3906      /**
3907      * Returns true if client-side validation on the form is successful.
3908      * @return Boolean
3909      */
3910     isValid : function(){
3911         var items = this.getItems();
3912         var valid = true;
3913         items.each(function(f){
3914            if(!f.validate()){
3915                valid = false;
3916                
3917            }
3918         });
3919         return valid;
3920     },
3921     /**
3922      * Returns true if any fields in this form have changed since their original load.
3923      * @return Boolean
3924      */
3925     isDirty : function(){
3926         var dirty = false;
3927         var items = this.getItems();
3928         items.each(function(f){
3929            if(f.isDirty()){
3930                dirty = true;
3931                return false;
3932            }
3933            return true;
3934         });
3935         return dirty;
3936     },
3937      /**
3938      * Performs a predefined action (submit or load) or custom actions you define on this form.
3939      * @param {String} actionName The name of the action type
3940      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3941      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3942      * accept other config options):
3943      * <pre>
3944 Property          Type             Description
3945 ----------------  ---------------  ----------------------------------------------------------------------------------
3946 url               String           The url for the action (defaults to the form's url)
3947 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3948 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3949 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3950                                    validate the form on the client (defaults to false)
3951      * </pre>
3952      * @return {BasicForm} this
3953      */
3954     doAction : function(action, options){
3955         if(typeof action == 'string'){
3956             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3957         }
3958         if(this.fireEvent('beforeaction', this, action) !== false){
3959             this.beforeAction(action);
3960             action.run.defer(100, action);
3961         }
3962         return this;
3963     },
3964     
3965     // private
3966     beforeAction : function(action){
3967         var o = action.options;
3968         
3969         // not really supported yet.. ??
3970         
3971         //if(this.waitMsgTarget === true){
3972             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3973         //}else if(this.waitMsgTarget){
3974         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3975         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3976         //}else {
3977         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3978        // }
3979          
3980     },
3981
3982     // private
3983     afterAction : function(action, success){
3984         this.activeAction = null;
3985         var o = action.options;
3986         
3987         //if(this.waitMsgTarget === true){
3988             this.el.unmask();
3989         //}else if(this.waitMsgTarget){
3990         //    this.waitMsgTarget.unmask();
3991         //}else{
3992         //    Roo.MessageBox.updateProgress(1);
3993         //    Roo.MessageBox.hide();
3994        // }
3995         // 
3996         if(success){
3997             if(o.reset){
3998                 this.reset();
3999             }
4000             Roo.callback(o.success, o.scope, [this, action]);
4001             this.fireEvent('actioncomplete', this, action);
4002             
4003         }else{
4004             
4005             // failure condition..
4006             // we have a scenario where updates need confirming.
4007             // eg. if a locking scenario exists..
4008             // we look for { errors : { needs_confirm : true }} in the response.
4009             if (
4010                 (typeof(action.result) != 'undefined')  &&
4011                 (typeof(action.result.errors) != 'undefined')  &&
4012                 (typeof(action.result.errors.needs_confirm) != 'undefined')
4013            ){
4014                 var _t = this;
4015                 Roo.log("not supported yet");
4016                  /*
4017                 
4018                 Roo.MessageBox.confirm(
4019                     "Change requires confirmation",
4020                     action.result.errorMsg,
4021                     function(r) {
4022                         if (r != 'yes') {
4023                             return;
4024                         }
4025                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
4026                     }
4027                     
4028                 );
4029                 */
4030                 
4031                 
4032                 return;
4033             }
4034             
4035             Roo.callback(o.failure, o.scope, [this, action]);
4036             // show an error message if no failed handler is set..
4037             if (!this.hasListener('actionfailed')) {
4038                 Roo.log("need to add dialog support");
4039                 /*
4040                 Roo.MessageBox.alert("Error",
4041                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4042                         action.result.errorMsg :
4043                         "Saving Failed, please check your entries or try again"
4044                 );
4045                 */
4046             }
4047             
4048             this.fireEvent('actionfailed', this, action);
4049         }
4050         
4051     },
4052     /**
4053      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4054      * @param {String} id The value to search for
4055      * @return Field
4056      */
4057     findField : function(id){
4058         var items = this.getItems();
4059         var field = items.get(id);
4060         if(!field){
4061              items.each(function(f){
4062                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4063                     field = f;
4064                     return false;
4065                 }
4066                 return true;
4067             });
4068         }
4069         return field || null;
4070     },
4071      /**
4072      * Mark fields in this form invalid in bulk.
4073      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4074      * @return {BasicForm} this
4075      */
4076     markInvalid : function(errors){
4077         if(errors instanceof Array){
4078             for(var i = 0, len = errors.length; i < len; i++){
4079                 var fieldError = errors[i];
4080                 var f = this.findField(fieldError.id);
4081                 if(f){
4082                     f.markInvalid(fieldError.msg);
4083                 }
4084             }
4085         }else{
4086             var field, id;
4087             for(id in errors){
4088                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4089                     field.markInvalid(errors[id]);
4090                 }
4091             }
4092         }
4093         //Roo.each(this.childForms || [], function (f) {
4094         //    f.markInvalid(errors);
4095         //});
4096         
4097         return this;
4098     },
4099
4100     /**
4101      * Set values for fields in this form in bulk.
4102      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4103      * @return {BasicForm} this
4104      */
4105     setValues : function(values){
4106         if(values instanceof Array){ // array of objects
4107             for(var i = 0, len = values.length; i < len; i++){
4108                 var v = values[i];
4109                 var f = this.findField(v.id);
4110                 if(f){
4111                     f.setValue(v.value);
4112                     if(this.trackResetOnLoad){
4113                         f.originalValue = f.getValue();
4114                     }
4115                 }
4116             }
4117         }else{ // object hash
4118             var field, id;
4119             for(id in values){
4120                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4121                     
4122                     if (field.setFromData && 
4123                         field.valueField && 
4124                         field.displayField &&
4125                         // combos' with local stores can 
4126                         // be queried via setValue()
4127                         // to set their value..
4128                         (field.store && !field.store.isLocal)
4129                         ) {
4130                         // it's a combo
4131                         var sd = { };
4132                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4133                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4134                         field.setFromData(sd);
4135                         
4136                     } else {
4137                         field.setValue(values[id]);
4138                     }
4139                     
4140                     
4141                     if(this.trackResetOnLoad){
4142                         field.originalValue = field.getValue();
4143                     }
4144                 }
4145             }
4146         }
4147          
4148         //Roo.each(this.childForms || [], function (f) {
4149         //    f.setValues(values);
4150         //});
4151                 
4152         return this;
4153     },
4154
4155     /**
4156      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4157      * they are returned as an array.
4158      * @param {Boolean} asString
4159      * @return {Object}
4160      */
4161     getValues : function(asString){
4162         //if (this.childForms) {
4163             // copy values from the child forms
4164         //    Roo.each(this.childForms, function (f) {
4165         //        this.setValues(f.getValues());
4166         //    }, this);
4167         //}
4168         
4169         
4170         
4171         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4172         if(asString === true){
4173             return fs;
4174         }
4175         return Roo.urlDecode(fs);
4176     },
4177     
4178     /**
4179      * Returns the fields in this form as an object with key/value pairs. 
4180      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4181      * @return {Object}
4182      */
4183     getFieldValues : function(with_hidden)
4184     {
4185         var items = this.getItems();
4186         var ret = {};
4187         items.each(function(f){
4188             if (!f.getName()) {
4189                 return;
4190             }
4191             var v = f.getValue();
4192             if (f.inputType =='radio') {
4193                 if (typeof(ret[f.getName()]) == 'undefined') {
4194                     ret[f.getName()] = ''; // empty..
4195                 }
4196                 
4197                 if (!f.el.dom.checked) {
4198                     return;
4199                     
4200                 }
4201                 v = f.el.dom.value;
4202                 
4203             }
4204             
4205             // not sure if this supported any more..
4206             if ((typeof(v) == 'object') && f.getRawValue) {
4207                 v = f.getRawValue() ; // dates..
4208             }
4209             // combo boxes where name != hiddenName...
4210             if (f.name != f.getName()) {
4211                 ret[f.name] = f.getRawValue();
4212             }
4213             ret[f.getName()] = v;
4214         });
4215         
4216         return ret;
4217     },
4218
4219     /**
4220      * Clears all invalid messages in this form.
4221      * @return {BasicForm} this
4222      */
4223     clearInvalid : function(){
4224         var items = this.getItems();
4225         
4226         items.each(function(f){
4227            f.clearInvalid();
4228         });
4229         
4230         
4231         
4232         return this;
4233     },
4234
4235     /**
4236      * Resets this form.
4237      * @return {BasicForm} this
4238      */
4239     reset : function(){
4240         var items = this.getItems();
4241         items.each(function(f){
4242             f.reset();
4243         });
4244         
4245         Roo.each(this.childForms || [], function (f) {
4246             f.reset();
4247         });
4248        
4249         
4250         return this;
4251     },
4252     getItems : function()
4253     {
4254         var r=new Roo.util.MixedCollection(false, function(o){
4255             return o.id || (o.id = Roo.id());
4256         });
4257         var iter = function(el) {
4258             if (el.inputEl) {
4259                 r.add(el);
4260             }
4261             if (!el.items) {
4262                 return;
4263             }
4264             Roo.each(el.items,function(e) {
4265                 iter(e);
4266             });
4267             
4268             
4269         };
4270         iter(this);
4271         return r;
4272         
4273         
4274         
4275         
4276     }
4277     
4278 });
4279
4280  
4281 /*
4282  * Based on:
4283  * Ext JS Library 1.1.1
4284  * Copyright(c) 2006-2007, Ext JS, LLC.
4285  *
4286  * Originally Released Under LGPL - original licence link has changed is not relivant.
4287  *
4288  * Fork - LGPL
4289  * <script type="text/javascript">
4290  */
4291 /**
4292  * @class Roo.form.VTypes
4293  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4294  * @singleton
4295  */
4296 Roo.form.VTypes = function(){
4297     // closure these in so they are only created once.
4298     var alpha = /^[a-zA-Z_]+$/;
4299     var alphanum = /^[a-zA-Z0-9_]+$/;
4300     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4301     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4302
4303     // All these messages and functions are configurable
4304     return {
4305         /**
4306          * The function used to validate email addresses
4307          * @param {String} value The email address
4308          */
4309         'email' : function(v){
4310             return email.test(v);
4311         },
4312         /**
4313          * The error text to display when the email validation function returns false
4314          * @type String
4315          */
4316         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4317         /**
4318          * The keystroke filter mask to be applied on email input
4319          * @type RegExp
4320          */
4321         'emailMask' : /[a-z0-9_\.\-@]/i,
4322
4323         /**
4324          * The function used to validate URLs
4325          * @param {String} value The URL
4326          */
4327         'url' : function(v){
4328             return url.test(v);
4329         },
4330         /**
4331          * The error text to display when the url validation function returns false
4332          * @type String
4333          */
4334         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4335         
4336         /**
4337          * The function used to validate alpha values
4338          * @param {String} value The value
4339          */
4340         'alpha' : function(v){
4341             return alpha.test(v);
4342         },
4343         /**
4344          * The error text to display when the alpha validation function returns false
4345          * @type String
4346          */
4347         'alphaText' : 'This field should only contain letters and _',
4348         /**
4349          * The keystroke filter mask to be applied on alpha input
4350          * @type RegExp
4351          */
4352         'alphaMask' : /[a-z_]/i,
4353
4354         /**
4355          * The function used to validate alphanumeric values
4356          * @param {String} value The value
4357          */
4358         'alphanum' : function(v){
4359             return alphanum.test(v);
4360         },
4361         /**
4362          * The error text to display when the alphanumeric validation function returns false
4363          * @type String
4364          */
4365         'alphanumText' : 'This field should only contain letters, numbers and _',
4366         /**
4367          * The keystroke filter mask to be applied on alphanumeric input
4368          * @type RegExp
4369          */
4370         'alphanumMask' : /[a-z0-9_]/i
4371     };
4372 }();/*
4373  * - LGPL
4374  *
4375  * Input
4376  * 
4377  */
4378
4379 /**
4380  * @class Roo.bootstrap.Input
4381  * @extends Roo.bootstrap.Component
4382  * Bootstrap Input class
4383  * @cfg {Boolean} disabled is it disabled
4384  * @cfg {String} fieldLabel - the label associated
4385  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4386  * @cfg {String} name name of the input
4387  * @cfg {string} fieldLabel - the label associated
4388  * @cfg {string}  inputType - input / file submit ...
4389  * @cfg {string} placeholder - placeholder to put in text.
4390  * @cfg {string}  before - input group add on before
4391  * @cfg {string} after - input group add on after
4392  * @cfg {string} size - (lg|sm) or leave empty..
4393  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4394  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4395  * @cfg {Number} md colspan out of 12 for computer-sized screens
4396  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4397  * @cfg {string} value default value of the input
4398  * @cfg {Number} labelWidth set the width of label (0-12)
4399  * @cfg {String} labelAlign (top|left)
4400  * @cfg {Boolean} readOnly Specifies that the field should be read-only
4401  * 
4402  * 
4403  * @constructor
4404  * Create a new Input
4405  * @param {Object} config The config object
4406  */
4407
4408 Roo.bootstrap.Input = function(config){
4409     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4410    
4411         this.addEvents({
4412             /**
4413              * @event focus
4414              * Fires when this field receives input focus.
4415              * @param {Roo.form.Field} this
4416              */
4417             focus : true,
4418             /**
4419              * @event blur
4420              * Fires when this field loses input focus.
4421              * @param {Roo.form.Field} this
4422              */
4423             blur : true,
4424             /**
4425              * @event specialkey
4426              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4427              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4428              * @param {Roo.form.Field} this
4429              * @param {Roo.EventObject} e The event object
4430              */
4431             specialkey : true,
4432             /**
4433              * @event change
4434              * Fires just before the field blurs if the field value has changed.
4435              * @param {Roo.form.Field} this
4436              * @param {Mixed} newValue The new value
4437              * @param {Mixed} oldValue The original value
4438              */
4439             change : true,
4440             /**
4441              * @event invalid
4442              * Fires after the field has been marked as invalid.
4443              * @param {Roo.form.Field} this
4444              * @param {String} msg The validation message
4445              */
4446             invalid : true,
4447             /**
4448              * @event valid
4449              * Fires after the field has been validated with no errors.
4450              * @param {Roo.form.Field} this
4451              */
4452             valid : true,
4453              /**
4454              * @event keyup
4455              * Fires after the key up
4456              * @param {Roo.form.Field} this
4457              * @param {Roo.EventObject}  e The event Object
4458              */
4459             keyup : true
4460         });
4461 };
4462
4463 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4464      /**
4465      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4466       automatic validation (defaults to "keyup").
4467      */
4468     validationEvent : "keyup",
4469      /**
4470      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4471      */
4472     validateOnBlur : true,
4473     /**
4474      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4475      */
4476     validationDelay : 250,
4477      /**
4478      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4479      */
4480     focusClass : "x-form-focus",  // not needed???
4481     
4482        
4483     /**
4484      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4485      */
4486     invalidClass : "has-error",
4487     
4488     /**
4489      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4490      */
4491     selectOnFocus : false,
4492     
4493      /**
4494      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4495      */
4496     maskRe : null,
4497        /**
4498      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4499      */
4500     vtype : null,
4501     
4502       /**
4503      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4504      */
4505     disableKeyFilter : false,
4506     
4507        /**
4508      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4509      */
4510     disabled : false,
4511      /**
4512      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4513      */
4514     allowBlank : true,
4515     /**
4516      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4517      */
4518     blankText : "This field is required",
4519     
4520      /**
4521      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4522      */
4523     minLength : 0,
4524     /**
4525      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4526      */
4527     maxLength : Number.MAX_VALUE,
4528     /**
4529      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4530      */
4531     minLengthText : "The minimum length for this field is {0}",
4532     /**
4533      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4534      */
4535     maxLengthText : "The maximum length for this field is {0}",
4536   
4537     
4538     /**
4539      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4540      * If available, this function will be called only after the basic validators all return true, and will be passed the
4541      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4542      */
4543     validator : null,
4544     /**
4545      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4546      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4547      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4548      */
4549     regex : null,
4550     /**
4551      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4552      */
4553     regexText : "",
4554     
4555     
4556     
4557     fieldLabel : '',
4558     inputType : 'text',
4559     
4560     name : false,
4561     placeholder: false,
4562     before : false,
4563     after : false,
4564     size : false,
4565     // private
4566     hasFocus : false,
4567     preventMark: false,
4568     isFormField : true,
4569     value : '',
4570     labelWidth : 2,
4571     labelAlign : false,
4572     readOnly : false,
4573     
4574     parentLabelAlign : function()
4575     {
4576         var parent = this;
4577         while (parent.parent()) {
4578             parent = parent.parent();
4579             if (typeof(parent.labelAlign) !='undefined') {
4580                 return parent.labelAlign;
4581             }
4582         }
4583         return 'left';
4584         
4585     },
4586     
4587     getAutoCreate : function(){
4588         
4589         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4590         
4591         var id = Roo.id();
4592         
4593         var cfg = {};
4594         
4595         if(this.inputType != 'hidden'){
4596             cfg.cls = 'form-group' //input-group
4597         }
4598         
4599         var input =  {
4600             tag: 'input',
4601             id : id,
4602             type : this.inputType,
4603             value : this.value,
4604             cls : 'form-control',
4605             placeholder : this.placeholder || ''
4606             
4607         };
4608         
4609         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4610             input.maxLength = this.maxLength;
4611         }
4612         
4613         if (this.disabled) {
4614             input.disabled=true;
4615         }
4616         
4617         if (this.readOnly) {
4618             input.readonly=true;
4619         }
4620         
4621         if (this.name) {
4622             input.name = this.name;
4623         }
4624         if (this.size) {
4625             input.cls += ' input-' + this.size;
4626         }
4627         var settings=this;
4628         ['xs','sm','md','lg'].map(function(size){
4629             if (settings[size]) {
4630                 cfg.cls += ' col-' + size + '-' + settings[size];
4631             }
4632         });
4633         
4634         var inputblock = input;
4635         
4636         if (this.before || this.after) {
4637             
4638             inputblock = {
4639                 cls : 'input-group',
4640                 cn :  [] 
4641             };
4642             if (this.before) {
4643                 inputblock.cn.push({
4644                     tag :'span',
4645                     cls : 'input-group-addon',
4646                     html : this.before
4647                 });
4648             }
4649             inputblock.cn.push(input);
4650             if (this.after) {
4651                 inputblock.cn.push({
4652                     tag :'span',
4653                     cls : 'input-group-addon',
4654                     html : this.after
4655                 });
4656             }
4657             
4658         };
4659         
4660         if (align ==='left' && this.fieldLabel.length) {
4661                 Roo.log("left and has label");
4662                 cfg.cn = [
4663                     
4664                     {
4665                         tag: 'label',
4666                         'for' :  id,
4667                         cls : 'control-label col-sm-' + this.labelWidth,
4668                         html : this.fieldLabel
4669                         
4670                     },
4671                     {
4672                         cls : "col-sm-" + (12 - this.labelWidth), 
4673                         cn: [
4674                             inputblock
4675                         ]
4676                     }
4677                     
4678                 ];
4679         } else if ( this.fieldLabel.length) {
4680                 Roo.log(" label");
4681                  cfg.cn = [
4682                    
4683                     {
4684                         tag: 'label',
4685                         //cls : 'input-group-addon',
4686                         html : this.fieldLabel
4687                         
4688                     },
4689                     
4690                     inputblock
4691                     
4692                 ];
4693
4694         } else {
4695             
4696                 Roo.log(" no label && no align");
4697                 cfg.cn = [
4698                     
4699                         inputblock
4700                     
4701                 ];
4702                 
4703                 
4704         };
4705         Roo.log('input-parentType: ' + this.parentType);
4706         
4707         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4708            cfg.cls += ' navbar-form';
4709            Roo.log(cfg);
4710         }
4711         
4712         return cfg;
4713         
4714     },
4715     /**
4716      * return the real input element.
4717      */
4718     inputEl: function ()
4719     {
4720         return this.el.select('input.form-control',true).first();
4721     },
4722     setDisabled : function(v)
4723     {
4724         var i  = this.inputEl().dom;
4725         if (!v) {
4726             i.removeAttribute('disabled');
4727             return;
4728             
4729         }
4730         i.setAttribute('disabled','true');
4731     },
4732     initEvents : function()
4733     {
4734         
4735         this.inputEl().on("keydown" , this.fireKey,  this);
4736         this.inputEl().on("focus", this.onFocus,  this);
4737         this.inputEl().on("blur", this.onBlur,  this);
4738         
4739         this.inputEl().relayEvent('keyup', this);
4740
4741         // reference to original value for reset
4742         this.originalValue = this.getValue();
4743         //Roo.form.TextField.superclass.initEvents.call(this);
4744         if(this.validationEvent == 'keyup'){
4745             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4746             this.inputEl().on('keyup', this.filterValidation, this);
4747         }
4748         else if(this.validationEvent !== false){
4749             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4750         }
4751         
4752         if(this.selectOnFocus){
4753             this.on("focus", this.preFocus, this);
4754             
4755         }
4756         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4757             this.inputEl().on("keypress", this.filterKeys, this);
4758         }
4759        /* if(this.grow){
4760             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4761             this.el.on("click", this.autoSize,  this);
4762         }
4763         */
4764         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4765             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4766         }
4767         
4768     },
4769     filterValidation : function(e){
4770         if(!e.isNavKeyPress()){
4771             this.validationTask.delay(this.validationDelay);
4772         }
4773     },
4774      /**
4775      * Validates the field value
4776      * @return {Boolean} True if the value is valid, else false
4777      */
4778     validate : function(){
4779         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4780         if(this.disabled || this.validateValue(this.getRawValue())){
4781             this.clearInvalid();
4782             return true;
4783         }
4784         return false;
4785     },
4786     
4787     
4788     /**
4789      * Validates a value according to the field's validation rules and marks the field as invalid
4790      * if the validation fails
4791      * @param {Mixed} value The value to validate
4792      * @return {Boolean} True if the value is valid, else false
4793      */
4794     validateValue : function(value){
4795         if(value.length < 1)  { // if it's blank
4796              if(this.allowBlank){
4797                 this.clearInvalid();
4798                 return true;
4799              }else{
4800                 this.markInvalid(this.blankText);
4801                 return false;
4802              }
4803         }
4804         if(value.length < this.minLength){
4805             this.markInvalid(String.format(this.minLengthText, this.minLength));
4806             return false;
4807         }
4808         if(value.length > this.maxLength){
4809             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4810             return false;
4811         }
4812         if(this.vtype){
4813             var vt = Roo.form.VTypes;
4814             if(!vt[this.vtype](value, this)){
4815                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4816                 return false;
4817             }
4818         }
4819         if(typeof this.validator == "function"){
4820             var msg = this.validator(value);
4821             if(msg !== true){
4822                 this.markInvalid(msg);
4823                 return false;
4824             }
4825         }
4826         if(this.regex && !this.regex.test(value)){
4827             this.markInvalid(this.regexText);
4828             return false;
4829         }
4830         return true;
4831     },
4832
4833     
4834     
4835      // private
4836     fireKey : function(e){
4837         //Roo.log('field ' + e.getKey());
4838         if(e.isNavKeyPress()){
4839             this.fireEvent("specialkey", this, e);
4840         }
4841     },
4842     focus : function (selectText){
4843         if(this.rendered){
4844             this.inputEl().focus();
4845             if(selectText === true){
4846                 this.inputEl().dom.select();
4847             }
4848         }
4849         return this;
4850     } ,
4851     
4852     onFocus : function(){
4853         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4854            // this.el.addClass(this.focusClass);
4855         }
4856         if(!this.hasFocus){
4857             this.hasFocus = true;
4858             this.startValue = this.getValue();
4859             this.fireEvent("focus", this);
4860         }
4861     },
4862     
4863     beforeBlur : Roo.emptyFn,
4864
4865     
4866     // private
4867     onBlur : function(){
4868         this.beforeBlur();
4869         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4870             //this.el.removeClass(this.focusClass);
4871         }
4872         this.hasFocus = false;
4873         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4874             this.validate();
4875         }
4876         var v = this.getValue();
4877         if(String(v) !== String(this.startValue)){
4878             this.fireEvent('change', this, v, this.startValue);
4879         }
4880         this.fireEvent("blur", this);
4881     },
4882     
4883     /**
4884      * Resets the current field value to the originally loaded value and clears any validation messages
4885      */
4886     reset : function(){
4887         this.setValue(this.originalValue);
4888         this.clearInvalid();
4889     },
4890      /**
4891      * Returns the name of the field
4892      * @return {Mixed} name The name field
4893      */
4894     getName: function(){
4895         return this.name;
4896     },
4897      /**
4898      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4899      * @return {Mixed} value The field value
4900      */
4901     getValue : function(){
4902         return this.inputEl().getValue();
4903     },
4904     /**
4905      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4906      * @return {Mixed} value The field value
4907      */
4908     getRawValue : function(){
4909         var v = this.inputEl().getValue();
4910         
4911         return v;
4912     },
4913     
4914     /**
4915      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4916      * @param {Mixed} value The value to set
4917      */
4918     setRawValue : function(v){
4919         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4920     },
4921     
4922     selectText : function(start, end){
4923         var v = this.getRawValue();
4924         if(v.length > 0){
4925             start = start === undefined ? 0 : start;
4926             end = end === undefined ? v.length : end;
4927             var d = this.inputEl().dom;
4928             if(d.setSelectionRange){
4929                 d.setSelectionRange(start, end);
4930             }else if(d.createTextRange){
4931                 var range = d.createTextRange();
4932                 range.moveStart("character", start);
4933                 range.moveEnd("character", v.length-end);
4934                 range.select();
4935             }
4936         }
4937     },
4938     
4939     /**
4940      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4941      * @param {Mixed} value The value to set
4942      */
4943     setValue : function(v){
4944         this.value = v;
4945         if(this.rendered){
4946             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4947             this.validate();
4948         }
4949     },
4950     
4951     /*
4952     processValue : function(value){
4953         if(this.stripCharsRe){
4954             var newValue = value.replace(this.stripCharsRe, '');
4955             if(newValue !== value){
4956                 this.setRawValue(newValue);
4957                 return newValue;
4958             }
4959         }
4960         return value;
4961     },
4962   */
4963     preFocus : function(){
4964         
4965         if(this.selectOnFocus){
4966             this.inputEl().dom.select();
4967         }
4968     },
4969     filterKeys : function(e){
4970         var k = e.getKey();
4971         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4972             return;
4973         }
4974         var c = e.getCharCode(), cc = String.fromCharCode(c);
4975         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4976             return;
4977         }
4978         if(!this.maskRe.test(cc)){
4979             e.stopEvent();
4980         }
4981     },
4982      /**
4983      * Clear any invalid styles/messages for this field
4984      */
4985     clearInvalid : function(){
4986         
4987         if(!this.el || this.preventMark){ // not rendered
4988             return;
4989         }
4990         this.el.removeClass(this.invalidClass);
4991         /*
4992         switch(this.msgTarget){
4993             case 'qtip':
4994                 this.el.dom.qtip = '';
4995                 break;
4996             case 'title':
4997                 this.el.dom.title = '';
4998                 break;
4999             case 'under':
5000                 if(this.errorEl){
5001                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5002                 }
5003                 break;
5004             case 'side':
5005                 if(this.errorIcon){
5006                     this.errorIcon.dom.qtip = '';
5007                     this.errorIcon.hide();
5008                     this.un('resize', this.alignErrorIcon, this);
5009                 }
5010                 break;
5011             default:
5012                 var t = Roo.getDom(this.msgTarget);
5013                 t.innerHTML = '';
5014                 t.style.display = 'none';
5015                 break;
5016         }
5017         */
5018         this.fireEvent('valid', this);
5019     },
5020      /**
5021      * Mark this field as invalid
5022      * @param {String} msg The validation message
5023      */
5024     markInvalid : function(msg){
5025         if(!this.el  || this.preventMark){ // not rendered
5026             return;
5027         }
5028         this.el.addClass(this.invalidClass);
5029         /*
5030         msg = msg || this.invalidText;
5031         switch(this.msgTarget){
5032             case 'qtip':
5033                 this.el.dom.qtip = msg;
5034                 this.el.dom.qclass = 'x-form-invalid-tip';
5035                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5036                     Roo.QuickTips.enable();
5037                 }
5038                 break;
5039             case 'title':
5040                 this.el.dom.title = msg;
5041                 break;
5042             case 'under':
5043                 if(!this.errorEl){
5044                     var elp = this.el.findParent('.x-form-element', 5, true);
5045                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5046                     this.errorEl.setWidth(elp.getWidth(true)-20);
5047                 }
5048                 this.errorEl.update(msg);
5049                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5050                 break;
5051             case 'side':
5052                 if(!this.errorIcon){
5053                     var elp = this.el.findParent('.x-form-element', 5, true);
5054                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5055                 }
5056                 this.alignErrorIcon();
5057                 this.errorIcon.dom.qtip = msg;
5058                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5059                 this.errorIcon.show();
5060                 this.on('resize', this.alignErrorIcon, this);
5061                 break;
5062             default:
5063                 var t = Roo.getDom(this.msgTarget);
5064                 t.innerHTML = msg;
5065                 t.style.display = this.msgDisplay;
5066                 break;
5067         }
5068         */
5069         this.fireEvent('invalid', this, msg);
5070     },
5071     // private
5072     SafariOnKeyDown : function(event)
5073     {
5074         // this is a workaround for a password hang bug on chrome/ webkit.
5075         
5076         var isSelectAll = false;
5077         
5078         if(this.inputEl().dom.selectionEnd > 0){
5079             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5080         }
5081         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5082             event.preventDefault();
5083             this.setValue('');
5084             return;
5085         }
5086         
5087         if(isSelectAll){ // backspace and delete key
5088             
5089             event.preventDefault();
5090             // this is very hacky as keydown always get's upper case.
5091             //
5092             var cc = String.fromCharCode(event.getCharCode());
5093             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
5094             
5095         }
5096     },
5097     adjustWidth : function(tag, w){
5098         tag = tag.toLowerCase();
5099         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5100             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5101                 if(tag == 'input'){
5102                     return w + 2;
5103                 }
5104                 if(tag == 'textarea'){
5105                     return w-2;
5106                 }
5107             }else if(Roo.isOpera){
5108                 if(tag == 'input'){
5109                     return w + 2;
5110                 }
5111                 if(tag == 'textarea'){
5112                     return w-2;
5113                 }
5114             }
5115         }
5116         return w;
5117     }
5118     
5119 });
5120
5121  
5122 /*
5123  * - LGPL
5124  *
5125  * Input
5126  * 
5127  */
5128
5129 /**
5130  * @class Roo.bootstrap.TextArea
5131  * @extends Roo.bootstrap.Input
5132  * Bootstrap TextArea class
5133  * @cfg {Number} cols Specifies the visible width of a text area
5134  * @cfg {Number} rows Specifies the visible number of lines in a text area
5135  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5136  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5137  * @cfg {string} html text
5138  * 
5139  * @constructor
5140  * Create a new TextArea
5141  * @param {Object} config The config object
5142  */
5143
5144 Roo.bootstrap.TextArea = function(config){
5145     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5146    
5147 };
5148
5149 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5150      
5151     cols : false,
5152     rows : 5,
5153     readOnly : false,
5154     warp : 'soft',
5155     resize : false,
5156     value: false,
5157     html: false,
5158     
5159     getAutoCreate : function(){
5160         
5161         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5162         
5163         var id = Roo.id();
5164         
5165         var cfg = {};
5166         
5167         var input =  {
5168             tag: 'textarea',
5169             id : id,
5170             warp : this.warp,
5171             rows : this.rows,
5172             value : this.value || '',
5173             html: this.html || '',
5174             cls : 'form-control',
5175             placeholder : this.placeholder || '' 
5176             
5177         };
5178         
5179         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5180             input.maxLength = this.maxLength;
5181         }
5182         
5183         if(this.resize){
5184             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5185         }
5186         
5187         if(this.cols){
5188             input.cols = this.cols;
5189         }
5190         
5191         if (this.readOnly) {
5192             input.readonly = true;
5193         }
5194         
5195         if (this.name) {
5196             input.name = this.name;
5197         }
5198         
5199         if (this.size) {
5200             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5201         }
5202         
5203         var settings=this;
5204         ['xs','sm','md','lg'].map(function(size){
5205             if (settings[size]) {
5206                 cfg.cls += ' col-' + size + '-' + settings[size];
5207             }
5208         });
5209         
5210         var inputblock = input;
5211         
5212         if (this.before || this.after) {
5213             
5214             inputblock = {
5215                 cls : 'input-group',
5216                 cn :  [] 
5217             };
5218             if (this.before) {
5219                 inputblock.cn.push({
5220                     tag :'span',
5221                     cls : 'input-group-addon',
5222                     html : this.before
5223                 });
5224             }
5225             inputblock.cn.push(input);
5226             if (this.after) {
5227                 inputblock.cn.push({
5228                     tag :'span',
5229                     cls : 'input-group-addon',
5230                     html : this.after
5231                 });
5232             }
5233             
5234         }
5235         
5236         if (align ==='left' && this.fieldLabel.length) {
5237                 Roo.log("left and has label");
5238                 cfg.cn = [
5239                     
5240                     {
5241                         tag: 'label',
5242                         'for' :  id,
5243                         cls : 'control-label col-sm-' + this.labelWidth,
5244                         html : this.fieldLabel
5245                         
5246                     },
5247                     {
5248                         cls : "col-sm-" + (12 - this.labelWidth), 
5249                         cn: [
5250                             inputblock
5251                         ]
5252                     }
5253                     
5254                 ];
5255         } else if ( this.fieldLabel.length) {
5256                 Roo.log(" label");
5257                  cfg.cn = [
5258                    
5259                     {
5260                         tag: 'label',
5261                         //cls : 'input-group-addon',
5262                         html : this.fieldLabel
5263                         
5264                     },
5265                     
5266                     inputblock
5267                     
5268                 ];
5269
5270         } else {
5271             
5272                    Roo.log(" no label && no align");
5273                 cfg.cn = [
5274                     
5275                         inputblock
5276                     
5277                 ];
5278                 
5279                 
5280         }
5281         
5282         if (this.disabled) {
5283             input.disabled=true;
5284         }
5285         
5286         return cfg;
5287         
5288     },
5289     /**
5290      * return the real textarea element.
5291      */
5292     inputEl: function ()
5293     {
5294         return this.el.select('textarea.form-control',true).first();
5295     }
5296 });
5297
5298  
5299 /*
5300  * - LGPL
5301  *
5302  * trigger field - base class for combo..
5303  * 
5304  */
5305  
5306 /**
5307  * @class Roo.bootstrap.TriggerField
5308  * @extends Roo.bootstrap.Input
5309  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5310  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5311  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5312  * for which you can provide a custom implementation.  For example:
5313  * <pre><code>
5314 var trigger = new Roo.bootstrap.TriggerField();
5315 trigger.onTriggerClick = myTriggerFn;
5316 trigger.applyTo('my-field');
5317 </code></pre>
5318  *
5319  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5320  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5321  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5322  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5323  * @constructor
5324  * Create a new TriggerField.
5325  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5326  * to the base TextField)
5327  */
5328 Roo.bootstrap.TriggerField = function(config){
5329     this.mimicing = false;
5330     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5331 };
5332
5333 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5334     /**
5335      * @cfg {String} triggerClass A CSS class to apply to the trigger
5336      */
5337      /**
5338      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5339      */
5340     hideTrigger:false,
5341
5342     /** @cfg {Boolean} grow @hide */
5343     /** @cfg {Number} growMin @hide */
5344     /** @cfg {Number} growMax @hide */
5345
5346     /**
5347      * @hide 
5348      * @method
5349      */
5350     autoSize: Roo.emptyFn,
5351     // private
5352     monitorTab : true,
5353     // private
5354     deferHeight : true,
5355
5356     
5357     actionMode : 'wrap',
5358     
5359     
5360     
5361     getAutoCreate : function(){
5362        
5363         var parent = this.parent();
5364         
5365         var align = this.parentLabelAlign();
5366         
5367         var id = Roo.id();
5368         
5369         var cfg = {
5370             cls: 'form-group' //input-group
5371         };
5372         
5373         
5374         var input =  {
5375             tag: 'input',
5376             id : id,
5377             type : this.inputType,
5378             cls : 'form-control',
5379             autocomplete: 'off',
5380             placeholder : this.placeholder || '' 
5381             
5382         };
5383         if (this.name) {
5384             input.name = this.name;
5385         }
5386         if (this.size) {
5387             input.cls += ' input-' + this.size;
5388         }
5389         
5390         if (this.disabled) {
5391             input.disabled=true;
5392         }
5393         
5394         var inputblock = input;
5395         
5396         if (this.before || this.after) {
5397             
5398             inputblock = {
5399                 cls : 'input-group',
5400                 cn :  [] 
5401             };
5402             if (this.before) {
5403                 inputblock.cn.push({
5404                     tag :'span',
5405                     cls : 'input-group-addon',
5406                     html : this.before
5407                 });
5408             }
5409             inputblock.cn.push(input);
5410             if (this.after) {
5411                 inputblock.cn.push({
5412                     tag :'span',
5413                     cls : 'input-group-addon',
5414                     html : this.after
5415                 });
5416             }
5417             
5418         };
5419         
5420         var box = {
5421             tag: 'div',
5422             cn: [
5423                 {
5424                     tag: 'input',
5425                     type : 'hidden',
5426                     cls: 'form-hidden-field'
5427                 },
5428                 inputblock
5429             ]
5430             
5431         };
5432         
5433         if(this.multiple){
5434             Roo.log('multiple');
5435             
5436             box = {
5437                 tag: 'div',
5438                 cn: [
5439                     {
5440                         tag: 'input',
5441                         type : 'hidden',
5442                         cls: 'form-hidden-field'
5443                     },
5444                     {
5445                         tag: 'ul',
5446                         cls: 'select2-choices',
5447                         cn:[
5448                             {
5449                                 tag: 'li',
5450                                 cls: 'select2-search-field',
5451                                 cn: [
5452
5453                                     inputblock
5454                                 ]
5455                             }
5456                         ]
5457                     }
5458                 ]
5459             }
5460         };
5461         
5462         var combobox = {
5463             cls: 'select2-container input-group',
5464             cn: [
5465                 box,
5466                 {
5467                     tag: 'ul',
5468                     cls: 'typeahead typeahead-long dropdown-menu',
5469                     style: 'display:none'
5470                 }
5471             ]
5472         };
5473         
5474         if(!this.multiple){
5475             combobox.cn.push({
5476                 tag :'span',
5477                 cls : 'input-group-addon btn dropdown-toggle',
5478                 cn : [
5479                     {
5480                         tag: 'span',
5481                         cls: 'caret'
5482                     },
5483                     {
5484                         tag: 'span',
5485                         cls: 'combobox-clear',
5486                         cn  : [
5487                             {
5488                                 tag : 'i',
5489                                 cls: 'icon-remove'
5490                             }
5491                         ]
5492                     }
5493                 ]
5494
5495             })
5496         }
5497         
5498         if(this.multiple){
5499             combobox.cls += ' select2-container-multi';
5500         }
5501         
5502         if (align ==='left' && this.fieldLabel.length) {
5503             
5504                 Roo.log("left and has label");
5505                 cfg.cn = [
5506                     
5507                     {
5508                         tag: 'label',
5509                         'for' :  id,
5510                         cls : 'control-label col-sm-' + this.labelWidth,
5511                         html : this.fieldLabel
5512                         
5513                     },
5514                     {
5515                         cls : "col-sm-" + (12 - this.labelWidth), 
5516                         cn: [
5517                             combobox
5518                         ]
5519                     }
5520                     
5521                 ];
5522         } else if ( this.fieldLabel.length) {
5523                 Roo.log(" label");
5524                  cfg.cn = [
5525                    
5526                     {
5527                         tag: 'label',
5528                         //cls : 'input-group-addon',
5529                         html : this.fieldLabel
5530                         
5531                     },
5532                     
5533                     combobox
5534                     
5535                 ];
5536
5537         } else {
5538             
5539                 Roo.log(" no label && no align");
5540                 cfg = combobox
5541                      
5542                 
5543         }
5544          
5545         var settings=this;
5546         ['xs','sm','md','lg'].map(function(size){
5547             if (settings[size]) {
5548                 cfg.cls += ' col-' + size + '-' + settings[size];
5549             }
5550         });
5551         
5552         return cfg;
5553         
5554     },
5555     
5556     
5557     
5558     // private
5559     onResize : function(w, h){
5560 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5561 //        if(typeof w == 'number'){
5562 //            var x = w - this.trigger.getWidth();
5563 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5564 //            this.trigger.setStyle('left', x+'px');
5565 //        }
5566     },
5567
5568     // private
5569     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5570
5571     // private
5572     getResizeEl : function(){
5573         return this.inputEl();
5574     },
5575
5576     // private
5577     getPositionEl : function(){
5578         return this.inputEl();
5579     },
5580
5581     // private
5582     alignErrorIcon : function(){
5583         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5584     },
5585
5586     // private
5587     initEvents : function(){
5588         
5589         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5590         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5591         if(!this.multiple){
5592             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5593             if(this.hideTrigger){
5594                 this.trigger.setDisplayed(false);
5595             }
5596             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5597         }
5598         
5599         if(this.multiple){
5600             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5601         }
5602         
5603         //this.trigger.addClassOnOver('x-form-trigger-over');
5604         //this.trigger.addClassOnClick('x-form-trigger-click');
5605         
5606         //if(!this.width){
5607         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5608         //}
5609     },
5610
5611     // private
5612     initTrigger : function(){
5613        
5614     },
5615
5616     // private
5617     onDestroy : function(){
5618         if(this.trigger){
5619             this.trigger.removeAllListeners();
5620           //  this.trigger.remove();
5621         }
5622         //if(this.wrap){
5623         //    this.wrap.remove();
5624         //}
5625         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5626     },
5627
5628     // private
5629     onFocus : function(){
5630         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5631         /*
5632         if(!this.mimicing){
5633             this.wrap.addClass('x-trigger-wrap-focus');
5634             this.mimicing = true;
5635             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5636             if(this.monitorTab){
5637                 this.el.on("keydown", this.checkTab, this);
5638             }
5639         }
5640         */
5641     },
5642
5643     // private
5644     checkTab : function(e){
5645         if(e.getKey() == e.TAB){
5646             this.triggerBlur();
5647         }
5648     },
5649
5650     // private
5651     onBlur : function(){
5652         // do nothing
5653     },
5654
5655     // private
5656     mimicBlur : function(e, t){
5657         /*
5658         if(!this.wrap.contains(t) && this.validateBlur()){
5659             this.triggerBlur();
5660         }
5661         */
5662     },
5663
5664     // private
5665     triggerBlur : function(){
5666         this.mimicing = false;
5667         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5668         if(this.monitorTab){
5669             this.el.un("keydown", this.checkTab, this);
5670         }
5671         //this.wrap.removeClass('x-trigger-wrap-focus');
5672         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5673     },
5674
5675     // private
5676     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5677     validateBlur : function(e, t){
5678         return true;
5679     },
5680
5681     // private
5682     onDisable : function(){
5683         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5684         //if(this.wrap){
5685         //    this.wrap.addClass('x-item-disabled');
5686         //}
5687     },
5688
5689     // private
5690     onEnable : function(){
5691         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5692         //if(this.wrap){
5693         //    this.el.removeClass('x-item-disabled');
5694         //}
5695     },
5696
5697     // private
5698     onShow : function(){
5699         var ae = this.getActionEl();
5700         
5701         if(ae){
5702             ae.dom.style.display = '';
5703             ae.dom.style.visibility = 'visible';
5704         }
5705     },
5706
5707     // private
5708     
5709     onHide : function(){
5710         var ae = this.getActionEl();
5711         ae.dom.style.display = 'none';
5712     },
5713
5714     /**
5715      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5716      * by an implementing function.
5717      * @method
5718      * @param {EventObject} e
5719      */
5720     onTriggerClick : Roo.emptyFn
5721 });
5722  /*
5723  * Based on:
5724  * Ext JS Library 1.1.1
5725  * Copyright(c) 2006-2007, Ext JS, LLC.
5726  *
5727  * Originally Released Under LGPL - original licence link has changed is not relivant.
5728  *
5729  * Fork - LGPL
5730  * <script type="text/javascript">
5731  */
5732
5733
5734 /**
5735  * @class Roo.data.SortTypes
5736  * @singleton
5737  * Defines the default sorting (casting?) comparison functions used when sorting data.
5738  */
5739 Roo.data.SortTypes = {
5740     /**
5741      * Default sort that does nothing
5742      * @param {Mixed} s The value being converted
5743      * @return {Mixed} The comparison value
5744      */
5745     none : function(s){
5746         return s;
5747     },
5748     
5749     /**
5750      * The regular expression used to strip tags
5751      * @type {RegExp}
5752      * @property
5753      */
5754     stripTagsRE : /<\/?[^>]+>/gi,
5755     
5756     /**
5757      * Strips all HTML tags to sort on text only
5758      * @param {Mixed} s The value being converted
5759      * @return {String} The comparison value
5760      */
5761     asText : function(s){
5762         return String(s).replace(this.stripTagsRE, "");
5763     },
5764     
5765     /**
5766      * Strips all HTML tags to sort on text only - Case insensitive
5767      * @param {Mixed} s The value being converted
5768      * @return {String} The comparison value
5769      */
5770     asUCText : function(s){
5771         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5772     },
5773     
5774     /**
5775      * Case insensitive string
5776      * @param {Mixed} s The value being converted
5777      * @return {String} The comparison value
5778      */
5779     asUCString : function(s) {
5780         return String(s).toUpperCase();
5781     },
5782     
5783     /**
5784      * Date sorting
5785      * @param {Mixed} s The value being converted
5786      * @return {Number} The comparison value
5787      */
5788     asDate : function(s) {
5789         if(!s){
5790             return 0;
5791         }
5792         if(s instanceof Date){
5793             return s.getTime();
5794         }
5795         return Date.parse(String(s));
5796     },
5797     
5798     /**
5799      * Float sorting
5800      * @param {Mixed} s The value being converted
5801      * @return {Float} The comparison value
5802      */
5803     asFloat : function(s) {
5804         var val = parseFloat(String(s).replace(/,/g, ""));
5805         if(isNaN(val)) val = 0;
5806         return val;
5807     },
5808     
5809     /**
5810      * Integer sorting
5811      * @param {Mixed} s The value being converted
5812      * @return {Number} The comparison value
5813      */
5814     asInt : function(s) {
5815         var val = parseInt(String(s).replace(/,/g, ""));
5816         if(isNaN(val)) val = 0;
5817         return val;
5818     }
5819 };/*
5820  * Based on:
5821  * Ext JS Library 1.1.1
5822  * Copyright(c) 2006-2007, Ext JS, LLC.
5823  *
5824  * Originally Released Under LGPL - original licence link has changed is not relivant.
5825  *
5826  * Fork - LGPL
5827  * <script type="text/javascript">
5828  */
5829
5830 /**
5831 * @class Roo.data.Record
5832  * Instances of this class encapsulate both record <em>definition</em> information, and record
5833  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5834  * to access Records cached in an {@link Roo.data.Store} object.<br>
5835  * <p>
5836  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5837  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5838  * objects.<br>
5839  * <p>
5840  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5841  * @constructor
5842  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5843  * {@link #create}. The parameters are the same.
5844  * @param {Array} data An associative Array of data values keyed by the field name.
5845  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5846  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5847  * not specified an integer id is generated.
5848  */
5849 Roo.data.Record = function(data, id){
5850     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5851     this.data = data;
5852 };
5853
5854 /**
5855  * Generate a constructor for a specific record layout.
5856  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5857  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5858  * Each field definition object may contain the following properties: <ul>
5859  * <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,
5860  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5861  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5862  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5863  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5864  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5865  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5866  * this may be omitted.</p></li>
5867  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5868  * <ul><li>auto (Default, implies no conversion)</li>
5869  * <li>string</li>
5870  * <li>int</li>
5871  * <li>float</li>
5872  * <li>boolean</li>
5873  * <li>date</li></ul></p></li>
5874  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5875  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5876  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5877  * by the Reader into an object that will be stored in the Record. It is passed the
5878  * following parameters:<ul>
5879  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5880  * </ul></p></li>
5881  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5882  * </ul>
5883  * <br>usage:<br><pre><code>
5884 var TopicRecord = Roo.data.Record.create(
5885     {name: 'title', mapping: 'topic_title'},
5886     {name: 'author', mapping: 'username'},
5887     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5888     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5889     {name: 'lastPoster', mapping: 'user2'},
5890     {name: 'excerpt', mapping: 'post_text'}
5891 );
5892
5893 var myNewRecord = new TopicRecord({
5894     title: 'Do my job please',
5895     author: 'noobie',
5896     totalPosts: 1,
5897     lastPost: new Date(),
5898     lastPoster: 'Animal',
5899     excerpt: 'No way dude!'
5900 });
5901 myStore.add(myNewRecord);
5902 </code></pre>
5903  * @method create
5904  * @static
5905  */
5906 Roo.data.Record.create = function(o){
5907     var f = function(){
5908         f.superclass.constructor.apply(this, arguments);
5909     };
5910     Roo.extend(f, Roo.data.Record);
5911     var p = f.prototype;
5912     p.fields = new Roo.util.MixedCollection(false, function(field){
5913         return field.name;
5914     });
5915     for(var i = 0, len = o.length; i < len; i++){
5916         p.fields.add(new Roo.data.Field(o[i]));
5917     }
5918     f.getField = function(name){
5919         return p.fields.get(name);  
5920     };
5921     return f;
5922 };
5923
5924 Roo.data.Record.AUTO_ID = 1000;
5925 Roo.data.Record.EDIT = 'edit';
5926 Roo.data.Record.REJECT = 'reject';
5927 Roo.data.Record.COMMIT = 'commit';
5928
5929 Roo.data.Record.prototype = {
5930     /**
5931      * Readonly flag - true if this record has been modified.
5932      * @type Boolean
5933      */
5934     dirty : false,
5935     editing : false,
5936     error: null,
5937     modified: null,
5938
5939     // private
5940     join : function(store){
5941         this.store = store;
5942     },
5943
5944     /**
5945      * Set the named field to the specified value.
5946      * @param {String} name The name of the field to set.
5947      * @param {Object} value The value to set the field to.
5948      */
5949     set : function(name, value){
5950         if(this.data[name] == value){
5951             return;
5952         }
5953         this.dirty = true;
5954         if(!this.modified){
5955             this.modified = {};
5956         }
5957         if(typeof this.modified[name] == 'undefined'){
5958             this.modified[name] = this.data[name];
5959         }
5960         this.data[name] = value;
5961         if(!this.editing && this.store){
5962             this.store.afterEdit(this);
5963         }       
5964     },
5965
5966     /**
5967      * Get the value of the named field.
5968      * @param {String} name The name of the field to get the value of.
5969      * @return {Object} The value of the field.
5970      */
5971     get : function(name){
5972         return this.data[name]; 
5973     },
5974
5975     // private
5976     beginEdit : function(){
5977         this.editing = true;
5978         this.modified = {}; 
5979     },
5980
5981     // private
5982     cancelEdit : function(){
5983         this.editing = false;
5984         delete this.modified;
5985     },
5986
5987     // private
5988     endEdit : function(){
5989         this.editing = false;
5990         if(this.dirty && this.store){
5991             this.store.afterEdit(this);
5992         }
5993     },
5994
5995     /**
5996      * Usually called by the {@link Roo.data.Store} which owns the Record.
5997      * Rejects all changes made to the Record since either creation, or the last commit operation.
5998      * Modified fields are reverted to their original values.
5999      * <p>
6000      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6001      * of reject operations.
6002      */
6003     reject : function(){
6004         var m = this.modified;
6005         for(var n in m){
6006             if(typeof m[n] != "function"){
6007                 this.data[n] = m[n];
6008             }
6009         }
6010         this.dirty = false;
6011         delete this.modified;
6012         this.editing = false;
6013         if(this.store){
6014             this.store.afterReject(this);
6015         }
6016     },
6017
6018     /**
6019      * Usually called by the {@link Roo.data.Store} which owns the Record.
6020      * Commits all changes made to the Record since either creation, or the last commit operation.
6021      * <p>
6022      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6023      * of commit operations.
6024      */
6025     commit : function(){
6026         this.dirty = false;
6027         delete this.modified;
6028         this.editing = false;
6029         if(this.store){
6030             this.store.afterCommit(this);
6031         }
6032     },
6033
6034     // private
6035     hasError : function(){
6036         return this.error != null;
6037     },
6038
6039     // private
6040     clearError : function(){
6041         this.error = null;
6042     },
6043
6044     /**
6045      * Creates a copy of this record.
6046      * @param {String} id (optional) A new record id if you don't want to use this record's id
6047      * @return {Record}
6048      */
6049     copy : function(newId) {
6050         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6051     }
6052 };/*
6053  * Based on:
6054  * Ext JS Library 1.1.1
6055  * Copyright(c) 2006-2007, Ext JS, LLC.
6056  *
6057  * Originally Released Under LGPL - original licence link has changed is not relivant.
6058  *
6059  * Fork - LGPL
6060  * <script type="text/javascript">
6061  */
6062
6063
6064
6065 /**
6066  * @class Roo.data.Store
6067  * @extends Roo.util.Observable
6068  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6069  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6070  * <p>
6071  * 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
6072  * has no knowledge of the format of the data returned by the Proxy.<br>
6073  * <p>
6074  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6075  * instances from the data object. These records are cached and made available through accessor functions.
6076  * @constructor
6077  * Creates a new Store.
6078  * @param {Object} config A config object containing the objects needed for the Store to access data,
6079  * and read the data into Records.
6080  */
6081 Roo.data.Store = function(config){
6082     this.data = new Roo.util.MixedCollection(false);
6083     this.data.getKey = function(o){
6084         return o.id;
6085     };
6086     this.baseParams = {};
6087     // private
6088     this.paramNames = {
6089         "start" : "start",
6090         "limit" : "limit",
6091         "sort" : "sort",
6092         "dir" : "dir",
6093         "multisort" : "_multisort"
6094     };
6095
6096     if(config && config.data){
6097         this.inlineData = config.data;
6098         delete config.data;
6099     }
6100
6101     Roo.apply(this, config);
6102     
6103     if(this.reader){ // reader passed
6104         this.reader = Roo.factory(this.reader, Roo.data);
6105         this.reader.xmodule = this.xmodule || false;
6106         if(!this.recordType){
6107             this.recordType = this.reader.recordType;
6108         }
6109         if(this.reader.onMetaChange){
6110             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6111         }
6112     }
6113
6114     if(this.recordType){
6115         this.fields = this.recordType.prototype.fields;
6116     }
6117     this.modified = [];
6118
6119     this.addEvents({
6120         /**
6121          * @event datachanged
6122          * Fires when the data cache has changed, and a widget which is using this Store
6123          * as a Record cache should refresh its view.
6124          * @param {Store} this
6125          */
6126         datachanged : true,
6127         /**
6128          * @event metachange
6129          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6130          * @param {Store} this
6131          * @param {Object} meta The JSON metadata
6132          */
6133         metachange : true,
6134         /**
6135          * @event add
6136          * Fires when Records have been added to the Store
6137          * @param {Store} this
6138          * @param {Roo.data.Record[]} records The array of Records added
6139          * @param {Number} index The index at which the record(s) were added
6140          */
6141         add : true,
6142         /**
6143          * @event remove
6144          * Fires when a Record has been removed from the Store
6145          * @param {Store} this
6146          * @param {Roo.data.Record} record The Record that was removed
6147          * @param {Number} index The index at which the record was removed
6148          */
6149         remove : true,
6150         /**
6151          * @event update
6152          * Fires when a Record has been updated
6153          * @param {Store} this
6154          * @param {Roo.data.Record} record The Record that was updated
6155          * @param {String} operation The update operation being performed.  Value may be one of:
6156          * <pre><code>
6157  Roo.data.Record.EDIT
6158  Roo.data.Record.REJECT
6159  Roo.data.Record.COMMIT
6160          * </code></pre>
6161          */
6162         update : true,
6163         /**
6164          * @event clear
6165          * Fires when the data cache has been cleared.
6166          * @param {Store} this
6167          */
6168         clear : true,
6169         /**
6170          * @event beforeload
6171          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6172          * the load action will be canceled.
6173          * @param {Store} this
6174          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6175          */
6176         beforeload : true,
6177         /**
6178          * @event beforeloadadd
6179          * Fires after a new set of Records has been loaded.
6180          * @param {Store} this
6181          * @param {Roo.data.Record[]} records The Records that were loaded
6182          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6183          */
6184         beforeloadadd : true,
6185         /**
6186          * @event load
6187          * Fires after a new set of Records has been loaded, before they are added to the store.
6188          * @param {Store} this
6189          * @param {Roo.data.Record[]} records The Records that were loaded
6190          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6191          * @params {Object} return from reader
6192          */
6193         load : true,
6194         /**
6195          * @event loadexception
6196          * Fires if an exception occurs in the Proxy during loading.
6197          * Called with the signature of the Proxy's "loadexception" event.
6198          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6199          * 
6200          * @param {Proxy} 
6201          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6202          * @param {Object} load options 
6203          * @param {Object} jsonData from your request (normally this contains the Exception)
6204          */
6205         loadexception : true
6206     });
6207     
6208     if(this.proxy){
6209         this.proxy = Roo.factory(this.proxy, Roo.data);
6210         this.proxy.xmodule = this.xmodule || false;
6211         this.relayEvents(this.proxy,  ["loadexception"]);
6212     }
6213     this.sortToggle = {};
6214     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6215
6216     Roo.data.Store.superclass.constructor.call(this);
6217
6218     if(this.inlineData){
6219         this.loadData(this.inlineData);
6220         delete this.inlineData;
6221     }
6222 };
6223
6224 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6225      /**
6226     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6227     * without a remote query - used by combo/forms at present.
6228     */
6229     
6230     /**
6231     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6232     */
6233     /**
6234     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6235     */
6236     /**
6237     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6238     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6239     */
6240     /**
6241     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6242     * on any HTTP request
6243     */
6244     /**
6245     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6246     */
6247     /**
6248     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6249     */
6250     multiSort: false,
6251     /**
6252     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6253     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6254     */
6255     remoteSort : false,
6256
6257     /**
6258     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6259      * loaded or when a record is removed. (defaults to false).
6260     */
6261     pruneModifiedRecords : false,
6262
6263     // private
6264     lastOptions : null,
6265
6266     /**
6267      * Add Records to the Store and fires the add event.
6268      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6269      */
6270     add : function(records){
6271         records = [].concat(records);
6272         for(var i = 0, len = records.length; i < len; i++){
6273             records[i].join(this);
6274         }
6275         var index = this.data.length;
6276         this.data.addAll(records);
6277         this.fireEvent("add", this, records, index);
6278     },
6279
6280     /**
6281      * Remove a Record from the Store and fires the remove event.
6282      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6283      */
6284     remove : function(record){
6285         var index = this.data.indexOf(record);
6286         this.data.removeAt(index);
6287         if(this.pruneModifiedRecords){
6288             this.modified.remove(record);
6289         }
6290         this.fireEvent("remove", this, record, index);
6291     },
6292
6293     /**
6294      * Remove all Records from the Store and fires the clear event.
6295      */
6296     removeAll : function(){
6297         this.data.clear();
6298         if(this.pruneModifiedRecords){
6299             this.modified = [];
6300         }
6301         this.fireEvent("clear", this);
6302     },
6303
6304     /**
6305      * Inserts Records to the Store at the given index and fires the add event.
6306      * @param {Number} index The start index at which to insert the passed Records.
6307      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6308      */
6309     insert : function(index, records){
6310         records = [].concat(records);
6311         for(var i = 0, len = records.length; i < len; i++){
6312             this.data.insert(index, records[i]);
6313             records[i].join(this);
6314         }
6315         this.fireEvent("add", this, records, index);
6316     },
6317
6318     /**
6319      * Get the index within the cache of the passed Record.
6320      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6321      * @return {Number} The index of the passed Record. Returns -1 if not found.
6322      */
6323     indexOf : function(record){
6324         return this.data.indexOf(record);
6325     },
6326
6327     /**
6328      * Get the index within the cache of the Record with the passed id.
6329      * @param {String} id The id of the Record to find.
6330      * @return {Number} The index of the Record. Returns -1 if not found.
6331      */
6332     indexOfId : function(id){
6333         return this.data.indexOfKey(id);
6334     },
6335
6336     /**
6337      * Get the Record with the specified id.
6338      * @param {String} id The id of the Record to find.
6339      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6340      */
6341     getById : function(id){
6342         return this.data.key(id);
6343     },
6344
6345     /**
6346      * Get the Record at the specified index.
6347      * @param {Number} index The index of the Record to find.
6348      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6349      */
6350     getAt : function(index){
6351         return this.data.itemAt(index);
6352     },
6353
6354     /**
6355      * Returns a range of Records between specified indices.
6356      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6357      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6358      * @return {Roo.data.Record[]} An array of Records
6359      */
6360     getRange : function(start, end){
6361         return this.data.getRange(start, end);
6362     },
6363
6364     // private
6365     storeOptions : function(o){
6366         o = Roo.apply({}, o);
6367         delete o.callback;
6368         delete o.scope;
6369         this.lastOptions = o;
6370     },
6371
6372     /**
6373      * Loads the Record cache from the configured Proxy using the configured Reader.
6374      * <p>
6375      * If using remote paging, then the first load call must specify the <em>start</em>
6376      * and <em>limit</em> properties in the options.params property to establish the initial
6377      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6378      * <p>
6379      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6380      * and this call will return before the new data has been loaded. Perform any post-processing
6381      * in a callback function, or in a "load" event handler.</strong>
6382      * <p>
6383      * @param {Object} options An object containing properties which control loading options:<ul>
6384      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6385      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6386      * passed the following arguments:<ul>
6387      * <li>r : Roo.data.Record[]</li>
6388      * <li>options: Options object from the load call</li>
6389      * <li>success: Boolean success indicator</li></ul></li>
6390      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6391      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6392      * </ul>
6393      */
6394     load : function(options){
6395         options = options || {};
6396         if(this.fireEvent("beforeload", this, options) !== false){
6397             this.storeOptions(options);
6398             var p = Roo.apply(options.params || {}, this.baseParams);
6399             // if meta was not loaded from remote source.. try requesting it.
6400             if (!this.reader.metaFromRemote) {
6401                 p._requestMeta = 1;
6402             }
6403             if(this.sortInfo && this.remoteSort){
6404                 var pn = this.paramNames;
6405                 p[pn["sort"]] = this.sortInfo.field;
6406                 p[pn["dir"]] = this.sortInfo.direction;
6407             }
6408             if (this.multiSort) {
6409                 var pn = this.paramNames;
6410                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6411             }
6412             
6413             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6414         }
6415     },
6416
6417     /**
6418      * Reloads the Record cache from the configured Proxy using the configured Reader and
6419      * the options from the last load operation performed.
6420      * @param {Object} options (optional) An object containing properties which may override the options
6421      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6422      * the most recently used options are reused).
6423      */
6424     reload : function(options){
6425         this.load(Roo.applyIf(options||{}, this.lastOptions));
6426     },
6427
6428     // private
6429     // Called as a callback by the Reader during a load operation.
6430     loadRecords : function(o, options, success){
6431         if(!o || success === false){
6432             if(success !== false){
6433                 this.fireEvent("load", this, [], options, o);
6434             }
6435             if(options.callback){
6436                 options.callback.call(options.scope || this, [], options, false);
6437             }
6438             return;
6439         }
6440         // if data returned failure - throw an exception.
6441         if (o.success === false) {
6442             // show a message if no listener is registered.
6443             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6444                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6445             }
6446             // loadmask wil be hooked into this..
6447             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6448             return;
6449         }
6450         var r = o.records, t = o.totalRecords || r.length;
6451         
6452         this.fireEvent("beforeloadadd", this, r, options, o);
6453         
6454         if(!options || options.add !== true){
6455             if(this.pruneModifiedRecords){
6456                 this.modified = [];
6457             }
6458             for(var i = 0, len = r.length; i < len; i++){
6459                 r[i].join(this);
6460             }
6461             if(this.snapshot){
6462                 this.data = this.snapshot;
6463                 delete this.snapshot;
6464             }
6465             this.data.clear();
6466             this.data.addAll(r);
6467             this.totalLength = t;
6468             this.applySort();
6469             this.fireEvent("datachanged", this);
6470         }else{
6471             this.totalLength = Math.max(t, this.data.length+r.length);
6472             this.add(r);
6473         }
6474         this.fireEvent("load", this, r, options, o);
6475         if(options.callback){
6476             options.callback.call(options.scope || this, r, options, true);
6477         }
6478     },
6479
6480
6481     /**
6482      * Loads data from a passed data block. A Reader which understands the format of the data
6483      * must have been configured in the constructor.
6484      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6485      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6486      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6487      */
6488     loadData : function(o, append){
6489         var r = this.reader.readRecords(o);
6490         this.loadRecords(r, {add: append}, true);
6491     },
6492
6493     /**
6494      * Gets the number of cached records.
6495      * <p>
6496      * <em>If using paging, this may not be the total size of the dataset. If the data object
6497      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6498      * the data set size</em>
6499      */
6500     getCount : function(){
6501         return this.data.length || 0;
6502     },
6503
6504     /**
6505      * Gets the total number of records in the dataset as returned by the server.
6506      * <p>
6507      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6508      * the dataset size</em>
6509      */
6510     getTotalCount : function(){
6511         return this.totalLength || 0;
6512     },
6513
6514     /**
6515      * Returns the sort state of the Store as an object with two properties:
6516      * <pre><code>
6517  field {String} The name of the field by which the Records are sorted
6518  direction {String} The sort order, "ASC" or "DESC"
6519      * </code></pre>
6520      */
6521     getSortState : function(){
6522         return this.sortInfo;
6523     },
6524
6525     // private
6526     applySort : function(){
6527         if(this.sortInfo && !this.remoteSort){
6528             var s = this.sortInfo, f = s.field;
6529             var st = this.fields.get(f).sortType;
6530             var fn = function(r1, r2){
6531                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6532                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6533             };
6534             this.data.sort(s.direction, fn);
6535             if(this.snapshot && this.snapshot != this.data){
6536                 this.snapshot.sort(s.direction, fn);
6537             }
6538         }
6539     },
6540
6541     /**
6542      * Sets the default sort column and order to be used by the next load operation.
6543      * @param {String} fieldName The name of the field to sort by.
6544      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6545      */
6546     setDefaultSort : function(field, dir){
6547         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6548     },
6549
6550     /**
6551      * Sort the Records.
6552      * If remote sorting is used, the sort is performed on the server, and the cache is
6553      * reloaded. If local sorting is used, the cache is sorted internally.
6554      * @param {String} fieldName The name of the field to sort by.
6555      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6556      */
6557     sort : function(fieldName, dir){
6558         var f = this.fields.get(fieldName);
6559         if(!dir){
6560             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6561             
6562             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6563                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6564             }else{
6565                 dir = f.sortDir;
6566             }
6567         }
6568         this.sortToggle[f.name] = dir;
6569         this.sortInfo = {field: f.name, direction: dir};
6570         if(!this.remoteSort){
6571             this.applySort();
6572             this.fireEvent("datachanged", this);
6573         }else{
6574             this.load(this.lastOptions);
6575         }
6576     },
6577
6578     /**
6579      * Calls the specified function for each of the Records in the cache.
6580      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6581      * Returning <em>false</em> aborts and exits the iteration.
6582      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6583      */
6584     each : function(fn, scope){
6585         this.data.each(fn, scope);
6586     },
6587
6588     /**
6589      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6590      * (e.g., during paging).
6591      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6592      */
6593     getModifiedRecords : function(){
6594         return this.modified;
6595     },
6596
6597     // private
6598     createFilterFn : function(property, value, anyMatch){
6599         if(!value.exec){ // not a regex
6600             value = String(value);
6601             if(value.length == 0){
6602                 return false;
6603             }
6604             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6605         }
6606         return function(r){
6607             return value.test(r.data[property]);
6608         };
6609     },
6610
6611     /**
6612      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6613      * @param {String} property A field on your records
6614      * @param {Number} start The record index to start at (defaults to 0)
6615      * @param {Number} end The last record index to include (defaults to length - 1)
6616      * @return {Number} The sum
6617      */
6618     sum : function(property, start, end){
6619         var rs = this.data.items, v = 0;
6620         start = start || 0;
6621         end = (end || end === 0) ? end : rs.length-1;
6622
6623         for(var i = start; i <= end; i++){
6624             v += (rs[i].data[property] || 0);
6625         }
6626         return v;
6627     },
6628
6629     /**
6630      * Filter the records by a specified property.
6631      * @param {String} field A field on your records
6632      * @param {String/RegExp} value Either a string that the field
6633      * should start with or a RegExp to test against the field
6634      * @param {Boolean} anyMatch True to match any part not just the beginning
6635      */
6636     filter : function(property, value, anyMatch){
6637         var fn = this.createFilterFn(property, value, anyMatch);
6638         return fn ? this.filterBy(fn) : this.clearFilter();
6639     },
6640
6641     /**
6642      * Filter by a function. The specified function will be called with each
6643      * record in this data source. If the function returns true the record is included,
6644      * otherwise it is filtered.
6645      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6646      * @param {Object} scope (optional) The scope of the function (defaults to this)
6647      */
6648     filterBy : function(fn, scope){
6649         this.snapshot = this.snapshot || this.data;
6650         this.data = this.queryBy(fn, scope||this);
6651         this.fireEvent("datachanged", this);
6652     },
6653
6654     /**
6655      * Query the records by a specified property.
6656      * @param {String} field A field on your records
6657      * @param {String/RegExp} value Either a string that the field
6658      * should start with or a RegExp to test against the field
6659      * @param {Boolean} anyMatch True to match any part not just the beginning
6660      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6661      */
6662     query : function(property, value, anyMatch){
6663         var fn = this.createFilterFn(property, value, anyMatch);
6664         return fn ? this.queryBy(fn) : this.data.clone();
6665     },
6666
6667     /**
6668      * Query by a function. The specified function will be called with each
6669      * record in this data source. If the function returns true the record is included
6670      * in the results.
6671      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6672      * @param {Object} scope (optional) The scope of the function (defaults to this)
6673       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6674      **/
6675     queryBy : function(fn, scope){
6676         var data = this.snapshot || this.data;
6677         return data.filterBy(fn, scope||this);
6678     },
6679
6680     /**
6681      * Collects unique values for a particular dataIndex from this store.
6682      * @param {String} dataIndex The property to collect
6683      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6684      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6685      * @return {Array} An array of the unique values
6686      **/
6687     collect : function(dataIndex, allowNull, bypassFilter){
6688         var d = (bypassFilter === true && this.snapshot) ?
6689                 this.snapshot.items : this.data.items;
6690         var v, sv, r = [], l = {};
6691         for(var i = 0, len = d.length; i < len; i++){
6692             v = d[i].data[dataIndex];
6693             sv = String(v);
6694             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6695                 l[sv] = true;
6696                 r[r.length] = v;
6697             }
6698         }
6699         return r;
6700     },
6701
6702     /**
6703      * Revert to a view of the Record cache with no filtering applied.
6704      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6705      */
6706     clearFilter : function(suppressEvent){
6707         if(this.snapshot && this.snapshot != this.data){
6708             this.data = this.snapshot;
6709             delete this.snapshot;
6710             if(suppressEvent !== true){
6711                 this.fireEvent("datachanged", this);
6712             }
6713         }
6714     },
6715
6716     // private
6717     afterEdit : function(record){
6718         if(this.modified.indexOf(record) == -1){
6719             this.modified.push(record);
6720         }
6721         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6722     },
6723     
6724     // private
6725     afterReject : function(record){
6726         this.modified.remove(record);
6727         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6728     },
6729
6730     // private
6731     afterCommit : function(record){
6732         this.modified.remove(record);
6733         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6734     },
6735
6736     /**
6737      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6738      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6739      */
6740     commitChanges : function(){
6741         var m = this.modified.slice(0);
6742         this.modified = [];
6743         for(var i = 0, len = m.length; i < len; i++){
6744             m[i].commit();
6745         }
6746     },
6747
6748     /**
6749      * Cancel outstanding changes on all changed records.
6750      */
6751     rejectChanges : function(){
6752         var m = this.modified.slice(0);
6753         this.modified = [];
6754         for(var i = 0, len = m.length; i < len; i++){
6755             m[i].reject();
6756         }
6757     },
6758
6759     onMetaChange : function(meta, rtype, o){
6760         this.recordType = rtype;
6761         this.fields = rtype.prototype.fields;
6762         delete this.snapshot;
6763         this.sortInfo = meta.sortInfo || this.sortInfo;
6764         this.modified = [];
6765         this.fireEvent('metachange', this, this.reader.meta);
6766     },
6767     
6768     moveIndex : function(data, type)
6769     {
6770         var index = this.indexOf(data);
6771         
6772         var newIndex = index + type;
6773         
6774         this.remove(data);
6775         
6776         this.insert(newIndex, data);
6777         
6778     }
6779 });/*
6780  * Based on:
6781  * Ext JS Library 1.1.1
6782  * Copyright(c) 2006-2007, Ext JS, LLC.
6783  *
6784  * Originally Released Under LGPL - original licence link has changed is not relivant.
6785  *
6786  * Fork - LGPL
6787  * <script type="text/javascript">
6788  */
6789
6790 /**
6791  * @class Roo.data.SimpleStore
6792  * @extends Roo.data.Store
6793  * Small helper class to make creating Stores from Array data easier.
6794  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6795  * @cfg {Array} fields An array of field definition objects, or field name strings.
6796  * @cfg {Array} data The multi-dimensional array of data
6797  * @constructor
6798  * @param {Object} config
6799  */
6800 Roo.data.SimpleStore = function(config){
6801     Roo.data.SimpleStore.superclass.constructor.call(this, {
6802         isLocal : true,
6803         reader: new Roo.data.ArrayReader({
6804                 id: config.id
6805             },
6806             Roo.data.Record.create(config.fields)
6807         ),
6808         proxy : new Roo.data.MemoryProxy(config.data)
6809     });
6810     this.load();
6811 };
6812 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6813  * Based on:
6814  * Ext JS Library 1.1.1
6815  * Copyright(c) 2006-2007, Ext JS, LLC.
6816  *
6817  * Originally Released Under LGPL - original licence link has changed is not relivant.
6818  *
6819  * Fork - LGPL
6820  * <script type="text/javascript">
6821  */
6822
6823 /**
6824 /**
6825  * @extends Roo.data.Store
6826  * @class Roo.data.JsonStore
6827  * Small helper class to make creating Stores for JSON data easier. <br/>
6828 <pre><code>
6829 var store = new Roo.data.JsonStore({
6830     url: 'get-images.php',
6831     root: 'images',
6832     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6833 });
6834 </code></pre>
6835  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6836  * JsonReader and HttpProxy (unless inline data is provided).</b>
6837  * @cfg {Array} fields An array of field definition objects, or field name strings.
6838  * @constructor
6839  * @param {Object} config
6840  */
6841 Roo.data.JsonStore = function(c){
6842     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6843         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6844         reader: new Roo.data.JsonReader(c, c.fields)
6845     }));
6846 };
6847 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6848  * Based on:
6849  * Ext JS Library 1.1.1
6850  * Copyright(c) 2006-2007, Ext JS, LLC.
6851  *
6852  * Originally Released Under LGPL - original licence link has changed is not relivant.
6853  *
6854  * Fork - LGPL
6855  * <script type="text/javascript">
6856  */
6857
6858  
6859 Roo.data.Field = function(config){
6860     if(typeof config == "string"){
6861         config = {name: config};
6862     }
6863     Roo.apply(this, config);
6864     
6865     if(!this.type){
6866         this.type = "auto";
6867     }
6868     
6869     var st = Roo.data.SortTypes;
6870     // named sortTypes are supported, here we look them up
6871     if(typeof this.sortType == "string"){
6872         this.sortType = st[this.sortType];
6873     }
6874     
6875     // set default sortType for strings and dates
6876     if(!this.sortType){
6877         switch(this.type){
6878             case "string":
6879                 this.sortType = st.asUCString;
6880                 break;
6881             case "date":
6882                 this.sortType = st.asDate;
6883                 break;
6884             default:
6885                 this.sortType = st.none;
6886         }
6887     }
6888
6889     // define once
6890     var stripRe = /[\$,%]/g;
6891
6892     // prebuilt conversion function for this field, instead of
6893     // switching every time we're reading a value
6894     if(!this.convert){
6895         var cv, dateFormat = this.dateFormat;
6896         switch(this.type){
6897             case "":
6898             case "auto":
6899             case undefined:
6900                 cv = function(v){ return v; };
6901                 break;
6902             case "string":
6903                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6904                 break;
6905             case "int":
6906                 cv = function(v){
6907                     return v !== undefined && v !== null && v !== '' ?
6908                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6909                     };
6910                 break;
6911             case "float":
6912                 cv = function(v){
6913                     return v !== undefined && v !== null && v !== '' ?
6914                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6915                     };
6916                 break;
6917             case "bool":
6918             case "boolean":
6919                 cv = function(v){ return v === true || v === "true" || v == 1; };
6920                 break;
6921             case "date":
6922                 cv = function(v){
6923                     if(!v){
6924                         return '';
6925                     }
6926                     if(v instanceof Date){
6927                         return v;
6928                     }
6929                     if(dateFormat){
6930                         if(dateFormat == "timestamp"){
6931                             return new Date(v*1000);
6932                         }
6933                         return Date.parseDate(v, dateFormat);
6934                     }
6935                     var parsed = Date.parse(v);
6936                     return parsed ? new Date(parsed) : null;
6937                 };
6938              break;
6939             
6940         }
6941         this.convert = cv;
6942     }
6943 };
6944
6945 Roo.data.Field.prototype = {
6946     dateFormat: null,
6947     defaultValue: "",
6948     mapping: null,
6949     sortType : null,
6950     sortDir : "ASC"
6951 };/*
6952  * Based on:
6953  * Ext JS Library 1.1.1
6954  * Copyright(c) 2006-2007, Ext JS, LLC.
6955  *
6956  * Originally Released Under LGPL - original licence link has changed is not relivant.
6957  *
6958  * Fork - LGPL
6959  * <script type="text/javascript">
6960  */
6961  
6962 // Base class for reading structured data from a data source.  This class is intended to be
6963 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6964
6965 /**
6966  * @class Roo.data.DataReader
6967  * Base class for reading structured data from a data source.  This class is intended to be
6968  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6969  */
6970
6971 Roo.data.DataReader = function(meta, recordType){
6972     
6973     this.meta = meta;
6974     
6975     this.recordType = recordType instanceof Array ? 
6976         Roo.data.Record.create(recordType) : recordType;
6977 };
6978
6979 Roo.data.DataReader.prototype = {
6980      /**
6981      * Create an empty record
6982      * @param {Object} data (optional) - overlay some values
6983      * @return {Roo.data.Record} record created.
6984      */
6985     newRow :  function(d) {
6986         var da =  {};
6987         this.recordType.prototype.fields.each(function(c) {
6988             switch( c.type) {
6989                 case 'int' : da[c.name] = 0; break;
6990                 case 'date' : da[c.name] = new Date(); break;
6991                 case 'float' : da[c.name] = 0.0; break;
6992                 case 'boolean' : da[c.name] = false; break;
6993                 default : da[c.name] = ""; break;
6994             }
6995             
6996         });
6997         return new this.recordType(Roo.apply(da, d));
6998     }
6999     
7000 };/*
7001  * Based on:
7002  * Ext JS Library 1.1.1
7003  * Copyright(c) 2006-2007, Ext JS, LLC.
7004  *
7005  * Originally Released Under LGPL - original licence link has changed is not relivant.
7006  *
7007  * Fork - LGPL
7008  * <script type="text/javascript">
7009  */
7010
7011 /**
7012  * @class Roo.data.DataProxy
7013  * @extends Roo.data.Observable
7014  * This class is an abstract base class for implementations which provide retrieval of
7015  * unformatted data objects.<br>
7016  * <p>
7017  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7018  * (of the appropriate type which knows how to parse the data object) to provide a block of
7019  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7020  * <p>
7021  * Custom implementations must implement the load method as described in
7022  * {@link Roo.data.HttpProxy#load}.
7023  */
7024 Roo.data.DataProxy = function(){
7025     this.addEvents({
7026         /**
7027          * @event beforeload
7028          * Fires before a network request is made to retrieve a data object.
7029          * @param {Object} This DataProxy object.
7030          * @param {Object} params The params parameter to the load function.
7031          */
7032         beforeload : true,
7033         /**
7034          * @event load
7035          * Fires before the load method's callback is called.
7036          * @param {Object} This DataProxy object.
7037          * @param {Object} o The data object.
7038          * @param {Object} arg The callback argument object passed to the load function.
7039          */
7040         load : true,
7041         /**
7042          * @event loadexception
7043          * Fires if an Exception occurs during data retrieval.
7044          * @param {Object} This DataProxy object.
7045          * @param {Object} o The data object.
7046          * @param {Object} arg The callback argument object passed to the load function.
7047          * @param {Object} e The Exception.
7048          */
7049         loadexception : true
7050     });
7051     Roo.data.DataProxy.superclass.constructor.call(this);
7052 };
7053
7054 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7055
7056     /**
7057      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7058      */
7059 /*
7060  * Based on:
7061  * Ext JS Library 1.1.1
7062  * Copyright(c) 2006-2007, Ext JS, LLC.
7063  *
7064  * Originally Released Under LGPL - original licence link has changed is not relivant.
7065  *
7066  * Fork - LGPL
7067  * <script type="text/javascript">
7068  */
7069 /**
7070  * @class Roo.data.MemoryProxy
7071  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7072  * to the Reader when its load method is called.
7073  * @constructor
7074  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7075  */
7076 Roo.data.MemoryProxy = function(data){
7077     if (data.data) {
7078         data = data.data;
7079     }
7080     Roo.data.MemoryProxy.superclass.constructor.call(this);
7081     this.data = data;
7082 };
7083
7084 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7085     /**
7086      * Load data from the requested source (in this case an in-memory
7087      * data object passed to the constructor), read the data object into
7088      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7089      * process that block using the passed callback.
7090      * @param {Object} params This parameter is not used by the MemoryProxy class.
7091      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7092      * object into a block of Roo.data.Records.
7093      * @param {Function} callback The function into which to pass the block of Roo.data.records.
7094      * The function must be passed <ul>
7095      * <li>The Record block object</li>
7096      * <li>The "arg" argument from the load function</li>
7097      * <li>A boolean success indicator</li>
7098      * </ul>
7099      * @param {Object} scope The scope in which to call the callback
7100      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7101      */
7102     load : function(params, reader, callback, scope, arg){
7103         params = params || {};
7104         var result;
7105         try {
7106             result = reader.readRecords(this.data);
7107         }catch(e){
7108             this.fireEvent("loadexception", this, arg, null, e);
7109             callback.call(scope, null, arg, false);
7110             return;
7111         }
7112         callback.call(scope, result, arg, true);
7113     },
7114     
7115     // private
7116     update : function(params, records){
7117         
7118     }
7119 });/*
7120  * Based on:
7121  * Ext JS Library 1.1.1
7122  * Copyright(c) 2006-2007, Ext JS, LLC.
7123  *
7124  * Originally Released Under LGPL - original licence link has changed is not relivant.
7125  *
7126  * Fork - LGPL
7127  * <script type="text/javascript">
7128  */
7129 /**
7130  * @class Roo.data.HttpProxy
7131  * @extends Roo.data.DataProxy
7132  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7133  * configured to reference a certain URL.<br><br>
7134  * <p>
7135  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7136  * from which the running page was served.<br><br>
7137  * <p>
7138  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7139  * <p>
7140  * Be aware that to enable the browser to parse an XML document, the server must set
7141  * the Content-Type header in the HTTP response to "text/xml".
7142  * @constructor
7143  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7144  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7145  * will be used to make the request.
7146  */
7147 Roo.data.HttpProxy = function(conn){
7148     Roo.data.HttpProxy.superclass.constructor.call(this);
7149     // is conn a conn config or a real conn?
7150     this.conn = conn;
7151     this.useAjax = !conn || !conn.events;
7152   
7153 };
7154
7155 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7156     // thse are take from connection...
7157     
7158     /**
7159      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7160      */
7161     /**
7162      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7163      * extra parameters to each request made by this object. (defaults to undefined)
7164      */
7165     /**
7166      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7167      *  to each request made by this object. (defaults to undefined)
7168      */
7169     /**
7170      * @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)
7171      */
7172     /**
7173      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7174      */
7175      /**
7176      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7177      * @type Boolean
7178      */
7179   
7180
7181     /**
7182      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7183      * @type Boolean
7184      */
7185     /**
7186      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7187      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7188      * a finer-grained basis than the DataProxy events.
7189      */
7190     getConnection : function(){
7191         return this.useAjax ? Roo.Ajax : this.conn;
7192     },
7193
7194     /**
7195      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7196      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7197      * process that block using the passed callback.
7198      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7199      * for the request to the remote server.
7200      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7201      * object into a block of Roo.data.Records.
7202      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7203      * The function must be passed <ul>
7204      * <li>The Record block object</li>
7205      * <li>The "arg" argument from the load function</li>
7206      * <li>A boolean success indicator</li>
7207      * </ul>
7208      * @param {Object} scope The scope in which to call the callback
7209      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7210      */
7211     load : function(params, reader, callback, scope, arg){
7212         if(this.fireEvent("beforeload", this, params) !== false){
7213             var  o = {
7214                 params : params || {},
7215                 request: {
7216                     callback : callback,
7217                     scope : scope,
7218                     arg : arg
7219                 },
7220                 reader: reader,
7221                 callback : this.loadResponse,
7222                 scope: this
7223             };
7224             if(this.useAjax){
7225                 Roo.applyIf(o, this.conn);
7226                 if(this.activeRequest){
7227                     Roo.Ajax.abort(this.activeRequest);
7228                 }
7229                 this.activeRequest = Roo.Ajax.request(o);
7230             }else{
7231                 this.conn.request(o);
7232             }
7233         }else{
7234             callback.call(scope||this, null, arg, false);
7235         }
7236     },
7237
7238     // private
7239     loadResponse : function(o, success, response){
7240         delete this.activeRequest;
7241         if(!success){
7242             this.fireEvent("loadexception", this, o, response);
7243             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7244             return;
7245         }
7246         var result;
7247         try {
7248             result = o.reader.read(response);
7249         }catch(e){
7250             this.fireEvent("loadexception", this, o, response, e);
7251             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7252             return;
7253         }
7254         
7255         this.fireEvent("load", this, o, o.request.arg);
7256         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7257     },
7258
7259     // private
7260     update : function(dataSet){
7261
7262     },
7263
7264     // private
7265     updateResponse : function(dataSet){
7266
7267     }
7268 });/*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278
7279 /**
7280  * @class Roo.data.ScriptTagProxy
7281  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7282  * other than the originating domain of the running page.<br><br>
7283  * <p>
7284  * <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
7285  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7286  * <p>
7287  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7288  * source code that is used as the source inside a &lt;script> tag.<br><br>
7289  * <p>
7290  * In order for the browser to process the returned data, the server must wrap the data object
7291  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7292  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7293  * depending on whether the callback name was passed:
7294  * <p>
7295  * <pre><code>
7296 boolean scriptTag = false;
7297 String cb = request.getParameter("callback");
7298 if (cb != null) {
7299     scriptTag = true;
7300     response.setContentType("text/javascript");
7301 } else {
7302     response.setContentType("application/x-json");
7303 }
7304 Writer out = response.getWriter();
7305 if (scriptTag) {
7306     out.write(cb + "(");
7307 }
7308 out.print(dataBlock.toJsonString());
7309 if (scriptTag) {
7310     out.write(");");
7311 }
7312 </pre></code>
7313  *
7314  * @constructor
7315  * @param {Object} config A configuration object.
7316  */
7317 Roo.data.ScriptTagProxy = function(config){
7318     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7319     Roo.apply(this, config);
7320     this.head = document.getElementsByTagName("head")[0];
7321 };
7322
7323 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7324
7325 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7326     /**
7327      * @cfg {String} url The URL from which to request the data object.
7328      */
7329     /**
7330      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7331      */
7332     timeout : 30000,
7333     /**
7334      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7335      * the server the name of the callback function set up by the load call to process the returned data object.
7336      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7337      * javascript output which calls this named function passing the data object as its only parameter.
7338      */
7339     callbackParam : "callback",
7340     /**
7341      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7342      * name to the request.
7343      */
7344     nocache : true,
7345
7346     /**
7347      * Load data from the configured URL, read the data object into
7348      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7349      * process that block using the passed callback.
7350      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7351      * for the request to the remote server.
7352      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7353      * object into a block of Roo.data.Records.
7354      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7355      * The function must be passed <ul>
7356      * <li>The Record block object</li>
7357      * <li>The "arg" argument from the load function</li>
7358      * <li>A boolean success indicator</li>
7359      * </ul>
7360      * @param {Object} scope The scope in which to call the callback
7361      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7362      */
7363     load : function(params, reader, callback, scope, arg){
7364         if(this.fireEvent("beforeload", this, params) !== false){
7365
7366             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7367
7368             var url = this.url;
7369             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7370             if(this.nocache){
7371                 url += "&_dc=" + (new Date().getTime());
7372             }
7373             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7374             var trans = {
7375                 id : transId,
7376                 cb : "stcCallback"+transId,
7377                 scriptId : "stcScript"+transId,
7378                 params : params,
7379                 arg : arg,
7380                 url : url,
7381                 callback : callback,
7382                 scope : scope,
7383                 reader : reader
7384             };
7385             var conn = this;
7386
7387             window[trans.cb] = function(o){
7388                 conn.handleResponse(o, trans);
7389             };
7390
7391             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7392
7393             if(this.autoAbort !== false){
7394                 this.abort();
7395             }
7396
7397             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7398
7399             var script = document.createElement("script");
7400             script.setAttribute("src", url);
7401             script.setAttribute("type", "text/javascript");
7402             script.setAttribute("id", trans.scriptId);
7403             this.head.appendChild(script);
7404
7405             this.trans = trans;
7406         }else{
7407             callback.call(scope||this, null, arg, false);
7408         }
7409     },
7410
7411     // private
7412     isLoading : function(){
7413         return this.trans ? true : false;
7414     },
7415
7416     /**
7417      * Abort the current server request.
7418      */
7419     abort : function(){
7420         if(this.isLoading()){
7421             this.destroyTrans(this.trans);
7422         }
7423     },
7424
7425     // private
7426     destroyTrans : function(trans, isLoaded){
7427         this.head.removeChild(document.getElementById(trans.scriptId));
7428         clearTimeout(trans.timeoutId);
7429         if(isLoaded){
7430             window[trans.cb] = undefined;
7431             try{
7432                 delete window[trans.cb];
7433             }catch(e){}
7434         }else{
7435             // if hasn't been loaded, wait for load to remove it to prevent script error
7436             window[trans.cb] = function(){
7437                 window[trans.cb] = undefined;
7438                 try{
7439                     delete window[trans.cb];
7440                 }catch(e){}
7441             };
7442         }
7443     },
7444
7445     // private
7446     handleResponse : function(o, trans){
7447         this.trans = false;
7448         this.destroyTrans(trans, true);
7449         var result;
7450         try {
7451             result = trans.reader.readRecords(o);
7452         }catch(e){
7453             this.fireEvent("loadexception", this, o, trans.arg, e);
7454             trans.callback.call(trans.scope||window, null, trans.arg, false);
7455             return;
7456         }
7457         this.fireEvent("load", this, o, trans.arg);
7458         trans.callback.call(trans.scope||window, result, trans.arg, true);
7459     },
7460
7461     // private
7462     handleFailure : function(trans){
7463         this.trans = false;
7464         this.destroyTrans(trans, false);
7465         this.fireEvent("loadexception", this, null, trans.arg);
7466         trans.callback.call(trans.scope||window, null, trans.arg, false);
7467     }
7468 });/*
7469  * Based on:
7470  * Ext JS Library 1.1.1
7471  * Copyright(c) 2006-2007, Ext JS, LLC.
7472  *
7473  * Originally Released Under LGPL - original licence link has changed is not relivant.
7474  *
7475  * Fork - LGPL
7476  * <script type="text/javascript">
7477  */
7478
7479 /**
7480  * @class Roo.data.JsonReader
7481  * @extends Roo.data.DataReader
7482  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7483  * based on mappings in a provided Roo.data.Record constructor.
7484  * 
7485  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7486  * in the reply previously. 
7487  * 
7488  * <p>
7489  * Example code:
7490  * <pre><code>
7491 var RecordDef = Roo.data.Record.create([
7492     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7493     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7494 ]);
7495 var myReader = new Roo.data.JsonReader({
7496     totalProperty: "results",    // The property which contains the total dataset size (optional)
7497     root: "rows",                // The property which contains an Array of row objects
7498     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7499 }, RecordDef);
7500 </code></pre>
7501  * <p>
7502  * This would consume a JSON file like this:
7503  * <pre><code>
7504 { 'results': 2, 'rows': [
7505     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7506     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7507 }
7508 </code></pre>
7509  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7510  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7511  * paged from the remote server.
7512  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7513  * @cfg {String} root name of the property which contains the Array of row objects.
7514  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7515  * @constructor
7516  * Create a new JsonReader
7517  * @param {Object} meta Metadata configuration options
7518  * @param {Object} recordType Either an Array of field definition objects,
7519  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7520  */
7521 Roo.data.JsonReader = function(meta, recordType){
7522     
7523     meta = meta || {};
7524     // set some defaults:
7525     Roo.applyIf(meta, {
7526         totalProperty: 'total',
7527         successProperty : 'success',
7528         root : 'data',
7529         id : 'id'
7530     });
7531     
7532     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7533 };
7534 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7535     
7536     /**
7537      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7538      * Used by Store query builder to append _requestMeta to params.
7539      * 
7540      */
7541     metaFromRemote : false,
7542     /**
7543      * This method is only used by a DataProxy which has retrieved data from a remote server.
7544      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7545      * @return {Object} data A data block which is used by an Roo.data.Store object as
7546      * a cache of Roo.data.Records.
7547      */
7548     read : function(response){
7549         var json = response.responseText;
7550        
7551         var o = /* eval:var:o */ eval("("+json+")");
7552         if(!o) {
7553             throw {message: "JsonReader.read: Json object not found"};
7554         }
7555         
7556         if(o.metaData){
7557             
7558             delete this.ef;
7559             this.metaFromRemote = true;
7560             this.meta = o.metaData;
7561             this.recordType = Roo.data.Record.create(o.metaData.fields);
7562             this.onMetaChange(this.meta, this.recordType, o);
7563         }
7564         return this.readRecords(o);
7565     },
7566
7567     // private function a store will implement
7568     onMetaChange : function(meta, recordType, o){
7569
7570     },
7571
7572     /**
7573          * @ignore
7574          */
7575     simpleAccess: function(obj, subsc) {
7576         return obj[subsc];
7577     },
7578
7579         /**
7580          * @ignore
7581          */
7582     getJsonAccessor: function(){
7583         var re = /[\[\.]/;
7584         return function(expr) {
7585             try {
7586                 return(re.test(expr))
7587                     ? new Function("obj", "return obj." + expr)
7588                     : function(obj){
7589                         return obj[expr];
7590                     };
7591             } catch(e){}
7592             return Roo.emptyFn;
7593         };
7594     }(),
7595
7596     /**
7597      * Create a data block containing Roo.data.Records from an XML document.
7598      * @param {Object} o An object which contains an Array of row objects in the property specified
7599      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7600      * which contains the total size of the dataset.
7601      * @return {Object} data A data block which is used by an Roo.data.Store object as
7602      * a cache of Roo.data.Records.
7603      */
7604     readRecords : function(o){
7605         /**
7606          * After any data loads, the raw JSON data is available for further custom processing.
7607          * @type Object
7608          */
7609         this.o = o;
7610         var s = this.meta, Record = this.recordType,
7611             f = Record.prototype.fields, fi = f.items, fl = f.length;
7612
7613 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7614         if (!this.ef) {
7615             if(s.totalProperty) {
7616                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7617                 }
7618                 if(s.successProperty) {
7619                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7620                 }
7621                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7622                 if (s.id) {
7623                         var g = this.getJsonAccessor(s.id);
7624                         this.getId = function(rec) {
7625                                 var r = g(rec);
7626                                 return (r === undefined || r === "") ? null : r;
7627                         };
7628                 } else {
7629                         this.getId = function(){return null;};
7630                 }
7631             this.ef = [];
7632             for(var jj = 0; jj < fl; jj++){
7633                 f = fi[jj];
7634                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7635                 this.ef[jj] = this.getJsonAccessor(map);
7636             }
7637         }
7638
7639         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7640         if(s.totalProperty){
7641             var vt = parseInt(this.getTotal(o), 10);
7642             if(!isNaN(vt)){
7643                 totalRecords = vt;
7644             }
7645         }
7646         if(s.successProperty){
7647             var vs = this.getSuccess(o);
7648             if(vs === false || vs === 'false'){
7649                 success = false;
7650             }
7651         }
7652         var records = [];
7653             for(var i = 0; i < c; i++){
7654                     var n = root[i];
7655                 var values = {};
7656                 var id = this.getId(n);
7657                 for(var j = 0; j < fl; j++){
7658                     f = fi[j];
7659                 var v = this.ef[j](n);
7660                 if (!f.convert) {
7661                     Roo.log('missing convert for ' + f.name);
7662                     Roo.log(f);
7663                     continue;
7664                 }
7665                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7666                 }
7667                 var record = new Record(values, id);
7668                 record.json = n;
7669                 records[i] = record;
7670             }
7671             return {
7672             raw : o,
7673                 success : success,
7674                 records : records,
7675                 totalRecords : totalRecords
7676             };
7677     }
7678 });/*
7679  * Based on:
7680  * Ext JS Library 1.1.1
7681  * Copyright(c) 2006-2007, Ext JS, LLC.
7682  *
7683  * Originally Released Under LGPL - original licence link has changed is not relivant.
7684  *
7685  * Fork - LGPL
7686  * <script type="text/javascript">
7687  */
7688
7689 /**
7690  * @class Roo.data.ArrayReader
7691  * @extends Roo.data.DataReader
7692  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7693  * Each element of that Array represents a row of data fields. The
7694  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7695  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7696  * <p>
7697  * Example code:.
7698  * <pre><code>
7699 var RecordDef = Roo.data.Record.create([
7700     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7701     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7702 ]);
7703 var myReader = new Roo.data.ArrayReader({
7704     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7705 }, RecordDef);
7706 </code></pre>
7707  * <p>
7708  * This would consume an Array like this:
7709  * <pre><code>
7710 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7711   </code></pre>
7712  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7713  * @constructor
7714  * Create a new JsonReader
7715  * @param {Object} meta Metadata configuration options.
7716  * @param {Object} recordType Either an Array of field definition objects
7717  * as specified to {@link Roo.data.Record#create},
7718  * or an {@link Roo.data.Record} object
7719  * created using {@link Roo.data.Record#create}.
7720  */
7721 Roo.data.ArrayReader = function(meta, recordType){
7722     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7723 };
7724
7725 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7726     /**
7727      * Create a data block containing Roo.data.Records from an XML document.
7728      * @param {Object} o An Array of row objects which represents the dataset.
7729      * @return {Object} data A data block which is used by an Roo.data.Store object as
7730      * a cache of Roo.data.Records.
7731      */
7732     readRecords : function(o){
7733         var sid = this.meta ? this.meta.id : null;
7734         var recordType = this.recordType, fields = recordType.prototype.fields;
7735         var records = [];
7736         var root = o;
7737             for(var i = 0; i < root.length; i++){
7738                     var n = root[i];
7739                 var values = {};
7740                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7741                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7742                 var f = fields.items[j];
7743                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7744                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7745                 v = f.convert(v);
7746                 values[f.name] = v;
7747             }
7748                 var record = new recordType(values, id);
7749                 record.json = n;
7750                 records[records.length] = record;
7751             }
7752             return {
7753                 records : records,
7754                 totalRecords : records.length
7755             };
7756     }
7757 });/*
7758  * - LGPL
7759  * * 
7760  */
7761
7762 /**
7763  * @class Roo.bootstrap.ComboBox
7764  * @extends Roo.bootstrap.TriggerField
7765  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7766  * @cfg {Boolean} append (true|false) default false
7767  * @constructor
7768  * Create a new ComboBox.
7769  * @param {Object} config Configuration options
7770  */
7771 Roo.bootstrap.ComboBox = function(config){
7772     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7773     this.addEvents({
7774         /**
7775          * @event expand
7776          * Fires when the dropdown list is expanded
7777              * @param {Roo.bootstrap.ComboBox} combo This combo box
7778              */
7779         'expand' : true,
7780         /**
7781          * @event collapse
7782          * Fires when the dropdown list is collapsed
7783              * @param {Roo.bootstrap.ComboBox} combo This combo box
7784              */
7785         'collapse' : true,
7786         /**
7787          * @event beforeselect
7788          * Fires before a list item is selected. Return false to cancel the selection.
7789              * @param {Roo.bootstrap.ComboBox} combo This combo box
7790              * @param {Roo.data.Record} record The data record returned from the underlying store
7791              * @param {Number} index The index of the selected item in the dropdown list
7792              */
7793         'beforeselect' : true,
7794         /**
7795          * @event select
7796          * Fires when a list item is selected
7797              * @param {Roo.bootstrap.ComboBox} combo This combo box
7798              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7799              * @param {Number} index The index of the selected item in the dropdown list
7800              */
7801         'select' : true,
7802         /**
7803          * @event beforequery
7804          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7805          * The event object passed has these properties:
7806              * @param {Roo.bootstrap.ComboBox} combo This combo box
7807              * @param {String} query The query
7808              * @param {Boolean} forceAll true to force "all" query
7809              * @param {Boolean} cancel true to cancel the query
7810              * @param {Object} e The query event object
7811              */
7812         'beforequery': true,
7813          /**
7814          * @event add
7815          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7816              * @param {Roo.bootstrap.ComboBox} combo This combo box
7817              */
7818         'add' : true,
7819         /**
7820          * @event edit
7821          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7822              * @param {Roo.bootstrap.ComboBox} combo This combo box
7823              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7824              */
7825         'edit' : true,
7826         /**
7827          * @event remove
7828          * Fires when the remove value from the combobox array
7829              * @param {Roo.bootstrap.ComboBox} combo This combo box
7830              */
7831         'remove' : true
7832         
7833     });
7834     
7835     
7836     this.selectedIndex = -1;
7837     if(this.mode == 'local'){
7838         if(config.queryDelay === undefined){
7839             this.queryDelay = 10;
7840         }
7841         if(config.minChars === undefined){
7842             this.minChars = 0;
7843         }
7844     }
7845 };
7846
7847 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7848      
7849     /**
7850      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7851      * rendering into an Roo.Editor, defaults to false)
7852      */
7853     /**
7854      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7855      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7856      */
7857     /**
7858      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7859      */
7860     /**
7861      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7862      * the dropdown list (defaults to undefined, with no header element)
7863      */
7864
7865      /**
7866      * @cfg {String/Roo.Template} tpl The template to use to render the output
7867      */
7868      
7869      /**
7870      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7871      */
7872     listWidth: undefined,
7873     /**
7874      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7875      * mode = 'remote' or 'text' if mode = 'local')
7876      */
7877     displayField: undefined,
7878     /**
7879      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7880      * mode = 'remote' or 'value' if mode = 'local'). 
7881      * Note: use of a valueField requires the user make a selection
7882      * in order for a value to be mapped.
7883      */
7884     valueField: undefined,
7885     
7886     
7887     /**
7888      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7889      * field's data value (defaults to the underlying DOM element's name)
7890      */
7891     hiddenName: undefined,
7892     /**
7893      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7894      */
7895     listClass: '',
7896     /**
7897      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7898      */
7899     selectedClass: 'active',
7900     
7901     /**
7902      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7903      */
7904     shadow:'sides',
7905     /**
7906      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7907      * anchor positions (defaults to 'tl-bl')
7908      */
7909     listAlign: 'tl-bl?',
7910     /**
7911      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7912      */
7913     maxHeight: 300,
7914     /**
7915      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7916      * query specified by the allQuery config option (defaults to 'query')
7917      */
7918     triggerAction: 'query',
7919     /**
7920      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7921      * (defaults to 4, does not apply if editable = false)
7922      */
7923     minChars : 4,
7924     /**
7925      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7926      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7927      */
7928     typeAhead: false,
7929     /**
7930      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7931      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7932      */
7933     queryDelay: 500,
7934     /**
7935      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7936      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7937      */
7938     pageSize: 0,
7939     /**
7940      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7941      * when editable = true (defaults to false)
7942      */
7943     selectOnFocus:false,
7944     /**
7945      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7946      */
7947     queryParam: 'query',
7948     /**
7949      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7950      * when mode = 'remote' (defaults to 'Loading...')
7951      */
7952     loadingText: 'Loading...',
7953     /**
7954      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7955      */
7956     resizable: false,
7957     /**
7958      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7959      */
7960     handleHeight : 8,
7961     /**
7962      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7963      * traditional select (defaults to true)
7964      */
7965     editable: true,
7966     /**
7967      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7968      */
7969     allQuery: '',
7970     /**
7971      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7972      */
7973     mode: 'remote',
7974     /**
7975      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7976      * listWidth has a higher value)
7977      */
7978     minListWidth : 70,
7979     /**
7980      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7981      * allow the user to set arbitrary text into the field (defaults to false)
7982      */
7983     forceSelection:false,
7984     /**
7985      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7986      * if typeAhead = true (defaults to 250)
7987      */
7988     typeAheadDelay : 250,
7989     /**
7990      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7991      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7992      */
7993     valueNotFoundText : undefined,
7994     /**
7995      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7996      */
7997     blockFocus : false,
7998     
7999     /**
8000      * @cfg {Boolean} disableClear Disable showing of clear button.
8001      */
8002     disableClear : false,
8003     /**
8004      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
8005      */
8006     alwaysQuery : false,
8007     
8008     /**
8009      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
8010      */
8011     multiple : false,
8012     
8013     //private
8014     addicon : false,
8015     editicon: false,
8016     
8017     page: 0,
8018     hasQuery: false,
8019     append: false,
8020     loadNext: false,
8021     item: [],
8022     
8023     // element that contains real text value.. (when hidden is used..)
8024      
8025     // private
8026     initEvents: function(){
8027         
8028         if (!this.store) {
8029             throw "can not find store for combo";
8030         }
8031         this.store = Roo.factory(this.store, Roo.data);
8032         
8033         
8034         
8035         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8036         
8037         
8038         if(this.hiddenName){
8039             
8040             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8041             
8042             this.hiddenField.dom.value =
8043                 this.hiddenValue !== undefined ? this.hiddenValue :
8044                 this.value !== undefined ? this.value : '';
8045
8046             // prevent input submission
8047             this.el.dom.removeAttribute('name');
8048             this.hiddenField.dom.setAttribute('name', this.hiddenName);
8049              
8050              
8051         }
8052         //if(Roo.isGecko){
8053         //    this.el.dom.setAttribute('autocomplete', 'off');
8054         //}
8055
8056         var cls = 'x-combo-list';
8057         this.list = this.el.select('ul.dropdown-menu',true).first();
8058
8059         //this.list = new Roo.Layer({
8060         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8061         //});
8062         
8063         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8064         this.list.setWidth(lw);
8065         
8066         this.list.on('mouseover', this.onViewOver, this);
8067         this.list.on('mousemove', this.onViewMove, this);
8068         
8069         this.list.on('scroll', this.onViewScroll, this);
8070         
8071         /*
8072         this.list.swallowEvent('mousewheel');
8073         this.assetHeight = 0;
8074
8075         if(this.title){
8076             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8077             this.assetHeight += this.header.getHeight();
8078         }
8079
8080         this.innerList = this.list.createChild({cls:cls+'-inner'});
8081         this.innerList.on('mouseover', this.onViewOver, this);
8082         this.innerList.on('mousemove', this.onViewMove, this);
8083         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8084         
8085         if(this.allowBlank && !this.pageSize && !this.disableClear){
8086             this.footer = this.list.createChild({cls:cls+'-ft'});
8087             this.pageTb = new Roo.Toolbar(this.footer);
8088            
8089         }
8090         if(this.pageSize){
8091             this.footer = this.list.createChild({cls:cls+'-ft'});
8092             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8093                     {pageSize: this.pageSize});
8094             
8095         }
8096         
8097         if (this.pageTb && this.allowBlank && !this.disableClear) {
8098             var _this = this;
8099             this.pageTb.add(new Roo.Toolbar.Fill(), {
8100                 cls: 'x-btn-icon x-btn-clear',
8101                 text: '&#160;',
8102                 handler: function()
8103                 {
8104                     _this.collapse();
8105                     _this.clearValue();
8106                     _this.onSelect(false, -1);
8107                 }
8108             });
8109         }
8110         if (this.footer) {
8111             this.assetHeight += this.footer.getHeight();
8112         }
8113         */
8114             
8115         if(!this.tpl){
8116             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8117         }
8118
8119         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8120             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8121         });
8122         //this.view.wrapEl.setDisplayed(false);
8123         this.view.on('click', this.onViewClick, this);
8124         
8125         
8126         
8127         this.store.on('beforeload', this.onBeforeLoad, this);
8128         this.store.on('load', this.onLoad, this);
8129         this.store.on('loadexception', this.onLoadException, this);
8130         /*
8131         if(this.resizable){
8132             this.resizer = new Roo.Resizable(this.list,  {
8133                pinned:true, handles:'se'
8134             });
8135             this.resizer.on('resize', function(r, w, h){
8136                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8137                 this.listWidth = w;
8138                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8139                 this.restrictHeight();
8140             }, this);
8141             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8142         }
8143         */
8144         if(!this.editable){
8145             this.editable = true;
8146             this.setEditable(false);
8147         }
8148         
8149         /*
8150         
8151         if (typeof(this.events.add.listeners) != 'undefined') {
8152             
8153             this.addicon = this.wrap.createChild(
8154                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8155        
8156             this.addicon.on('click', function(e) {
8157                 this.fireEvent('add', this);
8158             }, this);
8159         }
8160         if (typeof(this.events.edit.listeners) != 'undefined') {
8161             
8162             this.editicon = this.wrap.createChild(
8163                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8164             if (this.addicon) {
8165                 this.editicon.setStyle('margin-left', '40px');
8166             }
8167             this.editicon.on('click', function(e) {
8168                 
8169                 // we fire even  if inothing is selected..
8170                 this.fireEvent('edit', this, this.lastData );
8171                 
8172             }, this);
8173         }
8174         */
8175         
8176         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8177             "up" : function(e){
8178                 this.inKeyMode = true;
8179                 this.selectPrev();
8180             },
8181
8182             "down" : function(e){
8183                 if(!this.isExpanded()){
8184                     this.onTriggerClick();
8185                 }else{
8186                     this.inKeyMode = true;
8187                     this.selectNext();
8188                 }
8189             },
8190
8191             "enter" : function(e){
8192                 this.onViewClick();
8193                 //return true;
8194             },
8195
8196             "esc" : function(e){
8197                 this.collapse();
8198             },
8199
8200             "tab" : function(e){
8201                 this.collapse();
8202                 
8203                 if(this.fireEvent("specialkey", this, e)){
8204                     this.onViewClick(false);
8205                 }
8206                 
8207                 return true;
8208             },
8209
8210             scope : this,
8211
8212             doRelay : function(foo, bar, hname){
8213                 if(hname == 'down' || this.scope.isExpanded()){
8214                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8215                 }
8216                 return true;
8217             },
8218
8219             forceKeyDown: true
8220         });
8221         
8222         
8223         this.queryDelay = Math.max(this.queryDelay || 10,
8224                 this.mode == 'local' ? 10 : 250);
8225         
8226         
8227         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8228         
8229         if(this.typeAhead){
8230             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8231         }
8232         if(this.editable !== false){
8233             this.inputEl().on("keyup", this.onKeyUp, this);
8234         }
8235         if(this.forceSelection){
8236             this.on('blur', this.doForce, this);
8237         }
8238         
8239         if(this.multiple){
8240             this.choices = this.el.select('ul.select2-choices', true).first();
8241             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8242         }
8243     },
8244
8245     onDestroy : function(){
8246         if(this.view){
8247             this.view.setStore(null);
8248             this.view.el.removeAllListeners();
8249             this.view.el.remove();
8250             this.view.purgeListeners();
8251         }
8252         if(this.list){
8253             this.list.dom.innerHTML  = '';
8254         }
8255         if(this.store){
8256             this.store.un('beforeload', this.onBeforeLoad, this);
8257             this.store.un('load', this.onLoad, this);
8258             this.store.un('loadexception', this.onLoadException, this);
8259         }
8260         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8261     },
8262
8263     // private
8264     fireKey : function(e){
8265         if(e.isNavKeyPress() && !this.list.isVisible()){
8266             this.fireEvent("specialkey", this, e);
8267         }
8268     },
8269
8270     // private
8271     onResize: function(w, h){
8272 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8273 //        
8274 //        if(typeof w != 'number'){
8275 //            // we do not handle it!?!?
8276 //            return;
8277 //        }
8278 //        var tw = this.trigger.getWidth();
8279 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8280 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8281 //        var x = w - tw;
8282 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8283 //            
8284 //        //this.trigger.setStyle('left', x+'px');
8285 //        
8286 //        if(this.list && this.listWidth === undefined){
8287 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8288 //            this.list.setWidth(lw);
8289 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8290 //        }
8291         
8292     
8293         
8294     },
8295
8296     /**
8297      * Allow or prevent the user from directly editing the field text.  If false is passed,
8298      * the user will only be able to select from the items defined in the dropdown list.  This method
8299      * is the runtime equivalent of setting the 'editable' config option at config time.
8300      * @param {Boolean} value True to allow the user to directly edit the field text
8301      */
8302     setEditable : function(value){
8303         if(value == this.editable){
8304             return;
8305         }
8306         this.editable = value;
8307         if(!value){
8308             this.inputEl().dom.setAttribute('readOnly', true);
8309             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8310             this.inputEl().addClass('x-combo-noedit');
8311         }else{
8312             this.inputEl().dom.setAttribute('readOnly', false);
8313             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8314             this.inputEl().removeClass('x-combo-noedit');
8315         }
8316     },
8317
8318     // private
8319     
8320     onBeforeLoad : function(combo,opts){
8321         if(!this.hasFocus){
8322             return;
8323         }
8324          if (!opts.add) {
8325             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8326          }
8327         this.restrictHeight();
8328         this.selectedIndex = -1;
8329     },
8330
8331     // private
8332     onLoad : function(){
8333         
8334         this.hasQuery = false;
8335         
8336         if(!this.hasFocus){
8337             return;
8338         }
8339         
8340         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8341             this.loading.hide();
8342         }
8343         
8344         if(this.store.getCount() > 0){
8345             this.expand();
8346             this.restrictHeight();
8347             if(this.lastQuery == this.allQuery){
8348                 if(this.editable){
8349                     this.inputEl().dom.select();
8350                 }
8351                 if(!this.selectByValue(this.value, true)){
8352                     this.select(0, true);
8353                 }
8354             }else{
8355                 this.selectNext();
8356                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8357                     this.taTask.delay(this.typeAheadDelay);
8358                 }
8359             }
8360         }else{
8361             this.onEmptyResults();
8362         }
8363         
8364         //this.el.focus();
8365     },
8366     // private
8367     onLoadException : function()
8368     {
8369         this.hasQuery = false;
8370         
8371         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8372             this.loading.hide();
8373         }
8374         
8375         this.collapse();
8376         Roo.log(this.store.reader.jsonData);
8377         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8378             // fixme
8379             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8380         }
8381         
8382         
8383     },
8384     // private
8385     onTypeAhead : function(){
8386         if(this.store.getCount() > 0){
8387             var r = this.store.getAt(0);
8388             var newValue = r.data[this.displayField];
8389             var len = newValue.length;
8390             var selStart = this.getRawValue().length;
8391             
8392             if(selStart != len){
8393                 this.setRawValue(newValue);
8394                 this.selectText(selStart, newValue.length);
8395             }
8396         }
8397     },
8398
8399     // private
8400     onSelect : function(record, index){
8401         
8402         if(this.fireEvent('beforeselect', this, record, index) !== false){
8403         
8404             this.setFromData(index > -1 ? record.data : false);
8405             
8406             this.collapse();
8407             this.fireEvent('select', this, record, index);
8408         }
8409     },
8410
8411     /**
8412      * Returns the currently selected field value or empty string if no value is set.
8413      * @return {String} value The selected value
8414      */
8415     getValue : function(){
8416         
8417         if(this.multiple){
8418             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8419         }
8420         
8421         if(this.valueField){
8422             return typeof this.value != 'undefined' ? this.value : '';
8423         }else{
8424             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8425         }
8426     },
8427
8428     /**
8429      * Clears any text/value currently set in the field
8430      */
8431     clearValue : function(){
8432         if(this.hiddenField){
8433             this.hiddenField.dom.value = '';
8434         }
8435         this.value = '';
8436         this.setRawValue('');
8437         this.lastSelectionText = '';
8438         
8439     },
8440
8441     /**
8442      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8443      * will be displayed in the field.  If the value does not match the data value of an existing item,
8444      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8445      * Otherwise the field will be blank (although the value will still be set).
8446      * @param {String} value The value to match
8447      */
8448     setValue : function(v){
8449         if(this.multiple){
8450             this.syncValue();
8451             return;
8452         }
8453         
8454         var text = v;
8455         if(this.valueField){
8456             var r = this.findRecord(this.valueField, v);
8457             if(r){
8458                 text = r.data[this.displayField];
8459             }else if(this.valueNotFoundText !== undefined){
8460                 text = this.valueNotFoundText;
8461             }
8462         }
8463         this.lastSelectionText = text;
8464         if(this.hiddenField){
8465             this.hiddenField.dom.value = v;
8466         }
8467         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8468         this.value = v;
8469     },
8470     /**
8471      * @property {Object} the last set data for the element
8472      */
8473     
8474     lastData : false,
8475     /**
8476      * Sets the value of the field based on a object which is related to the record format for the store.
8477      * @param {Object} value the value to set as. or false on reset?
8478      */
8479     setFromData : function(o){
8480         
8481         if(this.multiple){
8482             this.addItem(o);
8483             return;
8484         }
8485             
8486         var dv = ''; // display value
8487         var vv = ''; // value value..
8488         this.lastData = o;
8489         if (this.displayField) {
8490             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8491         } else {
8492             // this is an error condition!!!
8493             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8494         }
8495         
8496         if(this.valueField){
8497             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8498         }
8499         
8500         if(this.hiddenField){
8501             this.hiddenField.dom.value = vv;
8502             
8503             this.lastSelectionText = dv;
8504             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8505             this.value = vv;
8506             return;
8507         }
8508         // no hidden field.. - we store the value in 'value', but still display
8509         // display field!!!!
8510         this.lastSelectionText = dv;
8511         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8512         this.value = vv;
8513         
8514         
8515     },
8516     // private
8517     reset : function(){
8518         // overridden so that last data is reset..
8519         this.setValue(this.originalValue);
8520         this.clearInvalid();
8521         this.lastData = false;
8522         if (this.view) {
8523             this.view.clearSelections();
8524         }
8525     },
8526     // private
8527     findRecord : function(prop, value){
8528         var record;
8529         if(this.store.getCount() > 0){
8530             this.store.each(function(r){
8531                 if(r.data[prop] == value){
8532                     record = r;
8533                     return false;
8534                 }
8535                 return true;
8536             });
8537         }
8538         return record;
8539     },
8540     
8541     getName: function()
8542     {
8543         // returns hidden if it's set..
8544         if (!this.rendered) {return ''};
8545         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8546         
8547     },
8548     // private
8549     onViewMove : function(e, t){
8550         this.inKeyMode = false;
8551     },
8552
8553     // private
8554     onViewOver : function(e, t){
8555         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8556             return;
8557         }
8558         var item = this.view.findItemFromChild(t);
8559         if(item){
8560             var index = this.view.indexOf(item);
8561             this.select(index, false);
8562         }
8563     },
8564
8565     // private
8566     onViewClick : function(doFocus)
8567     {
8568         var index = this.view.getSelectedIndexes()[0];
8569         var r = this.store.getAt(index);
8570         if(r){
8571             this.onSelect(r, index);
8572         }
8573         if(doFocus !== false && !this.blockFocus){
8574             this.inputEl().focus();
8575         }
8576     },
8577
8578     // private
8579     restrictHeight : function(){
8580         //this.innerList.dom.style.height = '';
8581         //var inner = this.innerList.dom;
8582         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8583         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8584         //this.list.beginUpdate();
8585         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8586         this.list.alignTo(this.inputEl(), this.listAlign);
8587         //this.list.endUpdate();
8588     },
8589
8590     // private
8591     onEmptyResults : function(){
8592         this.collapse();
8593     },
8594
8595     /**
8596      * Returns true if the dropdown list is expanded, else false.
8597      */
8598     isExpanded : function(){
8599         return this.list.isVisible();
8600     },
8601
8602     /**
8603      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
8609      */
8610     selectByValue : function(v, scrollIntoView){
8611         if(v !== undefined && v !== null){
8612             var r = this.findRecord(this.valueField || this.displayField, v);
8613             if(r){
8614                 this.select(this.store.indexOf(r), scrollIntoView);
8615                 return true;
8616             }
8617         }
8618         return false;
8619     },
8620
8621     /**
8622      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8623      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8624      * @param {Number} index The zero-based index of the list item to select
8625      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8626      * selected item if it is not currently in view (defaults to true)
8627      */
8628     select : function(index, scrollIntoView){
8629         this.selectedIndex = index;
8630         this.view.select(index);
8631         if(scrollIntoView !== false){
8632             var el = this.view.getNode(index);
8633             if(el){
8634                 //this.innerList.scrollChildIntoView(el, false);
8635                 
8636             }
8637         }
8638     },
8639
8640     // private
8641     selectNext : function(){
8642         var ct = this.store.getCount();
8643         if(ct > 0){
8644             if(this.selectedIndex == -1){
8645                 this.select(0);
8646             }else if(this.selectedIndex < ct-1){
8647                 this.select(this.selectedIndex+1);
8648             }
8649         }
8650     },
8651
8652     // private
8653     selectPrev : function(){
8654         var ct = this.store.getCount();
8655         if(ct > 0){
8656             if(this.selectedIndex == -1){
8657                 this.select(0);
8658             }else if(this.selectedIndex != 0){
8659                 this.select(this.selectedIndex-1);
8660             }
8661         }
8662     },
8663
8664     // private
8665     onKeyUp : function(e){
8666         if(this.editable !== false && !e.isSpecialKey()){
8667             this.lastKey = e.getKey();
8668             this.dqTask.delay(this.queryDelay);
8669         }
8670     },
8671
8672     // private
8673     validateBlur : function(){
8674         return !this.list || !this.list.isVisible();   
8675     },
8676
8677     // private
8678     initQuery : function(){
8679         this.doQuery(this.getRawValue());
8680     },
8681
8682     // private
8683     doForce : function(){
8684         if(this.el.dom.value.length > 0){
8685             this.el.dom.value =
8686                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8687              
8688         }
8689     },
8690
8691     /**
8692      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8693      * query allowing the query action to be canceled if needed.
8694      * @param {String} query The SQL query to execute
8695      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8696      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8697      * saved in the current store (defaults to false)
8698      */
8699     doQuery : function(q, forceAll){
8700         
8701         if(q === undefined || q === null){
8702             q = '';
8703         }
8704         var qe = {
8705             query: q,
8706             forceAll: forceAll,
8707             combo: this,
8708             cancel:false
8709         };
8710         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8711             return false;
8712         }
8713         q = qe.query;
8714         
8715         forceAll = qe.forceAll;
8716         if(forceAll === true || (q.length >= this.minChars)){
8717             
8718             this.hasQuery = true;
8719             
8720             if(this.lastQuery != q || this.alwaysQuery){
8721                 this.lastQuery = q;
8722                 if(this.mode == 'local'){
8723                     this.selectedIndex = -1;
8724                     if(forceAll){
8725                         this.store.clearFilter();
8726                     }else{
8727                         this.store.filter(this.displayField, q);
8728                     }
8729                     this.onLoad();
8730                 }else{
8731                     this.store.baseParams[this.queryParam] = q;
8732                     
8733                     var options = {params : this.getParams(q)};
8734                     
8735                     if(this.loadNext){
8736                         options.add = true;
8737                         options.params.start = this.page * this.pageSize;
8738                     }
8739                     
8740                     this.store.load(options);
8741                     this.expand();
8742                 }
8743             }else{
8744                 this.selectedIndex = -1;
8745                 this.onLoad();   
8746             }
8747         }
8748         
8749         this.loadNext = false;
8750     },
8751
8752     // private
8753     getParams : function(q){
8754         var p = {};
8755         //p[this.queryParam] = q;
8756         
8757         if(this.pageSize){
8758             p.start = 0;
8759             p.limit = this.pageSize;
8760         }
8761         return p;
8762     },
8763
8764     /**
8765      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8766      */
8767     collapse : function(){
8768         if(!this.isExpanded()){
8769             return;
8770         }
8771         
8772         this.list.hide();
8773         Roo.get(document).un('mousedown', this.collapseIf, this);
8774         Roo.get(document).un('mousewheel', this.collapseIf, this);
8775         if (!this.editable) {
8776             Roo.get(document).un('keydown', this.listKeyPress, this);
8777         }
8778         this.fireEvent('collapse', this);
8779     },
8780
8781     // private
8782     collapseIf : function(e){
8783         var in_combo  = e.within(this.el);
8784         var in_list =  e.within(this.list);
8785         
8786         if (in_combo || in_list) {
8787             //e.stopPropagation();
8788             return;
8789         }
8790
8791         this.collapse();
8792         
8793     },
8794
8795     /**
8796      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8797      */
8798     expand : function(){
8799        
8800         if(this.isExpanded() || !this.hasFocus){
8801             return;
8802         }
8803          Roo.log('expand');
8804         this.list.alignTo(this.inputEl(), this.listAlign);
8805         this.list.show();
8806         Roo.get(document).on('mousedown', this.collapseIf, this);
8807         Roo.get(document).on('mousewheel', this.collapseIf, this);
8808         if (!this.editable) {
8809             Roo.get(document).on('keydown', this.listKeyPress, this);
8810         }
8811         
8812         this.fireEvent('expand', this);
8813     },
8814
8815     // private
8816     // Implements the default empty TriggerField.onTriggerClick function
8817     onTriggerClick : function()
8818     {
8819         Roo.log('trigger click');
8820         
8821         if(this.disabled){
8822             return;
8823         }
8824         
8825         this.page = 0;
8826         this.loadNext = false;
8827         
8828         if(this.isExpanded()){
8829             this.collapse();
8830             if (!this.blockFocus) {
8831                 this.inputEl().focus();
8832             }
8833             
8834         }else {
8835             this.hasFocus = true;
8836             if(this.triggerAction == 'all') {
8837                 this.doQuery(this.allQuery, true);
8838             } else {
8839                 this.doQuery(this.getRawValue());
8840             }
8841             if (!this.blockFocus) {
8842                 this.inputEl().focus();
8843             }
8844         }
8845     },
8846     listKeyPress : function(e)
8847     {
8848         //Roo.log('listkeypress');
8849         // scroll to first matching element based on key pres..
8850         if (e.isSpecialKey()) {
8851             return false;
8852         }
8853         var k = String.fromCharCode(e.getKey()).toUpperCase();
8854         //Roo.log(k);
8855         var match  = false;
8856         var csel = this.view.getSelectedNodes();
8857         var cselitem = false;
8858         if (csel.length) {
8859             var ix = this.view.indexOf(csel[0]);
8860             cselitem  = this.store.getAt(ix);
8861             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8862                 cselitem = false;
8863             }
8864             
8865         }
8866         
8867         this.store.each(function(v) { 
8868             if (cselitem) {
8869                 // start at existing selection.
8870                 if (cselitem.id == v.id) {
8871                     cselitem = false;
8872                 }
8873                 return true;
8874             }
8875                 
8876             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8877                 match = this.store.indexOf(v);
8878                 return false;
8879             }
8880             return true;
8881         }, this);
8882         
8883         if (match === false) {
8884             return true; // no more action?
8885         }
8886         // scroll to?
8887         this.view.select(match);
8888         var sn = Roo.get(this.view.getSelectedNodes()[0])
8889         //sn.scrollIntoView(sn.dom.parentNode, false);
8890     },
8891     
8892     onViewScroll : function(e, t){
8893         
8894         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8895             return;
8896         }
8897         
8898         this.hasQuery = true;
8899         
8900         this.loading = this.list.select('.loading', true).first();
8901         
8902         if(this.loading === null){
8903             this.list.createChild({
8904                 tag: 'div',
8905                 cls: 'loading select2-more-results select2-active',
8906                 html: 'Loading more results...'
8907             })
8908             
8909             this.loading = this.list.select('.loading', true).first();
8910             
8911             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8912             
8913             this.loading.hide();
8914         }
8915         
8916         this.loading.show();
8917         
8918         var _combo = this;
8919         
8920         this.page++;
8921         this.loadNext = true;
8922         
8923         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8924         
8925         return;
8926     },
8927     
8928     addItem : function(o)
8929     {   
8930         var dv = ''; // display value
8931         
8932         if (this.displayField) {
8933             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8934         } else {
8935             // this is an error condition!!!
8936             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8937         }
8938         
8939         if(!dv.length){
8940             return;
8941         }
8942         
8943         var choice = this.choices.createChild({
8944             tag: 'li',
8945             cls: 'select2-search-choice',
8946             cn: [
8947                 {
8948                     tag: 'div',
8949                     html: dv
8950                 },
8951                 {
8952                     tag: 'a',
8953                     href: '#',
8954                     cls: 'select2-search-choice-close',
8955                     tabindex: '-1'
8956                 }
8957             ]
8958             
8959         }, this.searchField);
8960         
8961         var close = choice.select('a.select2-search-choice-close', true).first()
8962         
8963         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8964         
8965         this.item.push(o);
8966         this.lastData = o;
8967         
8968         this.syncValue();
8969         
8970         this.inputEl().dom.value = '';
8971         
8972     },
8973     
8974     onRemoveItem : function(e, _self, o)
8975     {
8976         Roo.log('remove item');
8977         var index = this.item.indexOf(o.data) * 1;
8978         
8979         if( index < 0){
8980             Roo.log('not this item?!');
8981             return;
8982         }
8983         
8984         this.item.splice(index, 1);
8985         o.item.remove();
8986         
8987         this.syncValue();
8988         
8989         this.fireEvent('remove', this);
8990         
8991     },
8992     
8993     syncValue : function()
8994     {
8995         if(!this.item.length){
8996             this.clearValue();
8997             return;
8998         }
8999             
9000         var value = [];
9001         var _this = this;
9002         Roo.each(this.item, function(i){
9003             if(_this.valueField){
9004                 value.push(i[_this.valueField]);
9005                 return;
9006             }
9007
9008             value.push(i);
9009         });
9010
9011         this.value = value.join(',');
9012
9013         if(this.hiddenField){
9014             this.hiddenField.dom.value = this.value;
9015         }
9016     },
9017     
9018     clearItem : function()
9019     {
9020         if(!this.multiple){
9021             return;
9022         }
9023         
9024         this.item = [];
9025         
9026         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9027            c.remove();
9028         });
9029         
9030         this.syncValue();
9031     }
9032     
9033     
9034
9035     /** 
9036     * @cfg {Boolean} grow 
9037     * @hide 
9038     */
9039     /** 
9040     * @cfg {Number} growMin 
9041     * @hide 
9042     */
9043     /** 
9044     * @cfg {Number} growMax 
9045     * @hide 
9046     */
9047     /**
9048      * @hide
9049      * @method autoSize
9050      */
9051 });
9052 /*
9053  * Based on:
9054  * Ext JS Library 1.1.1
9055  * Copyright(c) 2006-2007, Ext JS, LLC.
9056  *
9057  * Originally Released Under LGPL - original licence link has changed is not relivant.
9058  *
9059  * Fork - LGPL
9060  * <script type="text/javascript">
9061  */
9062
9063 /**
9064  * @class Roo.View
9065  * @extends Roo.util.Observable
9066  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9067  * This class also supports single and multi selection modes. <br>
9068  * Create a data model bound view:
9069  <pre><code>
9070  var store = new Roo.data.Store(...);
9071
9072  var view = new Roo.View({
9073     el : "my-element",
9074     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9075  
9076     singleSelect: true,
9077     selectedClass: "ydataview-selected",
9078     store: store
9079  });
9080
9081  // listen for node click?
9082  view.on("click", function(vw, index, node, e){
9083  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9084  });
9085
9086  // load XML data
9087  dataModel.load("foobar.xml");
9088  </code></pre>
9089  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9090  * <br><br>
9091  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9092  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9093  * 
9094  * Note: old style constructor is still suported (container, template, config)
9095  * 
9096  * @constructor
9097  * Create a new View
9098  * @param {Object} config The config object
9099  * 
9100  */
9101 Roo.View = function(config, depreciated_tpl, depreciated_config){
9102     
9103     if (typeof(depreciated_tpl) == 'undefined') {
9104         // new way.. - universal constructor.
9105         Roo.apply(this, config);
9106         this.el  = Roo.get(this.el);
9107     } else {
9108         // old format..
9109         this.el  = Roo.get(config);
9110         this.tpl = depreciated_tpl;
9111         Roo.apply(this, depreciated_config);
9112     }
9113     this.wrapEl  = this.el.wrap().wrap();
9114     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9115     
9116     
9117     if(typeof(this.tpl) == "string"){
9118         this.tpl = new Roo.Template(this.tpl);
9119     } else {
9120         // support xtype ctors..
9121         this.tpl = new Roo.factory(this.tpl, Roo);
9122     }
9123     
9124     
9125     this.tpl.compile();
9126    
9127   
9128     
9129      
9130     /** @private */
9131     this.addEvents({
9132         /**
9133          * @event beforeclick
9134          * Fires before a click is processed. Returns false to cancel the default action.
9135          * @param {Roo.View} this
9136          * @param {Number} index The index of the target node
9137          * @param {HTMLElement} node The target node
9138          * @param {Roo.EventObject} e The raw event object
9139          */
9140             "beforeclick" : true,
9141         /**
9142          * @event click
9143          * Fires when a template node is clicked.
9144          * @param {Roo.View} this
9145          * @param {Number} index The index of the target node
9146          * @param {HTMLElement} node The target node
9147          * @param {Roo.EventObject} e The raw event object
9148          */
9149             "click" : true,
9150         /**
9151          * @event dblclick
9152          * Fires when a template node is double clicked.
9153          * @param {Roo.View} this
9154          * @param {Number} index The index of the target node
9155          * @param {HTMLElement} node The target node
9156          * @param {Roo.EventObject} e The raw event object
9157          */
9158             "dblclick" : true,
9159         /**
9160          * @event contextmenu
9161          * Fires when a template node is right clicked.
9162          * @param {Roo.View} this
9163          * @param {Number} index The index of the target node
9164          * @param {HTMLElement} node The target node
9165          * @param {Roo.EventObject} e The raw event object
9166          */
9167             "contextmenu" : true,
9168         /**
9169          * @event selectionchange
9170          * Fires when the selected nodes change.
9171          * @param {Roo.View} this
9172          * @param {Array} selections Array of the selected nodes
9173          */
9174             "selectionchange" : true,
9175     
9176         /**
9177          * @event beforeselect
9178          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9179          * @param {Roo.View} this
9180          * @param {HTMLElement} node The node to be selected
9181          * @param {Array} selections Array of currently selected nodes
9182          */
9183             "beforeselect" : true,
9184         /**
9185          * @event preparedata
9186          * Fires on every row to render, to allow you to change the data.
9187          * @param {Roo.View} this
9188          * @param {Object} data to be rendered (change this)
9189          */
9190           "preparedata" : true
9191           
9192           
9193         });
9194
9195
9196
9197     this.el.on({
9198         "click": this.onClick,
9199         "dblclick": this.onDblClick,
9200         "contextmenu": this.onContextMenu,
9201         scope:this
9202     });
9203
9204     this.selections = [];
9205     this.nodes = [];
9206     this.cmp = new Roo.CompositeElementLite([]);
9207     if(this.store){
9208         this.store = Roo.factory(this.store, Roo.data);
9209         this.setStore(this.store, true);
9210     }
9211     
9212     if ( this.footer && this.footer.xtype) {
9213            
9214          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9215         
9216         this.footer.dataSource = this.store
9217         this.footer.container = fctr;
9218         this.footer = Roo.factory(this.footer, Roo);
9219         fctr.insertFirst(this.el);
9220         
9221         // this is a bit insane - as the paging toolbar seems to detach the el..
9222 //        dom.parentNode.parentNode.parentNode
9223          // they get detached?
9224     }
9225     
9226     
9227     Roo.View.superclass.constructor.call(this);
9228     
9229     
9230 };
9231
9232 Roo.extend(Roo.View, Roo.util.Observable, {
9233     
9234      /**
9235      * @cfg {Roo.data.Store} store Data store to load data from.
9236      */
9237     store : false,
9238     
9239     /**
9240      * @cfg {String|Roo.Element} el The container element.
9241      */
9242     el : '',
9243     
9244     /**
9245      * @cfg {String|Roo.Template} tpl The template used by this View 
9246      */
9247     tpl : false,
9248     /**
9249      * @cfg {String} dataName the named area of the template to use as the data area
9250      *                          Works with domtemplates roo-name="name"
9251      */
9252     dataName: false,
9253     /**
9254      * @cfg {String} selectedClass The css class to add to selected nodes
9255      */
9256     selectedClass : "x-view-selected",
9257      /**
9258      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9259      */
9260     emptyText : "",
9261     
9262     /**
9263      * @cfg {String} text to display on mask (default Loading)
9264      */
9265     mask : false,
9266     /**
9267      * @cfg {Boolean} multiSelect Allow multiple selection
9268      */
9269     multiSelect : false,
9270     /**
9271      * @cfg {Boolean} singleSelect Allow single selection
9272      */
9273     singleSelect:  false,
9274     
9275     /**
9276      * @cfg {Boolean} toggleSelect - selecting 
9277      */
9278     toggleSelect : false,
9279     
9280     /**
9281      * Returns the element this view is bound to.
9282      * @return {Roo.Element}
9283      */
9284     getEl : function(){
9285         return this.wrapEl;
9286     },
9287     
9288     
9289
9290     /**
9291      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9292      */
9293     refresh : function(){
9294         Roo.log('refresh');
9295         var t = this.tpl;
9296         
9297         // if we are using something like 'domtemplate', then
9298         // the what gets used is:
9299         // t.applySubtemplate(NAME, data, wrapping data..)
9300         // the outer template then get' applied with
9301         //     the store 'extra data'
9302         // and the body get's added to the
9303         //      roo-name="data" node?
9304         //      <span class='roo-tpl-{name}'></span> ?????
9305         
9306         
9307         
9308         this.clearSelections();
9309         this.el.update("");
9310         var html = [];
9311         var records = this.store.getRange();
9312         if(records.length < 1) {
9313             
9314             // is this valid??  = should it render a template??
9315             
9316             this.el.update(this.emptyText);
9317             return;
9318         }
9319         var el = this.el;
9320         if (this.dataName) {
9321             this.el.update(t.apply(this.store.meta)); //????
9322             el = this.el.child('.roo-tpl-' + this.dataName);
9323         }
9324         
9325         for(var i = 0, len = records.length; i < len; i++){
9326             var data = this.prepareData(records[i].data, i, records[i]);
9327             this.fireEvent("preparedata", this, data, i, records[i]);
9328             html[html.length] = Roo.util.Format.trim(
9329                 this.dataName ?
9330                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9331                     t.apply(data)
9332             );
9333         }
9334         
9335         
9336         
9337         el.update(html.join(""));
9338         this.nodes = el.dom.childNodes;
9339         this.updateIndexes(0);
9340     },
9341     
9342
9343     /**
9344      * Function to override to reformat the data that is sent to
9345      * the template for each node.
9346      * DEPRICATED - use the preparedata event handler.
9347      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9348      * a JSON object for an UpdateManager bound view).
9349      */
9350     prepareData : function(data, index, record)
9351     {
9352         this.fireEvent("preparedata", this, data, index, record);
9353         return data;
9354     },
9355
9356     onUpdate : function(ds, record){
9357          Roo.log('on update');   
9358         this.clearSelections();
9359         var index = this.store.indexOf(record);
9360         var n = this.nodes[index];
9361         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9362         n.parentNode.removeChild(n);
9363         this.updateIndexes(index, index);
9364     },
9365
9366     
9367     
9368 // --------- FIXME     
9369     onAdd : function(ds, records, index)
9370     {
9371         Roo.log(['on Add', ds, records, index] );        
9372         this.clearSelections();
9373         if(this.nodes.length == 0){
9374             this.refresh();
9375             return;
9376         }
9377         var n = this.nodes[index];
9378         for(var i = 0, len = records.length; i < len; i++){
9379             var d = this.prepareData(records[i].data, i, records[i]);
9380             if(n){
9381                 this.tpl.insertBefore(n, d);
9382             }else{
9383                 
9384                 this.tpl.append(this.el, d);
9385             }
9386         }
9387         this.updateIndexes(index);
9388     },
9389
9390     onRemove : function(ds, record, index){
9391         Roo.log('onRemove');
9392         this.clearSelections();
9393         var el = this.dataName  ?
9394             this.el.child('.roo-tpl-' + this.dataName) :
9395             this.el; 
9396         
9397         el.dom.removeChild(this.nodes[index]);
9398         this.updateIndexes(index);
9399     },
9400
9401     /**
9402      * Refresh an individual node.
9403      * @param {Number} index
9404      */
9405     refreshNode : function(index){
9406         this.onUpdate(this.store, this.store.getAt(index));
9407     },
9408
9409     updateIndexes : function(startIndex, endIndex){
9410         var ns = this.nodes;
9411         startIndex = startIndex || 0;
9412         endIndex = endIndex || ns.length - 1;
9413         for(var i = startIndex; i <= endIndex; i++){
9414             ns[i].nodeIndex = i;
9415         }
9416     },
9417
9418     /**
9419      * Changes the data store this view uses and refresh the view.
9420      * @param {Store} store
9421      */
9422     setStore : function(store, initial){
9423         if(!initial && this.store){
9424             this.store.un("datachanged", this.refresh);
9425             this.store.un("add", this.onAdd);
9426             this.store.un("remove", this.onRemove);
9427             this.store.un("update", this.onUpdate);
9428             this.store.un("clear", this.refresh);
9429             this.store.un("beforeload", this.onBeforeLoad);
9430             this.store.un("load", this.onLoad);
9431             this.store.un("loadexception", this.onLoad);
9432         }
9433         if(store){
9434           
9435             store.on("datachanged", this.refresh, this);
9436             store.on("add", this.onAdd, this);
9437             store.on("remove", this.onRemove, this);
9438             store.on("update", this.onUpdate, this);
9439             store.on("clear", this.refresh, this);
9440             store.on("beforeload", this.onBeforeLoad, this);
9441             store.on("load", this.onLoad, this);
9442             store.on("loadexception", this.onLoad, this);
9443         }
9444         
9445         if(store){
9446             this.refresh();
9447         }
9448     },
9449     /**
9450      * onbeforeLoad - masks the loading area.
9451      *
9452      */
9453     onBeforeLoad : function(store,opts)
9454     {
9455          Roo.log('onBeforeLoad');   
9456         if (!opts.add) {
9457             this.el.update("");
9458         }
9459         this.el.mask(this.mask ? this.mask : "Loading" ); 
9460     },
9461     onLoad : function ()
9462     {
9463         this.el.unmask();
9464     },
9465     
9466
9467     /**
9468      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9469      * @param {HTMLElement} node
9470      * @return {HTMLElement} The template node
9471      */
9472     findItemFromChild : function(node){
9473         var el = this.dataName  ?
9474             this.el.child('.roo-tpl-' + this.dataName,true) :
9475             this.el.dom; 
9476         
9477         if(!node || node.parentNode == el){
9478                     return node;
9479             }
9480             var p = node.parentNode;
9481             while(p && p != el){
9482             if(p.parentNode == el){
9483                 return p;
9484             }
9485             p = p.parentNode;
9486         }
9487             return null;
9488     },
9489
9490     /** @ignore */
9491     onClick : function(e){
9492         var item = this.findItemFromChild(e.getTarget());
9493         if(item){
9494             var index = this.indexOf(item);
9495             if(this.onItemClick(item, index, e) !== false){
9496                 this.fireEvent("click", this, index, item, e);
9497             }
9498         }else{
9499             this.clearSelections();
9500         }
9501     },
9502
9503     /** @ignore */
9504     onContextMenu : function(e){
9505         var item = this.findItemFromChild(e.getTarget());
9506         if(item){
9507             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9508         }
9509     },
9510
9511     /** @ignore */
9512     onDblClick : function(e){
9513         var item = this.findItemFromChild(e.getTarget());
9514         if(item){
9515             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9516         }
9517     },
9518
9519     onItemClick : function(item, index, e)
9520     {
9521         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9522             return false;
9523         }
9524         if (this.toggleSelect) {
9525             var m = this.isSelected(item) ? 'unselect' : 'select';
9526             Roo.log(m);
9527             var _t = this;
9528             _t[m](item, true, false);
9529             return true;
9530         }
9531         if(this.multiSelect || this.singleSelect){
9532             if(this.multiSelect && e.shiftKey && this.lastSelection){
9533                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9534             }else{
9535                 this.select(item, this.multiSelect && e.ctrlKey);
9536                 this.lastSelection = item;
9537             }
9538             e.preventDefault();
9539         }
9540         return true;
9541     },
9542
9543     /**
9544      * Get the number of selected nodes.
9545      * @return {Number}
9546      */
9547     getSelectionCount : function(){
9548         return this.selections.length;
9549     },
9550
9551     /**
9552      * Get the currently selected nodes.
9553      * @return {Array} An array of HTMLElements
9554      */
9555     getSelectedNodes : function(){
9556         return this.selections;
9557     },
9558
9559     /**
9560      * Get the indexes of the selected nodes.
9561      * @return {Array}
9562      */
9563     getSelectedIndexes : function(){
9564         var indexes = [], s = this.selections;
9565         for(var i = 0, len = s.length; i < len; i++){
9566             indexes.push(s[i].nodeIndex);
9567         }
9568         return indexes;
9569     },
9570
9571     /**
9572      * Clear all selections
9573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9574      */
9575     clearSelections : function(suppressEvent){
9576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9577             this.cmp.elements = this.selections;
9578             this.cmp.removeClass(this.selectedClass);
9579             this.selections = [];
9580             if(!suppressEvent){
9581                 this.fireEvent("selectionchange", this, this.selections);
9582             }
9583         }
9584     },
9585
9586     /**
9587      * Returns true if the passed node is selected
9588      * @param {HTMLElement/Number} node The node or node index
9589      * @return {Boolean}
9590      */
9591     isSelected : function(node){
9592         var s = this.selections;
9593         if(s.length < 1){
9594             return false;
9595         }
9596         node = this.getNode(node);
9597         return s.indexOf(node) !== -1;
9598     },
9599
9600     /**
9601      * Selects nodes.
9602      * @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
9603      * @param {Boolean} keepExisting (optional) true to keep existing selections
9604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9605      */
9606     select : function(nodeInfo, keepExisting, suppressEvent){
9607         if(nodeInfo instanceof Array){
9608             if(!keepExisting){
9609                 this.clearSelections(true);
9610             }
9611             for(var i = 0, len = nodeInfo.length; i < len; i++){
9612                 this.select(nodeInfo[i], true, true);
9613             }
9614             return;
9615         } 
9616         var node = this.getNode(nodeInfo);
9617         if(!node || this.isSelected(node)){
9618             return; // already selected.
9619         }
9620         if(!keepExisting){
9621             this.clearSelections(true);
9622         }
9623         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9624             Roo.fly(node).addClass(this.selectedClass);
9625             this.selections.push(node);
9626             if(!suppressEvent){
9627                 this.fireEvent("selectionchange", this, this.selections);
9628             }
9629         }
9630         
9631         
9632     },
9633       /**
9634      * Unselects nodes.
9635      * @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
9636      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9637      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9638      */
9639     unselect : function(nodeInfo, keepExisting, suppressEvent)
9640     {
9641         if(nodeInfo instanceof Array){
9642             Roo.each(this.selections, function(s) {
9643                 this.unselect(s, nodeInfo);
9644             }, this);
9645             return;
9646         }
9647         var node = this.getNode(nodeInfo);
9648         if(!node || !this.isSelected(node)){
9649             Roo.log("not selected");
9650             return; // not selected.
9651         }
9652         // fireevent???
9653         var ns = [];
9654         Roo.each(this.selections, function(s) {
9655             if (s == node ) {
9656                 Roo.fly(node).removeClass(this.selectedClass);
9657
9658                 return;
9659             }
9660             ns.push(s);
9661         },this);
9662         
9663         this.selections= ns;
9664         this.fireEvent("selectionchange", this, this.selections);
9665     },
9666
9667     /**
9668      * Gets a template node.
9669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9670      * @return {HTMLElement} The node or null if it wasn't found
9671      */
9672     getNode : function(nodeInfo){
9673         if(typeof nodeInfo == "string"){
9674             return document.getElementById(nodeInfo);
9675         }else if(typeof nodeInfo == "number"){
9676             return this.nodes[nodeInfo];
9677         }
9678         return nodeInfo;
9679     },
9680
9681     /**
9682      * Gets a range template nodes.
9683      * @param {Number} startIndex
9684      * @param {Number} endIndex
9685      * @return {Array} An array of nodes
9686      */
9687     getNodes : function(start, end){
9688         var ns = this.nodes;
9689         start = start || 0;
9690         end = typeof end == "undefined" ? ns.length - 1 : end;
9691         var nodes = [];
9692         if(start <= end){
9693             for(var i = start; i <= end; i++){
9694                 nodes.push(ns[i]);
9695             }
9696         } else{
9697             for(var i = start; i >= end; i--){
9698                 nodes.push(ns[i]);
9699             }
9700         }
9701         return nodes;
9702     },
9703
9704     /**
9705      * Finds the index of the passed node
9706      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9707      * @return {Number} The index of the node or -1
9708      */
9709     indexOf : function(node){
9710         node = this.getNode(node);
9711         if(typeof node.nodeIndex == "number"){
9712             return node.nodeIndex;
9713         }
9714         var ns = this.nodes;
9715         for(var i = 0, len = ns.length; i < len; i++){
9716             if(ns[i] == node){
9717                 return i;
9718             }
9719         }
9720         return -1;
9721     }
9722 });
9723 /*
9724  * - LGPL
9725  *
9726  * based on jquery fullcalendar
9727  * 
9728  */
9729
9730 Roo.bootstrap = Roo.bootstrap || {};
9731 /**
9732  * @class Roo.bootstrap.Calendar
9733  * @extends Roo.bootstrap.Component
9734  * Bootstrap Calendar class
9735  * @cfg {Boolean} loadMask (true|false) default false
9736     
9737  * @constructor
9738  * Create a new Container
9739  * @param {Object} config The config object
9740  */
9741
9742
9743
9744 Roo.bootstrap.Calendar = function(config){
9745     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9746      this.addEvents({
9747         /**
9748              * @event select
9749              * Fires when a date is selected
9750              * @param {DatePicker} this
9751              * @param {Date} date The selected date
9752              */
9753         'select': true,
9754         /**
9755              * @event monthchange
9756              * Fires when the displayed month changes 
9757              * @param {DatePicker} this
9758              * @param {Date} date The selected month
9759              */
9760         'monthchange': true,
9761         /**
9762              * @event evententer
9763              * Fires when mouse over an event
9764              * @param {Calendar} this
9765              * @param {event} Event
9766              */
9767         'evententer': true,
9768         /**
9769              * @event eventleave
9770              * Fires when the mouse leaves an
9771              * @param {Calendar} this
9772              * @param {event}
9773              */
9774         'eventleave': true,
9775         /**
9776              * @event eventclick
9777              * Fires when the mouse click an
9778              * @param {Calendar} this
9779              * @param {event}
9780              */
9781         'eventclick': true
9782         
9783     });
9784
9785 };
9786
9787 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9788     
9789      /**
9790      * @cfg {Number} startDay
9791      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9792      */
9793     startDay : 0,
9794     
9795     loadMask : false,
9796       
9797     getAutoCreate : function(){
9798         
9799         
9800         var fc_button = function(name, corner, style, content ) {
9801             return Roo.apply({},{
9802                 tag : 'span',
9803                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9804                          (corner.length ?
9805                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9806                             ''
9807                         ),
9808                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9809                 unselectable: 'on'
9810             });
9811         };
9812         
9813         var header = {
9814             tag : 'table',
9815             cls : 'fc-header',
9816             style : 'width:100%',
9817             cn : [
9818                 {
9819                     tag: 'tr',
9820                     cn : [
9821                         {
9822                             tag : 'td',
9823                             cls : 'fc-header-left',
9824                             cn : [
9825                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9826                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9827                                 { tag: 'span', cls: 'fc-header-space' },
9828                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9829                                 
9830                                 
9831                             ]
9832                         },
9833                         
9834                         {
9835                             tag : 'td',
9836                             cls : 'fc-header-center',
9837                             cn : [
9838                                 {
9839                                     tag: 'span',
9840                                     cls: 'fc-header-title',
9841                                     cn : {
9842                                         tag: 'H2',
9843                                         html : 'month / year'
9844                                     }
9845                                 }
9846                                 
9847                             ]
9848                         },
9849                         {
9850                             tag : 'td',
9851                             cls : 'fc-header-right',
9852                             cn : [
9853                           /*      fc_button('month', 'left', '', 'month' ),
9854                                 fc_button('week', '', '', 'week' ),
9855                                 fc_button('day', 'right', '', 'day' )
9856                             */    
9857                                 
9858                             ]
9859                         }
9860                         
9861                     ]
9862                 }
9863             ]
9864         };
9865         
9866        
9867         var cal_heads = function() {
9868             var ret = [];
9869             // fixme - handle this.
9870             
9871             for (var i =0; i < Date.dayNames.length; i++) {
9872                 var d = Date.dayNames[i];
9873                 ret.push({
9874                     tag: 'th',
9875                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9876                     html : d.substring(0,3)
9877                 });
9878                 
9879             }
9880             ret[0].cls += ' fc-first';
9881             ret[6].cls += ' fc-last';
9882             return ret;
9883         };
9884         var cal_cell = function(n) {
9885             return  {
9886                 tag: 'td',
9887                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9888                 cn : [
9889                     {
9890                         cn : [
9891                             {
9892                                 cls: 'fc-day-number',
9893                                 html: 'D'
9894                             },
9895                             {
9896                                 cls: 'fc-day-content',
9897                              
9898                                 cn : [
9899                                      {
9900                                         style: 'position: relative;' // height: 17px;
9901                                     }
9902                                 ]
9903                             }
9904                             
9905                             
9906                         ]
9907                     }
9908                 ]
9909                 
9910             }
9911         };
9912         var cal_rows = function() {
9913             
9914             var ret = []
9915             for (var r = 0; r < 6; r++) {
9916                 var row= {
9917                     tag : 'tr',
9918                     cls : 'fc-week',
9919                     cn : []
9920                 };
9921                 
9922                 for (var i =0; i < Date.dayNames.length; i++) {
9923                     var d = Date.dayNames[i];
9924                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9925
9926                 }
9927                 row.cn[0].cls+=' fc-first';
9928                 row.cn[0].cn[0].style = 'min-height:90px';
9929                 row.cn[6].cls+=' fc-last';
9930                 ret.push(row);
9931                 
9932             }
9933             ret[0].cls += ' fc-first';
9934             ret[4].cls += ' fc-prev-last';
9935             ret[5].cls += ' fc-last';
9936             return ret;
9937             
9938         };
9939         
9940         var cal_table = {
9941             tag: 'table',
9942             cls: 'fc-border-separate',
9943             style : 'width:100%',
9944             cellspacing  : 0,
9945             cn : [
9946                 { 
9947                     tag: 'thead',
9948                     cn : [
9949                         { 
9950                             tag: 'tr',
9951                             cls : 'fc-first fc-last',
9952                             cn : cal_heads()
9953                         }
9954                     ]
9955                 },
9956                 { 
9957                     tag: 'tbody',
9958                     cn : cal_rows()
9959                 }
9960                   
9961             ]
9962         };
9963          
9964          var cfg = {
9965             cls : 'fc fc-ltr',
9966             cn : [
9967                 header,
9968                 {
9969                     cls : 'fc-content',
9970                     style : "position: relative;",
9971                     cn : [
9972                         {
9973                             cls : 'fc-view fc-view-month fc-grid',
9974                             style : 'position: relative',
9975                             unselectable : 'on',
9976                             cn : [
9977                                 {
9978                                     cls : 'fc-event-container',
9979                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9980                                 },
9981                                 cal_table
9982                             ]
9983                         }
9984                     ]
9985     
9986                 }
9987            ] 
9988             
9989         };
9990         
9991          
9992         
9993         return cfg;
9994     },
9995     
9996     
9997     initEvents : function()
9998     {
9999         if(!this.store){
10000             throw "can not find store for calendar";
10001         }
10002         
10003         var mark = {
10004             tag: "div",
10005             cls:"x-dlg-mask",
10006             style: "text-align:center",
10007             cn: [
10008                 {
10009                     tag: "div",
10010                     style: "background-color:white;width:50%;margin:250 auto",
10011                     cn: [
10012                         {
10013                             tag: "img",
10014                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10015                         },
10016                         {
10017                             tag: "span",
10018                             html: "Loading"
10019                         }
10020                         
10021                     ]
10022                 }
10023             ]
10024         }
10025         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10026         
10027         var size = this.el.select('.fc-content', true).first().getSize();
10028         this.maskEl.setSize(size.width, size.height);
10029         this.maskEl.enableDisplayMode("block");
10030         if(!this.loadMask){
10031             this.maskEl.hide();
10032         }
10033         
10034         this.store = Roo.factory(this.store, Roo.data);
10035         this.store.on('load', this.onLoad, this);
10036         this.store.on('beforeload', this.onBeforeLoad, this);
10037         
10038         this.resize();
10039         
10040         this.cells = this.el.select('.fc-day',true);
10041         //Roo.log(this.cells);
10042         this.textNodes = this.el.query('.fc-day-number');
10043         this.cells.addClassOnOver('fc-state-hover');
10044         
10045         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10046         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10047         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10048         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10049         
10050         this.on('monthchange', this.onMonthChange, this);
10051         
10052         this.update(new Date().clearTime());
10053     },
10054     
10055     resize : function() {
10056         var sz  = this.el.getSize();
10057         
10058         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10059         this.el.select('.fc-day-content div',true).setHeight(34);
10060     },
10061     
10062     
10063     // private
10064     showPrevMonth : function(e){
10065         this.update(this.activeDate.add("mo", -1));
10066     },
10067     showToday : function(e){
10068         this.update(new Date().clearTime());
10069     },
10070     // private
10071     showNextMonth : function(e){
10072         this.update(this.activeDate.add("mo", 1));
10073     },
10074
10075     // private
10076     showPrevYear : function(){
10077         this.update(this.activeDate.add("y", -1));
10078     },
10079
10080     // private
10081     showNextYear : function(){
10082         this.update(this.activeDate.add("y", 1));
10083     },
10084
10085     
10086    // private
10087     update : function(date)
10088     {
10089         var vd = this.activeDate;
10090         this.activeDate = date;
10091 //        if(vd && this.el){
10092 //            var t = date.getTime();
10093 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10094 //                Roo.log('using add remove');
10095 //                
10096 //                this.fireEvent('monthchange', this, date);
10097 //                
10098 //                this.cells.removeClass("fc-state-highlight");
10099 //                this.cells.each(function(c){
10100 //                   if(c.dateValue == t){
10101 //                       c.addClass("fc-state-highlight");
10102 //                       setTimeout(function(){
10103 //                            try{c.dom.firstChild.focus();}catch(e){}
10104 //                       }, 50);
10105 //                       return false;
10106 //                   }
10107 //                   return true;
10108 //                });
10109 //                return;
10110 //            }
10111 //        }
10112         
10113         var days = date.getDaysInMonth();
10114         
10115         var firstOfMonth = date.getFirstDateOfMonth();
10116         var startingPos = firstOfMonth.getDay()-this.startDay;
10117         
10118         if(startingPos < this.startDay){
10119             startingPos += 7;
10120         }
10121         
10122         var pm = date.add(Date.MONTH, -1);
10123         var prevStart = pm.getDaysInMonth()-startingPos;
10124 //        
10125         this.cells = this.el.select('.fc-day',true);
10126         this.textNodes = this.el.query('.fc-day-number');
10127         this.cells.addClassOnOver('fc-state-hover');
10128         
10129         var cells = this.cells.elements;
10130         var textEls = this.textNodes;
10131         
10132         Roo.each(cells, function(cell){
10133             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10134         });
10135         
10136         days += startingPos;
10137
10138         // convert everything to numbers so it's fast
10139         var day = 86400000;
10140         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10141         //Roo.log(d);
10142         //Roo.log(pm);
10143         //Roo.log(prevStart);
10144         
10145         var today = new Date().clearTime().getTime();
10146         var sel = date.clearTime().getTime();
10147         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10148         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10149         var ddMatch = this.disabledDatesRE;
10150         var ddText = this.disabledDatesText;
10151         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10152         var ddaysText = this.disabledDaysText;
10153         var format = this.format;
10154         
10155         var setCellClass = function(cal, cell){
10156             
10157             //Roo.log('set Cell Class');
10158             cell.title = "";
10159             var t = d.getTime();
10160             
10161             //Roo.log(d);
10162             
10163             cell.dateValue = t;
10164             if(t == today){
10165                 cell.className += " fc-today";
10166                 cell.className += " fc-state-highlight";
10167                 cell.title = cal.todayText;
10168             }
10169             if(t == sel){
10170                 // disable highlight in other month..
10171                 //cell.className += " fc-state-highlight";
10172                 
10173             }
10174             // disabling
10175             if(t < min) {
10176                 cell.className = " fc-state-disabled";
10177                 cell.title = cal.minText;
10178                 return;
10179             }
10180             if(t > max) {
10181                 cell.className = " fc-state-disabled";
10182                 cell.title = cal.maxText;
10183                 return;
10184             }
10185             if(ddays){
10186                 if(ddays.indexOf(d.getDay()) != -1){
10187                     cell.title = ddaysText;
10188                     cell.className = " fc-state-disabled";
10189                 }
10190             }
10191             if(ddMatch && format){
10192                 var fvalue = d.dateFormat(format);
10193                 if(ddMatch.test(fvalue)){
10194                     cell.title = ddText.replace("%0", fvalue);
10195                     cell.className = " fc-state-disabled";
10196                 }
10197             }
10198             
10199             if (!cell.initialClassName) {
10200                 cell.initialClassName = cell.dom.className;
10201             }
10202             
10203             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10204         };
10205
10206         var i = 0;
10207         
10208         for(; i < startingPos; i++) {
10209             textEls[i].innerHTML = (++prevStart);
10210             d.setDate(d.getDate()+1);
10211             
10212             cells[i].className = "fc-past fc-other-month";
10213             setCellClass(this, cells[i]);
10214         }
10215         
10216         var intDay = 0;
10217         
10218         for(; i < days; i++){
10219             intDay = i - startingPos + 1;
10220             textEls[i].innerHTML = (intDay);
10221             d.setDate(d.getDate()+1);
10222             
10223             cells[i].className = ''; // "x-date-active";
10224             setCellClass(this, cells[i]);
10225         }
10226         var extraDays = 0;
10227         
10228         for(; i < 42; i++) {
10229             textEls[i].innerHTML = (++extraDays);
10230             d.setDate(d.getDate()+1);
10231             
10232             cells[i].className = "fc-future fc-other-month";
10233             setCellClass(this, cells[i]);
10234         }
10235         
10236         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10237         
10238         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10239         
10240         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10241         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10242         
10243         if(totalRows != 6){
10244             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10245             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10246         }
10247         
10248         this.fireEvent('monthchange', this, date);
10249         
10250         
10251         /*
10252         if(!this.internalRender){
10253             var main = this.el.dom.firstChild;
10254             var w = main.offsetWidth;
10255             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10256             Roo.fly(main).setWidth(w);
10257             this.internalRender = true;
10258             // opera does not respect the auto grow header center column
10259             // then, after it gets a width opera refuses to recalculate
10260             // without a second pass
10261             if(Roo.isOpera && !this.secondPass){
10262                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10263                 this.secondPass = true;
10264                 this.update.defer(10, this, [date]);
10265             }
10266         }
10267         */
10268         
10269     },
10270     
10271     findCell : function(dt) {
10272         dt = dt.clearTime().getTime();
10273         var ret = false;
10274         this.cells.each(function(c){
10275             //Roo.log("check " +c.dateValue + '?=' + dt);
10276             if(c.dateValue == dt){
10277                 ret = c;
10278                 return false;
10279             }
10280             return true;
10281         });
10282         
10283         return ret;
10284     },
10285     
10286     findCells : function(ev) {
10287         var s = ev.start.clone().clearTime().getTime();
10288        // Roo.log(s);
10289         var e= ev.end.clone().clearTime().getTime();
10290        // Roo.log(e);
10291         var ret = [];
10292         this.cells.each(function(c){
10293              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10294             
10295             if(c.dateValue > e){
10296                 return ;
10297             }
10298             if(c.dateValue < s){
10299                 return ;
10300             }
10301             ret.push(c);
10302         });
10303         
10304         return ret;    
10305     },
10306     
10307     findBestRow: function(cells)
10308     {
10309         var ret = 0;
10310         
10311         for (var i =0 ; i < cells.length;i++) {
10312             ret  = Math.max(cells[i].rows || 0,ret);
10313         }
10314         return ret;
10315         
10316     },
10317     
10318     
10319     addItem : function(ev)
10320     {
10321         // look for vertical location slot in
10322         var cells = this.findCells(ev);
10323         
10324         ev.row = this.findBestRow(cells);
10325         
10326         // work out the location.
10327         
10328         var crow = false;
10329         var rows = [];
10330         for(var i =0; i < cells.length; i++) {
10331             if (!crow) {
10332                 crow = {
10333                     start : cells[i],
10334                     end :  cells[i]
10335                 };
10336                 continue;
10337             }
10338             if (crow.start.getY() == cells[i].getY()) {
10339                 // on same row.
10340                 crow.end = cells[i];
10341                 continue;
10342             }
10343             // different row.
10344             rows.push(crow);
10345             crow = {
10346                 start: cells[i],
10347                 end : cells[i]
10348             };
10349             
10350         }
10351         
10352         rows.push(crow);
10353         ev.els = [];
10354         ev.rows = rows;
10355         ev.cells = cells;
10356         for (var i = 0; i < cells.length;i++) {
10357             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10358             
10359         }
10360         
10361         this.calevents.push(ev);
10362     },
10363     
10364     clearEvents: function() {
10365         
10366         if(!this.calevents){
10367             return;
10368         }
10369         
10370         Roo.each(this.cells.elements, function(c){
10371             c.rows = 0;
10372         });
10373         
10374         Roo.each(this.calevents, function(e) {
10375             Roo.each(e.els, function(el) {
10376                 el.un('mouseenter' ,this.onEventEnter, this);
10377                 el.un('mouseleave' ,this.onEventLeave, this);
10378                 el.remove();
10379             },this);
10380         },this);
10381         
10382     },
10383     
10384     renderEvents: function()
10385     {   
10386         // first make sure there is enough space..
10387         
10388         this.cells.each(function(c) {
10389         
10390             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10391         });
10392         
10393         for (var e = 0; e < this.calevents.length; e++) {
10394             var ev = this.calevents[e];
10395             var cells = ev.cells;
10396             var rows = ev.rows;
10397             
10398             for(var i =0; i < rows.length; i++) {
10399                 
10400                  
10401                 // how many rows should it span..
10402                 
10403                 var  cfg = {
10404                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10405                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10406                     
10407                     unselectable : "on",
10408                     cn : [
10409                         {
10410                             cls: 'fc-event-inner',
10411                             cn : [
10412 //                                {
10413 //                                  tag:'span',
10414 //                                  cls: 'fc-event-time',
10415 //                                  html : cells.length > 1 ? '' : ev.time
10416 //                                },
10417                                 {
10418                                   tag:'span',
10419                                   cls: 'fc-event-title',
10420                                   html : String.format('{0}', ev.title)
10421                                 }
10422                                 
10423                                 
10424                             ]
10425                         },
10426                         {
10427                             cls: 'ui-resizable-handle ui-resizable-e',
10428                             html : '&nbsp;&nbsp;&nbsp'
10429                         }
10430                         
10431                     ]
10432                 };
10433                 if (i == 0) {
10434                     cfg.cls += ' fc-event-start';
10435                 }
10436                 if ((i+1) == rows.length) {
10437                     cfg.cls += ' fc-event-end';
10438                 }
10439                 
10440                 var ctr = this.el.select('.fc-event-container',true).first();
10441                 var cg = ctr.createChild(cfg);
10442                 
10443                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10444                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10445                 cg.on('click', this.onEventClick, this, ev);
10446                 
10447                 ev.els.push(cg);
10448                 
10449                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10450                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10451                 //Roo.log(cg);
10452                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10453                 cg.setWidth(ebox.right - sbox.x -2);
10454             }
10455             
10456             
10457         }
10458         
10459     },
10460     
10461     onEventEnter: function (e, el,event,d) {
10462         this.fireEvent('evententer', this, el, event);
10463     },
10464     
10465     onEventLeave: function (e, el,event,d) {
10466         this.fireEvent('eventleave', this, el, event);
10467     },
10468     
10469     onEventClick: function (e, el,event,d) {
10470         this.fireEvent('eventclick', this, el, event);
10471     },
10472     
10473     onMonthChange: function () {
10474         this.store.load();
10475     },
10476     
10477     onLoad: function () 
10478     {   
10479         this.calevents = [];
10480         var cal = this;
10481         
10482         if(this.store.getCount() > 0){
10483             this.store.data.each(function(d){
10484                cal.addItem({
10485                     id : d.data.id,
10486                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10487                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10488                     time : d.data.start_time,
10489                     title : d.data.title,
10490                     description : d.data.description,
10491                     venue : d.data.venue
10492                 });
10493             });
10494         }
10495         
10496         this.renderEvents();
10497         
10498         if(this.loadMask){
10499             this.maskEl.hide();
10500         }
10501     },
10502     
10503     onBeforeLoad: function()
10504     {
10505         this.clearEvents();
10506         
10507         if(this.loadMask){
10508             this.maskEl.show();
10509         }
10510     }
10511 });
10512
10513  
10514  /*
10515  * - LGPL
10516  *
10517  * element
10518  * 
10519  */
10520
10521 /**
10522  * @class Roo.bootstrap.Popover
10523  * @extends Roo.bootstrap.Component
10524  * Bootstrap Popover class
10525  * @cfg {String} html contents of the popover   (or false to use children..)
10526  * @cfg {String} title of popover (or false to hide)
10527  * @cfg {String} placement how it is placed
10528  * @cfg {String} trigger click || hover (or false to trigger manually)
10529  * @cfg {String} over what (parent or false to trigger manually.)
10530  * 
10531  * @constructor
10532  * Create a new Popover
10533  * @param {Object} config The config object
10534  */
10535
10536 Roo.bootstrap.Popover = function(config){
10537     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10538 };
10539
10540 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10541     
10542     title: 'Fill in a title',
10543     html: false,
10544     
10545     placement : 'right',
10546     trigger : 'hover', // hover
10547     
10548     over: 'parent',
10549     
10550     can_build_overlaid : false,
10551     
10552     getChildContainer : function()
10553     {
10554         return this.el.select('.popover-content',true).first();
10555     },
10556     
10557     getAutoCreate : function(){
10558          Roo.log('make popover?');
10559         var cfg = {
10560            cls : 'popover roo-dynamic',
10561            style: 'display:block',
10562            cn : [
10563                 {
10564                     cls : 'arrow'
10565                 },
10566                 {
10567                     cls : 'popover-inner',
10568                     cn : [
10569                         {
10570                             tag: 'h3',
10571                             cls: 'popover-title',
10572                             html : this.title
10573                         },
10574                         {
10575                             cls : 'popover-content',
10576                             html : this.html
10577                         }
10578                     ]
10579                     
10580                 }
10581            ]
10582         };
10583         
10584         return cfg;
10585     },
10586     setTitle: function(str)
10587     {
10588         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10589     },
10590     setContent: function(str)
10591     {
10592         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10593     },
10594     // as it get's added to the bottom of the page.
10595     onRender : function(ct, position)
10596     {
10597         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10598         if(!this.el){
10599             var cfg = Roo.apply({},  this.getAutoCreate());
10600             cfg.id = Roo.id();
10601             
10602             if (this.cls) {
10603                 cfg.cls += ' ' + this.cls;
10604             }
10605             if (this.style) {
10606                 cfg.style = this.style;
10607             }
10608             Roo.log("adding to ")
10609             this.el = Roo.get(document.body).createChild(cfg, position);
10610             Roo.log(this.el);
10611         }
10612         this.initEvents();
10613     },
10614     
10615     initEvents : function()
10616     {
10617         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10618         this.el.enableDisplayMode('block');
10619         this.el.hide();
10620         if (this.over === false) {
10621             return; 
10622         }
10623         if (this.triggers === false) {
10624             return;
10625         }
10626         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10627         var triggers = this.trigger ? this.trigger.split(' ') : [];
10628         Roo.each(triggers, function(trigger) {
10629         
10630             if (trigger == 'click') {
10631                 on_el.on('click', this.toggle, this);
10632             } else if (trigger != 'manual') {
10633                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10634                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10635       
10636                 on_el.on(eventIn  ,this.enter, this);
10637                 on_el.on(eventOut, this.leave, this);
10638             }
10639         }, this);
10640         
10641     },
10642     
10643     
10644     // private
10645     timeout : null,
10646     hoverState : null,
10647     
10648     toggle : function () {
10649         this.hoverState == 'in' ? this.leave() : this.enter();
10650     },
10651     
10652     enter : function () {
10653        
10654     
10655         clearTimeout(this.timeout);
10656     
10657         this.hoverState = 'in'
10658     
10659         if (!this.delay || !this.delay.show) {
10660             this.show();
10661             return 
10662         }
10663         var _t = this;
10664         this.timeout = setTimeout(function () {
10665             if (_t.hoverState == 'in') {
10666                 _t.show();
10667             }
10668         }, this.delay.show)
10669     },
10670     leave : function() {
10671         clearTimeout(this.timeout);
10672     
10673         this.hoverState = 'out'
10674     
10675         if (!this.delay || !this.delay.hide) {
10676             this.hide();
10677             return 
10678         }
10679         var _t = this;
10680         this.timeout = setTimeout(function () {
10681             if (_t.hoverState == 'out') {
10682                 _t.hide();
10683             }
10684         }, this.delay.hide)
10685     },
10686     
10687     show : function (on_el)
10688     {
10689         if (!on_el) {
10690             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10691         }
10692         // set content.
10693         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10694         if (this.html !== false) {
10695             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10696         }
10697         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10698         if (!this.title.length) {
10699             this.el.select('.popover-title',true).hide();
10700         }
10701         
10702         var placement = typeof this.placement == 'function' ?
10703             this.placement.call(this, this.el, on_el) :
10704             this.placement;
10705             
10706         var autoToken = /\s?auto?\s?/i;
10707         var autoPlace = autoToken.test(placement);
10708         if (autoPlace) {
10709             placement = placement.replace(autoToken, '') || 'top';
10710         }
10711         
10712         //this.el.detach()
10713         //this.el.setXY([0,0]);
10714         this.el.show();
10715         this.el.dom.style.display='block';
10716         this.el.addClass(placement);
10717         
10718         //this.el.appendTo(on_el);
10719         
10720         var p = this.getPosition();
10721         var box = this.el.getBox();
10722         
10723         if (autoPlace) {
10724             // fixme..
10725         }
10726         var align = Roo.bootstrap.Popover.alignment[placement]
10727         this.el.alignTo(on_el, align[0],align[1]);
10728         //var arrow = this.el.select('.arrow',true).first();
10729         //arrow.set(align[2], 
10730         
10731         this.el.addClass('in');
10732         this.hoverState = null;
10733         
10734         if (this.el.hasClass('fade')) {
10735             // fade it?
10736         }
10737         
10738     },
10739     hide : function()
10740     {
10741         this.el.setXY([0,0]);
10742         this.el.removeClass('in');
10743         this.el.hide();
10744         
10745     }
10746     
10747 });
10748
10749 Roo.bootstrap.Popover.alignment = {
10750     'left' : ['r-l', [-10,0], 'right'],
10751     'right' : ['l-r', [10,0], 'left'],
10752     'bottom' : ['t-b', [0,10], 'top'],
10753     'top' : [ 'b-t', [0,-10], 'bottom']
10754 };
10755
10756  /*
10757  * - LGPL
10758  *
10759  * Progress
10760  * 
10761  */
10762
10763 /**
10764  * @class Roo.bootstrap.Progress
10765  * @extends Roo.bootstrap.Component
10766  * Bootstrap Progress class
10767  * @cfg {Boolean} striped striped of the progress bar
10768  * @cfg {Boolean} active animated of the progress bar
10769  * 
10770  * 
10771  * @constructor
10772  * Create a new Progress
10773  * @param {Object} config The config object
10774  */
10775
10776 Roo.bootstrap.Progress = function(config){
10777     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10778 };
10779
10780 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10781     
10782     striped : false,
10783     active: false,
10784     
10785     getAutoCreate : function(){
10786         var cfg = {
10787             tag: 'div',
10788             cls: 'progress'
10789         };
10790         
10791         
10792         if(this.striped){
10793             cfg.cls += ' progress-striped';
10794         }
10795       
10796         if(this.active){
10797             cfg.cls += ' active';
10798         }
10799         
10800         
10801         return cfg;
10802     }
10803    
10804 });
10805
10806  
10807
10808  /*
10809  * - LGPL
10810  *
10811  * ProgressBar
10812  * 
10813  */
10814
10815 /**
10816  * @class Roo.bootstrap.ProgressBar
10817  * @extends Roo.bootstrap.Component
10818  * Bootstrap ProgressBar class
10819  * @cfg {Number} aria_valuenow aria-value now
10820  * @cfg {Number} aria_valuemin aria-value min
10821  * @cfg {Number} aria_valuemax aria-value max
10822  * @cfg {String} label label for the progress bar
10823  * @cfg {String} panel (success | info | warning | danger )
10824  * @cfg {String} role role of the progress bar
10825  * @cfg {String} sr_only text
10826  * 
10827  * 
10828  * @constructor
10829  * Create a new ProgressBar
10830  * @param {Object} config The config object
10831  */
10832
10833 Roo.bootstrap.ProgressBar = function(config){
10834     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10835 };
10836
10837 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10838     
10839     aria_valuenow : 0,
10840     aria_valuemin : 0,
10841     aria_valuemax : 100,
10842     label : false,
10843     panel : false,
10844     role : false,
10845     sr_only: false,
10846     
10847     getAutoCreate : function()
10848     {
10849         
10850         var cfg = {
10851             tag: 'div',
10852             cls: 'progress-bar',
10853             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10854         };
10855         
10856         if(this.sr_only){
10857             cfg.cn = {
10858                 tag: 'span',
10859                 cls: 'sr-only',
10860                 html: this.sr_only
10861             }
10862         }
10863         
10864         if(this.role){
10865             cfg.role = this.role;
10866         }
10867         
10868         if(this.aria_valuenow){
10869             cfg['aria-valuenow'] = this.aria_valuenow;
10870         }
10871         
10872         if(this.aria_valuemin){
10873             cfg['aria-valuemin'] = this.aria_valuemin;
10874         }
10875         
10876         if(this.aria_valuemax){
10877             cfg['aria-valuemax'] = this.aria_valuemax;
10878         }
10879         
10880         if(this.label && !this.sr_only){
10881             cfg.html = this.label;
10882         }
10883         
10884         if(this.panel){
10885             cfg.cls += ' progress-bar-' + this.panel;
10886         }
10887         
10888         return cfg;
10889     },
10890     
10891     update : function(aria_valuenow)
10892     {
10893         this.aria_valuenow = aria_valuenow;
10894         
10895         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10896     }
10897    
10898 });
10899
10900  
10901
10902  /*
10903  * - LGPL
10904  *
10905  * TabPanel
10906  * 
10907  */
10908
10909 /**
10910  * @class Roo.bootstrap.TabPanel
10911  * @extends Roo.bootstrap.Component
10912  * Bootstrap TabPanel class
10913  * @cfg {Boolean} active panel active
10914  * @cfg {String} html panel content
10915  * @cfg {String} tabId tab relate id
10916  * 
10917  * 
10918  * @constructor
10919  * Create a new TabPanel
10920  * @param {Object} config The config object
10921  */
10922
10923 Roo.bootstrap.TabPanel = function(config){
10924     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10925 };
10926
10927 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10928     
10929     active: false,
10930     html: false,
10931     tabId: false,
10932     
10933     getAutoCreate : function(){
10934         var cfg = {
10935             tag: 'div',
10936             cls: 'tab-pane',
10937             html: this.html || ''
10938         };
10939         
10940         if(this.active){
10941             cfg.cls += ' active';
10942         }
10943         
10944         if(this.tabId){
10945             cfg.tabId = this.tabId;
10946         }
10947         
10948         return cfg;
10949     }
10950    
10951 });
10952
10953  
10954
10955  /*
10956  * - LGPL
10957  *
10958  * DateField
10959  * 
10960  */
10961
10962 /**
10963  * @class Roo.bootstrap.DateField
10964  * @extends Roo.bootstrap.Input
10965  * Bootstrap DateField class
10966  * @cfg {Number} weekStart default 0
10967  * @cfg {Number} weekStart default 0
10968  * @cfg {Number} viewMode default empty, (months|years)
10969  * @cfg {Number} minViewMode default empty, (months|years)
10970  * @cfg {Number} startDate default -Infinity
10971  * @cfg {Number} endDate default Infinity
10972  * @cfg {Boolean} todayHighlight default false
10973  * @cfg {Boolean} todayBtn default false
10974  * @cfg {Boolean} calendarWeeks default false
10975  * @cfg {Object} daysOfWeekDisabled default empty
10976  * 
10977  * @cfg {Boolean} keyboardNavigation default true
10978  * @cfg {String} language default en
10979  * 
10980  * @constructor
10981  * Create a new DateField
10982  * @param {Object} config The config object
10983  */
10984
10985 Roo.bootstrap.DateField = function(config){
10986     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10987      this.addEvents({
10988             /**
10989              * @event show
10990              * Fires when this field show.
10991              * @param {Roo.bootstrap.DateField} this
10992              * @param {Mixed} date The date value
10993              */
10994             show : true,
10995             /**
10996              * @event show
10997              * Fires when this field hide.
10998              * @param {Roo.bootstrap.DateField} this
10999              * @param {Mixed} date The date value
11000              */
11001             hide : true,
11002             /**
11003              * @event select
11004              * Fires when select a date.
11005              * @param {Roo.bootstrap.DateField} this
11006              * @param {Mixed} date The date value
11007              */
11008             select : true
11009         });
11010 };
11011
11012 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
11013     
11014     /**
11015      * @cfg {String} format
11016      * The default date format string which can be overriden for localization support.  The format must be
11017      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11018      */
11019     format : "m/d/y",
11020     /**
11021      * @cfg {String} altFormats
11022      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11023      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11024      */
11025     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11026     
11027     weekStart : 0,
11028     
11029     viewMode : '',
11030     
11031     minViewMode : '',
11032     
11033     todayHighlight : false,
11034     
11035     todayBtn: false,
11036     
11037     language: 'en',
11038     
11039     keyboardNavigation: true,
11040     
11041     calendarWeeks: false,
11042     
11043     startDate: -Infinity,
11044     
11045     endDate: Infinity,
11046     
11047     daysOfWeekDisabled: [],
11048     
11049     _events: [],
11050     
11051     UTCDate: function()
11052     {
11053         return new Date(Date.UTC.apply(Date, arguments));
11054     },
11055     
11056     UTCToday: function()
11057     {
11058         var today = new Date();
11059         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11060     },
11061     
11062     getDate: function() {
11063             var d = this.getUTCDate();
11064             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11065     },
11066     
11067     getUTCDate: function() {
11068             return this.date;
11069     },
11070     
11071     setDate: function(d) {
11072             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11073     },
11074     
11075     setUTCDate: function(d) {
11076             this.date = d;
11077             this.setValue(this.formatDate(this.date));
11078     },
11079         
11080     onRender: function(ct, position)
11081     {
11082         
11083         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11084         
11085         this.language = this.language || 'en';
11086         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11087         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11088         
11089         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11090         this.format = this.format || 'm/d/y';
11091         this.isInline = false;
11092         this.isInput = true;
11093         this.component = this.el.select('.add-on', true).first() || false;
11094         this.component = (this.component && this.component.length === 0) ? false : this.component;
11095         this.hasInput = this.component && this.inputEL().length;
11096         
11097         if (typeof(this.minViewMode === 'string')) {
11098             switch (this.minViewMode) {
11099                 case 'months':
11100                     this.minViewMode = 1;
11101                     break;
11102                 case 'years':
11103                     this.minViewMode = 2;
11104                     break;
11105                 default:
11106                     this.minViewMode = 0;
11107                     break;
11108             }
11109         }
11110         
11111         if (typeof(this.viewMode === 'string')) {
11112             switch (this.viewMode) {
11113                 case 'months':
11114                     this.viewMode = 1;
11115                     break;
11116                 case 'years':
11117                     this.viewMode = 2;
11118                     break;
11119                 default:
11120                     this.viewMode = 0;
11121                     break;
11122             }
11123         }
11124                 
11125         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11126         
11127         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11128         
11129         this.picker().on('mousedown', this.onMousedown, this);
11130         this.picker().on('click', this.onClick, this);
11131         
11132         this.picker().addClass('datepicker-dropdown');
11133         
11134         this.startViewMode = this.viewMode;
11135         
11136         
11137         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11138             if(!this.calendarWeeks){
11139                 v.remove();
11140                 return;
11141             };
11142             
11143             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11144             v.attr('colspan', function(i, val){
11145                 return parseInt(val) + 1;
11146             });
11147         })
11148                         
11149         
11150         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11151         
11152         this.setStartDate(this.startDate);
11153         this.setEndDate(this.endDate);
11154         
11155         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11156         
11157         this.fillDow();
11158         this.fillMonths();
11159         this.update();
11160         this.showMode();
11161         
11162         if(this.isInline) {
11163             this.show();
11164         }
11165     },
11166     
11167     picker : function()
11168     {
11169         return this.el.select('.datepicker', true).first();
11170     },
11171     
11172     fillDow: function()
11173     {
11174         var dowCnt = this.weekStart;
11175         
11176         var dow = {
11177             tag: 'tr',
11178             cn: [
11179                 
11180             ]
11181         };
11182         
11183         if(this.calendarWeeks){
11184             dow.cn.push({
11185                 tag: 'th',
11186                 cls: 'cw',
11187                 html: '&nbsp;'
11188             })
11189         }
11190         
11191         while (dowCnt < this.weekStart + 7) {
11192             dow.cn.push({
11193                 tag: 'th',
11194                 cls: 'dow',
11195                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11196             });
11197         }
11198         
11199         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11200     },
11201     
11202     fillMonths: function()
11203     {    
11204         var i = 0
11205         var months = this.picker().select('>.datepicker-months td', true).first();
11206         
11207         months.dom.innerHTML = '';
11208         
11209         while (i < 12) {
11210             var month = {
11211                 tag: 'span',
11212                 cls: 'month',
11213                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11214             }
11215             
11216             months.createChild(month);
11217         }
11218         
11219     },
11220     
11221     update: function(){
11222         
11223         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11224         
11225         if (this.date < this.startDate) {
11226             this.viewDate = new Date(this.startDate);
11227         } else if (this.date > this.endDate) {
11228             this.viewDate = new Date(this.endDate);
11229         } else {
11230             this.viewDate = new Date(this.date);
11231         }
11232         
11233         this.fill();
11234     },
11235     
11236     fill: function() {
11237         var d = new Date(this.viewDate),
11238                 year = d.getUTCFullYear(),
11239                 month = d.getUTCMonth(),
11240                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11241                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11242                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11243                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11244                 currentDate = this.date && this.date.valueOf(),
11245                 today = this.UTCToday();
11246         
11247         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11248         
11249 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11250         
11251 //        this.picker.select('>tfoot th.today').
11252 //                                              .text(dates[this.language].today)
11253 //                                              .toggle(this.todayBtn !== false);
11254     
11255         this.updateNavArrows();
11256         this.fillMonths();
11257                                                 
11258         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11259         
11260         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11261          
11262         prevMonth.setUTCDate(day);
11263         
11264         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11265         
11266         var nextMonth = new Date(prevMonth);
11267         
11268         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11269         
11270         nextMonth = nextMonth.valueOf();
11271         
11272         var fillMonths = false;
11273         
11274         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11275         
11276         while(prevMonth.valueOf() < nextMonth) {
11277             var clsName = '';
11278             
11279             if (prevMonth.getUTCDay() === this.weekStart) {
11280                 if(fillMonths){
11281                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11282                 }
11283                     
11284                 fillMonths = {
11285                     tag: 'tr',
11286                     cn: []
11287                 };
11288                 
11289                 if(this.calendarWeeks){
11290                     // ISO 8601: First week contains first thursday.
11291                     // ISO also states week starts on Monday, but we can be more abstract here.
11292                     var
11293                     // Start of current week: based on weekstart/current date
11294                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11295                     // Thursday of this week
11296                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11297                     // First Thursday of year, year from thursday
11298                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11299                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11300                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11301                     
11302                     fillMonths.cn.push({
11303                         tag: 'td',
11304                         cls: 'cw',
11305                         html: calWeek
11306                     });
11307                 }
11308             }
11309             
11310             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11311                 clsName += ' old';
11312             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11313                 clsName += ' new';
11314             }
11315             if (this.todayHighlight &&
11316                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11317                 prevMonth.getUTCMonth() == today.getMonth() &&
11318                 prevMonth.getUTCDate() == today.getDate()) {
11319                 clsName += ' today';
11320             }
11321             
11322             if (currentDate && prevMonth.valueOf() === currentDate) {
11323                 clsName += ' active';
11324             }
11325             
11326             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11327                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11328                     clsName += ' disabled';
11329             }
11330             
11331             fillMonths.cn.push({
11332                 tag: 'td',
11333                 cls: 'day ' + clsName,
11334                 html: prevMonth.getDate()
11335             })
11336             
11337             prevMonth.setDate(prevMonth.getDate()+1);
11338         }
11339           
11340         var currentYear = this.date && this.date.getUTCFullYear();
11341         var currentMonth = this.date && this.date.getUTCMonth();
11342         
11343         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11344         
11345         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11346             v.removeClass('active');
11347             
11348             if(currentYear === year && k === currentMonth){
11349                 v.addClass('active');
11350             }
11351             
11352             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11353                 v.addClass('disabled');
11354             }
11355             
11356         });
11357         
11358         
11359         year = parseInt(year/10, 10) * 10;
11360         
11361         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11362         
11363         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11364         
11365         year -= 1;
11366         for (var i = -1; i < 11; i++) {
11367             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11368                 tag: 'span',
11369                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11370                 html: year
11371             })
11372             
11373             year += 1;
11374         }
11375     },
11376     
11377     showMode: function(dir) {
11378         if (dir) {
11379             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11380         }
11381         Roo.each(this.picker().select('>div',true).elements, function(v){
11382             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11383             v.hide();
11384         });
11385         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11386     },
11387     
11388     place: function()
11389     {
11390         if(this.isInline) return;
11391         
11392         this.picker().removeClass(['bottom', 'top']);
11393         
11394         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11395             /*
11396              * place to the top of element!
11397              *
11398              */
11399             
11400             this.picker().addClass('top');
11401             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11402             
11403             return;
11404         }
11405         
11406         this.picker().addClass('bottom');
11407         
11408         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11409     },
11410     
11411     parseDate : function(value){
11412         if(!value || value instanceof Date){
11413             return value;
11414         }
11415         var v = Date.parseDate(value, this.format);
11416         if (!v && this.useIso) {
11417             v = Date.parseDate(value, 'Y-m-d');
11418         }
11419         if(!v && this.altFormats){
11420             if(!this.altFormatsArray){
11421                 this.altFormatsArray = this.altFormats.split("|");
11422             }
11423             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11424                 v = Date.parseDate(value, this.altFormatsArray[i]);
11425             }
11426         }
11427         return v;
11428     },
11429     
11430     formatDate : function(date, fmt){
11431         return (!date || !(date instanceof Date)) ?
11432         date : date.dateFormat(fmt || this.format);
11433     },
11434     
11435     onFocus : function()
11436     {
11437         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11438         this.show();
11439     },
11440     
11441     onBlur : function()
11442     {
11443         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11444         this.hide();
11445     },
11446     
11447     show : function()
11448     {
11449         this.picker().show();
11450         this.update();
11451         this.place();
11452         
11453         this.fireEvent('show', this, this.date);
11454     },
11455     
11456     hide : function()
11457     {
11458         if(this.isInline) return;
11459         this.picker().hide();
11460         this.viewMode = this.startViewMode;
11461         this.showMode();
11462         
11463         this.fireEvent('hide', this, this.date);
11464         
11465     },
11466     
11467     onMousedown: function(e){
11468         e.stopPropagation();
11469         e.preventDefault();
11470     },
11471     
11472     keyup: function(e){
11473         Roo.bootstrap.DateField.superclass.keyup.call(this);
11474         this.update();
11475         
11476     },
11477
11478     setValue: function(v){
11479         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11480         
11481         this.fireEvent('select', this, this.date);
11482         
11483     },
11484     
11485     fireKey: function(e){
11486         if (!this.picker().isVisible()){
11487             if (e.keyCode == 27) // allow escape to hide and re-show picker
11488                 this.show();
11489             return;
11490         }
11491         var dateChanged = false,
11492         dir, day, month,
11493         newDate, newViewDate;
11494         switch(e.keyCode){
11495             case 27: // escape
11496                 this.hide();
11497                 e.preventDefault();
11498                 break;
11499             case 37: // left
11500             case 39: // right
11501                 if (!this.keyboardNavigation) break;
11502                 dir = e.keyCode == 37 ? -1 : 1;
11503                 
11504                 if (e.ctrlKey){
11505                     newDate = this.moveYear(this.date, dir);
11506                     newViewDate = this.moveYear(this.viewDate, dir);
11507                 } else if (e.shiftKey){
11508                     newDate = this.moveMonth(this.date, dir);
11509                     newViewDate = this.moveMonth(this.viewDate, dir);
11510                 } else {
11511                     newDate = new Date(this.date);
11512                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11513                     newViewDate = new Date(this.viewDate);
11514                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11515                 }
11516                 if (this.dateWithinRange(newDate)){
11517                     this.date = newDate;
11518                     this.viewDate = newViewDate;
11519                     this.setValue(this.formatDate(this.date));
11520                     this.update();
11521                     e.preventDefault();
11522                     dateChanged = true;
11523                 }
11524                 break;
11525             case 38: // up
11526             case 40: // down
11527                 if (!this.keyboardNavigation) break;
11528                 dir = e.keyCode == 38 ? -1 : 1;
11529                 if (e.ctrlKey){
11530                     newDate = this.moveYear(this.date, dir);
11531                     newViewDate = this.moveYear(this.viewDate, dir);
11532                 } else if (e.shiftKey){
11533                     newDate = this.moveMonth(this.date, dir);
11534                     newViewDate = this.moveMonth(this.viewDate, dir);
11535                 } else {
11536                     newDate = new Date(this.date);
11537                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11538                     newViewDate = new Date(this.viewDate);
11539                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11540                 }
11541                 if (this.dateWithinRange(newDate)){
11542                     this.date = newDate;
11543                     this.viewDate = newViewDate;
11544                     this.setValue(this.formatDate(this.date));
11545                     this.update();
11546                     e.preventDefault();
11547                     dateChanged = true;
11548                 }
11549                 break;
11550             case 13: // enter
11551                 this.setValue(this.formatDate(this.date));
11552                 this.hide();
11553                 e.preventDefault();
11554                 break;
11555             case 9: // tab
11556                 this.setValue(this.formatDate(this.date));
11557                 this.hide();
11558                 break;
11559         }
11560     },
11561     
11562     
11563     onClick: function(e) {
11564         e.stopPropagation();
11565         e.preventDefault();
11566         
11567         var target = e.getTarget();
11568         
11569         if(target.nodeName.toLowerCase() === 'i'){
11570             target = Roo.get(target).dom.parentNode;
11571         }
11572         
11573         var nodeName = target.nodeName;
11574         var className = target.className;
11575         var html = target.innerHTML;
11576         
11577         switch(nodeName.toLowerCase()) {
11578             case 'th':
11579                 switch(className) {
11580                     case 'switch':
11581                         this.showMode(1);
11582                         break;
11583                     case 'prev':
11584                     case 'next':
11585                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11586                         switch(this.viewMode){
11587                                 case 0:
11588                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11589                                         break;
11590                                 case 1:
11591                                 case 2:
11592                                         this.viewDate = this.moveYear(this.viewDate, dir);
11593                                         break;
11594                         }
11595                         this.fill();
11596                         break;
11597                     case 'today':
11598                         var date = new Date();
11599                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11600                         this.fill()
11601                         this.setValue(this.formatDate(this.date));
11602                         this.hide();
11603                         break;
11604                 }
11605                 break;
11606             case 'span':
11607                 if (className.indexOf('disabled') === -1) {
11608                     this.viewDate.setUTCDate(1);
11609                     if (className.indexOf('month') !== -1) {
11610                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11611                     } else {
11612                         var year = parseInt(html, 10) || 0;
11613                         this.viewDate.setUTCFullYear(year);
11614                         
11615                     }
11616                     this.showMode(-1);
11617                     this.fill();
11618                 }
11619                 break;
11620                 
11621             case 'td':
11622                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11623                     var day = parseInt(html, 10) || 1;
11624                     var year = this.viewDate.getUTCFullYear(),
11625                         month = this.viewDate.getUTCMonth();
11626
11627                     if (className.indexOf('old') !== -1) {
11628                         if(month === 0 ){
11629                             month = 11;
11630                             year -= 1;
11631                         }else{
11632                             month -= 1;
11633                         }
11634                     } else if (className.indexOf('new') !== -1) {
11635                         if (month == 11) {
11636                             month = 0;
11637                             year += 1;
11638                         } else {
11639                             month += 1;
11640                         }
11641                     }
11642                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11643                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11644                     this.fill();
11645                     this.setValue(this.formatDate(this.date));
11646                     this.hide();
11647                 }
11648                 break;
11649         }
11650     },
11651     
11652     setStartDate: function(startDate){
11653         this.startDate = startDate || -Infinity;
11654         if (this.startDate !== -Infinity) {
11655             this.startDate = this.parseDate(this.startDate);
11656         }
11657         this.update();
11658         this.updateNavArrows();
11659     },
11660
11661     setEndDate: function(endDate){
11662         this.endDate = endDate || Infinity;
11663         if (this.endDate !== Infinity) {
11664             this.endDate = this.parseDate(this.endDate);
11665         }
11666         this.update();
11667         this.updateNavArrows();
11668     },
11669     
11670     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11671         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11672         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11673             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11674         }
11675         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11676             return parseInt(d, 10);
11677         });
11678         this.update();
11679         this.updateNavArrows();
11680     },
11681     
11682     updateNavArrows: function() {
11683         var d = new Date(this.viewDate),
11684         year = d.getUTCFullYear(),
11685         month = d.getUTCMonth();
11686         
11687         Roo.each(this.picker().select('.prev', true).elements, function(v){
11688             v.show();
11689             switch (this.viewMode) {
11690                 case 0:
11691
11692                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11693                         v.hide();
11694                     }
11695                     break;
11696                 case 1:
11697                 case 2:
11698                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11699                         v.hide();
11700                     }
11701                     break;
11702             }
11703         });
11704         
11705         Roo.each(this.picker().select('.next', true).elements, function(v){
11706             v.show();
11707             switch (this.viewMode) {
11708                 case 0:
11709
11710                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11711                         v.hide();
11712                     }
11713                     break;
11714                 case 1:
11715                 case 2:
11716                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11717                         v.hide();
11718                     }
11719                     break;
11720             }
11721         })
11722     },
11723     
11724     moveMonth: function(date, dir){
11725         if (!dir) return date;
11726         var new_date = new Date(date.valueOf()),
11727         day = new_date.getUTCDate(),
11728         month = new_date.getUTCMonth(),
11729         mag = Math.abs(dir),
11730         new_month, test;
11731         dir = dir > 0 ? 1 : -1;
11732         if (mag == 1){
11733             test = dir == -1
11734             // If going back one month, make sure month is not current month
11735             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11736             ? function(){
11737                 return new_date.getUTCMonth() == month;
11738             }
11739             // If going forward one month, make sure month is as expected
11740             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11741             : function(){
11742                 return new_date.getUTCMonth() != new_month;
11743             };
11744             new_month = month + dir;
11745             new_date.setUTCMonth(new_month);
11746             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11747             if (new_month < 0 || new_month > 11)
11748                 new_month = (new_month + 12) % 12;
11749         } else {
11750             // For magnitudes >1, move one month at a time...
11751             for (var i=0; i<mag; i++)
11752                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11753                 new_date = this.moveMonth(new_date, dir);
11754             // ...then reset the day, keeping it in the new month
11755             new_month = new_date.getUTCMonth();
11756             new_date.setUTCDate(day);
11757             test = function(){
11758                 return new_month != new_date.getUTCMonth();
11759             };
11760         }
11761         // Common date-resetting loop -- if date is beyond end of month, make it
11762         // end of month
11763         while (test()){
11764             new_date.setUTCDate(--day);
11765             new_date.setUTCMonth(new_month);
11766         }
11767         return new_date;
11768     },
11769
11770     moveYear: function(date, dir){
11771         return this.moveMonth(date, dir*12);
11772     },
11773
11774     dateWithinRange: function(date){
11775         return date >= this.startDate && date <= this.endDate;
11776     },
11777
11778     
11779     remove: function() {
11780         this.picker().remove();
11781     }
11782    
11783 });
11784
11785 Roo.apply(Roo.bootstrap.DateField,  {
11786     
11787     head : {
11788         tag: 'thead',
11789         cn: [
11790         {
11791             tag: 'tr',
11792             cn: [
11793             {
11794                 tag: 'th',
11795                 cls: 'prev',
11796                 html: '<i class="icon-arrow-left"/>'
11797             },
11798             {
11799                 tag: 'th',
11800                 cls: 'switch',
11801                 colspan: '5'
11802             },
11803             {
11804                 tag: 'th',
11805                 cls: 'next',
11806                 html: '<i class="icon-arrow-right"/>'
11807             }
11808
11809             ]
11810         }
11811         ]
11812     },
11813     
11814     content : {
11815         tag: 'tbody',
11816         cn: [
11817         {
11818             tag: 'tr',
11819             cn: [
11820             {
11821                 tag: 'td',
11822                 colspan: '7'
11823             }
11824             ]
11825         }
11826         ]
11827     },
11828     
11829     footer : {
11830         tag: 'tfoot',
11831         cn: [
11832         {
11833             tag: 'tr',
11834             cn: [
11835             {
11836                 tag: 'th',
11837                 colspan: '7',
11838                 cls: 'today'
11839             }
11840                     
11841             ]
11842         }
11843         ]
11844     },
11845     
11846     dates:{
11847         en: {
11848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11853             today: "Today"
11854         }
11855     },
11856     
11857     modes: [
11858     {
11859         clsName: 'days',
11860         navFnc: 'Month',
11861         navStep: 1
11862     },
11863     {
11864         clsName: 'months',
11865         navFnc: 'FullYear',
11866         navStep: 1
11867     },
11868     {
11869         clsName: 'years',
11870         navFnc: 'FullYear',
11871         navStep: 10
11872     }]
11873 });
11874
11875 Roo.apply(Roo.bootstrap.DateField,  {
11876   
11877     template : {
11878         tag: 'div',
11879         cls: 'datepicker dropdown-menu',
11880         cn: [
11881         {
11882             tag: 'div',
11883             cls: 'datepicker-days',
11884             cn: [
11885             {
11886                 tag: 'table',
11887                 cls: 'table-condensed',
11888                 cn:[
11889                 Roo.bootstrap.DateField.head,
11890                 {
11891                     tag: 'tbody'
11892                 },
11893                 Roo.bootstrap.DateField.footer
11894                 ]
11895             }
11896             ]
11897         },
11898         {
11899             tag: 'div',
11900             cls: 'datepicker-months',
11901             cn: [
11902             {
11903                 tag: 'table',
11904                 cls: 'table-condensed',
11905                 cn:[
11906                 Roo.bootstrap.DateField.head,
11907                 Roo.bootstrap.DateField.content,
11908                 Roo.bootstrap.DateField.footer
11909                 ]
11910             }
11911             ]
11912         },
11913         {
11914             tag: 'div',
11915             cls: 'datepicker-years',
11916             cn: [
11917             {
11918                 tag: 'table',
11919                 cls: 'table-condensed',
11920                 cn:[
11921                 Roo.bootstrap.DateField.head,
11922                 Roo.bootstrap.DateField.content,
11923                 Roo.bootstrap.DateField.footer
11924                 ]
11925             }
11926             ]
11927         }
11928         ]
11929     }
11930 });
11931
11932  
11933
11934  /*
11935  * - LGPL
11936  *
11937  * TimeField
11938  * 
11939  */
11940
11941 /**
11942  * @class Roo.bootstrap.TimeField
11943  * @extends Roo.bootstrap.Input
11944  * Bootstrap DateField class
11945  * 
11946  * 
11947  * @constructor
11948  * Create a new TimeField
11949  * @param {Object} config The config object
11950  */
11951
11952 Roo.bootstrap.TimeField = function(config){
11953     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11954     this.addEvents({
11955             /**
11956              * @event show
11957              * Fires when this field show.
11958              * @param {Roo.bootstrap.DateField} this
11959              * @param {Mixed} date The date value
11960              */
11961             show : true,
11962             /**
11963              * @event show
11964              * Fires when this field hide.
11965              * @param {Roo.bootstrap.DateField} this
11966              * @param {Mixed} date The date value
11967              */
11968             hide : true,
11969             /**
11970              * @event select
11971              * Fires when select a date.
11972              * @param {Roo.bootstrap.DateField} this
11973              * @param {Mixed} date The date value
11974              */
11975             select : true
11976         });
11977 };
11978
11979 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11980     
11981     /**
11982      * @cfg {String} format
11983      * The default time format string which can be overriden for localization support.  The format must be
11984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11985      */
11986     format : "H:i",
11987        
11988     onRender: function(ct, position)
11989     {
11990         
11991         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11992                 
11993         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11994         
11995         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11996         
11997         this.pop = this.picker().select('>.datepicker-time',true).first();
11998         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11999         
12000         this.picker().on('mousedown', this.onMousedown, this);
12001         this.picker().on('click', this.onClick, this);
12002         
12003         this.picker().addClass('datepicker-dropdown');
12004     
12005         this.fillTime();
12006         this.update();
12007             
12008         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12009         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12010         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12011         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12012         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12013         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12014
12015     },
12016     
12017     fireKey: function(e){
12018         if (!this.picker().isVisible()){
12019             if (e.keyCode == 27) // allow escape to hide and re-show picker
12020                 this.show();
12021             return;
12022         }
12023
12024         e.preventDefault();
12025         
12026         switch(e.keyCode){
12027             case 27: // escape
12028                 this.hide();
12029                 break;
12030             case 37: // left
12031             case 39: // right
12032                 this.onTogglePeriod();
12033                 break;
12034             case 38: // up
12035                 this.onIncrementMinutes();
12036                 break;
12037             case 40: // down
12038                 this.onDecrementMinutes();
12039                 break;
12040             case 13: // enter
12041             case 9: // tab
12042                 this.setTime();
12043                 break;
12044         }
12045     },
12046     
12047     onClick: function(e) {
12048         e.stopPropagation();
12049         e.preventDefault();
12050     },
12051     
12052     picker : function()
12053     {
12054         return this.el.select('.datepicker', true).first();
12055     },
12056     
12057     fillTime: function()
12058     {    
12059         var time = this.pop.select('tbody', true).first();
12060         
12061         time.dom.innerHTML = '';
12062         
12063         time.createChild({
12064             tag: 'tr',
12065             cn: [
12066                 {
12067                     tag: 'td',
12068                     cn: [
12069                         {
12070                             tag: 'a',
12071                             href: '#',
12072                             cls: 'btn',
12073                             cn: [
12074                                 {
12075                                     tag: 'span',
12076                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
12077                                 }
12078                             ]
12079                         } 
12080                     ]
12081                 },
12082                 {
12083                     tag: 'td',
12084                     cls: 'separator'
12085                 },
12086                 {
12087                     tag: 'td',
12088                     cn: [
12089                         {
12090                             tag: 'a',
12091                             href: '#',
12092                             cls: 'btn',
12093                             cn: [
12094                                 {
12095                                     tag: 'span',
12096                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
12097                                 }
12098                             ]
12099                         }
12100                     ]
12101                 },
12102                 {
12103                     tag: 'td',
12104                     cls: 'separator'
12105                 }
12106             ]
12107         });
12108         
12109         time.createChild({
12110             tag: 'tr',
12111             cn: [
12112                 {
12113                     tag: 'td',
12114                     cn: [
12115                         {
12116                             tag: 'span',
12117                             cls: 'timepicker-hour',
12118                             html: '00'
12119                         }  
12120                     ]
12121                 },
12122                 {
12123                     tag: 'td',
12124                     cls: 'separator',
12125                     html: ':'
12126                 },
12127                 {
12128                     tag: 'td',
12129                     cn: [
12130                         {
12131                             tag: 'span',
12132                             cls: 'timepicker-minute',
12133                             html: '00'
12134                         }  
12135                     ]
12136                 },
12137                 {
12138                     tag: 'td',
12139                     cls: 'separator'
12140                 },
12141                 {
12142                     tag: 'td',
12143                     cn: [
12144                         {
12145                             tag: 'button',
12146                             type: 'button',
12147                             cls: 'btn btn-primary period',
12148                             html: 'AM'
12149                             
12150                         }
12151                     ]
12152                 }
12153             ]
12154         });
12155         
12156         time.createChild({
12157             tag: 'tr',
12158             cn: [
12159                 {
12160                     tag: 'td',
12161                     cn: [
12162                         {
12163                             tag: 'a',
12164                             href: '#',
12165                             cls: 'btn',
12166                             cn: [
12167                                 {
12168                                     tag: 'span',
12169                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
12170                                 }
12171                             ]
12172                         }
12173                     ]
12174                 },
12175                 {
12176                     tag: 'td',
12177                     cls: 'separator'
12178                 },
12179                 {
12180                     tag: 'td',
12181                     cn: [
12182                         {
12183                             tag: 'a',
12184                             href: '#',
12185                             cls: 'btn',
12186                             cn: [
12187                                 {
12188                                     tag: 'span',
12189                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
12190                                 }
12191                             ]
12192                         }
12193                     ]
12194                 },
12195                 {
12196                     tag: 'td',
12197                     cls: 'separator'
12198                 }
12199             ]
12200         });
12201         
12202     },
12203     
12204     update: function()
12205     {
12206         
12207         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12208         
12209         this.fill();
12210     },
12211     
12212     fill: function() 
12213     {
12214         var hours = this.time.getHours();
12215         var minutes = this.time.getMinutes();
12216         var period = 'AM';
12217         
12218         if(hours > 11){
12219             period = 'PM';
12220         }
12221         
12222         if(hours == 0){
12223             hours = 12;
12224         }
12225         
12226         
12227         if(hours > 12){
12228             hours = hours - 12;
12229         }
12230         
12231         if(hours < 10){
12232             hours = '0' + hours;
12233         }
12234         
12235         if(minutes < 10){
12236             minutes = '0' + minutes;
12237         }
12238         
12239         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12240         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12241         this.pop.select('button', true).first().dom.innerHTML = period;
12242         
12243     },
12244     
12245     place: function()
12246     {   
12247         this.picker().removeClass(['bottom', 'top']);
12248         
12249         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12250             /*
12251              * place to the top of element!
12252              *
12253              */
12254             
12255             this.picker().addClass('top');
12256             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12257             
12258             return;
12259         }
12260         
12261         this.picker().addClass('bottom');
12262         
12263         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12264     },
12265   
12266     onFocus : function()
12267     {
12268         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12269         this.show();
12270     },
12271     
12272     onBlur : function()
12273     {
12274         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12275         this.hide();
12276     },
12277     
12278     show : function()
12279     {
12280         this.picker().show();
12281         this.pop.show();
12282         this.update();
12283         this.place();
12284         
12285         this.fireEvent('show', this, this.date);
12286     },
12287     
12288     hide : function()
12289     {
12290         this.picker().hide();
12291         this.pop.hide();
12292         
12293         this.fireEvent('hide', this, this.date);
12294     },
12295     
12296     setTime : function()
12297     {
12298         this.hide();
12299         this.setValue(this.time.format(this.format));
12300         
12301         this.fireEvent('select', this, this.date);
12302         
12303         
12304     },
12305     
12306     onMousedown: function(e){
12307         e.stopPropagation();
12308         e.preventDefault();
12309     },
12310     
12311     onIncrementHours: function()
12312     {
12313         Roo.log('onIncrementHours');
12314         this.time = this.time.add(Date.HOUR, 1);
12315         this.update();
12316         
12317     },
12318     
12319     onDecrementHours: function()
12320     {
12321         Roo.log('onDecrementHours');
12322         this.time = this.time.add(Date.HOUR, -1);
12323         this.update();
12324     },
12325     
12326     onIncrementMinutes: function()
12327     {
12328         Roo.log('onIncrementMinutes');
12329         this.time = this.time.add(Date.MINUTE, 1);
12330         this.update();
12331     },
12332     
12333     onDecrementMinutes: function()
12334     {
12335         Roo.log('onDecrementMinutes');
12336         this.time = this.time.add(Date.MINUTE, -1);
12337         this.update();
12338     },
12339     
12340     onTogglePeriod: function()
12341     {
12342         Roo.log('onTogglePeriod');
12343         this.time = this.time.add(Date.HOUR, 12);
12344         this.update();
12345     }
12346     
12347    
12348 });
12349
12350 Roo.apply(Roo.bootstrap.TimeField,  {
12351     
12352     content : {
12353         tag: 'tbody',
12354         cn: [
12355             {
12356                 tag: 'tr',
12357                 cn: [
12358                 {
12359                     tag: 'td',
12360                     colspan: '7'
12361                 }
12362                 ]
12363             }
12364         ]
12365     },
12366     
12367     footer : {
12368         tag: 'tfoot',
12369         cn: [
12370             {
12371                 tag: 'tr',
12372                 cn: [
12373                 {
12374                     tag: 'th',
12375                     colspan: '7',
12376                     cls: '',
12377                     cn: [
12378                         {
12379                             tag: 'button',
12380                             cls: 'btn btn-info ok',
12381                             html: 'OK'
12382                         }
12383                     ]
12384                 }
12385
12386                 ]
12387             }
12388         ]
12389     }
12390 });
12391
12392 Roo.apply(Roo.bootstrap.TimeField,  {
12393   
12394     template : {
12395         tag: 'div',
12396         cls: 'datepicker dropdown-menu',
12397         cn: [
12398             {
12399                 tag: 'div',
12400                 cls: 'datepicker-time',
12401                 cn: [
12402                 {
12403                     tag: 'table',
12404                     cls: 'table-condensed',
12405                     cn:[
12406                     Roo.bootstrap.TimeField.content,
12407                     Roo.bootstrap.TimeField.footer
12408                     ]
12409                 }
12410                 ]
12411             }
12412         ]
12413     }
12414 });
12415
12416  
12417
12418  /*
12419  * - LGPL
12420  *
12421  * CheckBox
12422  * 
12423  */
12424
12425 /**
12426  * @class Roo.bootstrap.CheckBox
12427  * @extends Roo.bootstrap.Input
12428  * Bootstrap CheckBox class
12429  * 
12430  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12431  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12432  * @cfg {String} boxLabel The text that appears beside the checkbox
12433  * @cfg {Boolean} checked initnal the element
12434  * 
12435  * @constructor
12436  * Create a new CheckBox
12437  * @param {Object} config The config object
12438  */
12439
12440 Roo.bootstrap.CheckBox = function(config){
12441     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12442    
12443         this.addEvents({
12444             /**
12445             * @event check
12446             * Fires when the element is checked or unchecked.
12447             * @param {Roo.bootstrap.CheckBox} this This input
12448             * @param {Boolean} checked The new checked value
12449             */
12450            check : true
12451         });
12452 };
12453
12454 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12455     
12456     inputType: 'checkbox',
12457     inputValue: 1,
12458     valueOff: 0,
12459     boxLabel: false,
12460     checked: false,
12461     
12462     getAutoCreate : function()
12463     {
12464         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12465         
12466         var id = Roo.id();
12467         
12468         var cfg = {};
12469         
12470         cfg.cls = 'form-group' //input-group
12471         
12472         var input =  {
12473             tag: 'input',
12474             id : id,
12475             type : this.inputType,
12476             value : (!this.checked) ? this.valueOff : this.inputValue,
12477             cls : 'form-box',
12478             placeholder : this.placeholder || ''
12479             
12480         };
12481         
12482         if (this.disabled) {
12483             input.disabled=true;
12484         }
12485         
12486         if(this.checked){
12487             input.checked = this.checked;
12488         }
12489         
12490         if (this.name) {
12491             input.name = this.name;
12492         }
12493         
12494         if (this.size) {
12495             input.cls += ' input-' + this.size;
12496         }
12497         
12498         var settings=this;
12499         ['xs','sm','md','lg'].map(function(size){
12500             if (settings[size]) {
12501                 cfg.cls += ' col-' + size + '-' + settings[size];
12502             }
12503         });
12504         
12505         var inputblock = input;
12506         
12507         if (this.before || this.after) {
12508             
12509             inputblock = {
12510                 cls : 'input-group',
12511                 cn :  [] 
12512             };
12513             if (this.before) {
12514                 inputblock.cn.push({
12515                     tag :'span',
12516                     cls : 'input-group-addon',
12517                     html : this.before
12518                 });
12519             }
12520             inputblock.cn.push(input);
12521             if (this.after) {
12522                 inputblock.cn.push({
12523                     tag :'span',
12524                     cls : 'input-group-addon',
12525                     html : this.after
12526                 });
12527             }
12528             
12529         };
12530         
12531         if (align ==='left' && this.fieldLabel.length) {
12532                 Roo.log("left and has label");
12533                 cfg.cn = [
12534                     
12535                     {
12536                         tag: 'label',
12537                         'for' :  id,
12538                         cls : 'control-label col-md-' + this.labelWidth,
12539                         html : this.fieldLabel
12540                         
12541                     },
12542                     {
12543                         cls : "col-md-" + (12 - this.labelWidth), 
12544                         cn: [
12545                             inputblock
12546                         ]
12547                     }
12548                     
12549                 ];
12550         } else if ( this.fieldLabel.length) {
12551                 Roo.log(" label");
12552                 cfg.cn = [
12553                    
12554                     {
12555                         tag: this.boxLabel ? 'span' : 'label',
12556                         'for': id,
12557                         cls: 'control-label box-input-label',
12558                         //cls : 'input-group-addon',
12559                         html : this.fieldLabel
12560                         
12561                     },
12562                     
12563                     inputblock
12564                     
12565                 ];
12566
12567         } else {
12568             
12569                    Roo.log(" no label && no align");
12570                 cfg.cn = [
12571                     
12572                         inputblock
12573                     
12574                 ];
12575                 
12576                 
12577         };
12578         
12579         if(this.boxLabel){
12580             cfg.cn.push({
12581                 tag: 'label',
12582                 'for': id,
12583                 cls: 'box-label',
12584                 html: this.boxLabel
12585             })
12586         }
12587         
12588         return cfg;
12589         
12590     },
12591     
12592     /**
12593      * return the real input element.
12594      */
12595     inputEl: function ()
12596     {
12597         return this.el.select('input.form-box',true).first();
12598     },
12599     
12600     initEvents : function()
12601     {
12602 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12603         
12604         this.inputEl().on('click', this.onClick,  this);
12605         
12606     },
12607     
12608     onClick : function()
12609     {   
12610         this.setChecked(!this.checked);
12611     },
12612     
12613     setChecked : function(state,suppressEvent)
12614     {
12615         this.checked = state;
12616         
12617         this.inputEl().dom.checked = state;
12618         
12619         if(suppressEvent !== true){
12620             this.fireEvent('check', this, state);
12621         }
12622         
12623         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12624         
12625     },
12626     
12627     setValue : function(v,suppressEvent)
12628     {
12629         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12630     }
12631     
12632 });
12633
12634  
12635 /*
12636  * - LGPL
12637  *
12638  * Radio
12639  * 
12640  */
12641
12642 /**
12643  * @class Roo.bootstrap.Radio
12644  * @extends Roo.bootstrap.CheckBox
12645  * Bootstrap Radio class
12646
12647  * @constructor
12648  * Create a new Radio
12649  * @param {Object} config The config object
12650  */
12651
12652 Roo.bootstrap.Radio = function(config){
12653     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12654    
12655 };
12656
12657 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12658     
12659     inputType: 'radio',
12660     inputValue: '',
12661     valueOff: '',
12662     
12663     getAutoCreate : function()
12664     {
12665         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12666         
12667         var id = Roo.id();
12668         
12669         var cfg = {};
12670         
12671         cfg.cls = 'form-group' //input-group
12672         
12673         var input =  {
12674             tag: 'input',
12675             id : id,
12676             type : this.inputType,
12677             value : (!this.checked) ? this.valueOff : this.inputValue,
12678             cls : 'form-box',
12679             placeholder : this.placeholder || ''
12680             
12681         };
12682         
12683         if (this.disabled) {
12684             input.disabled=true;
12685         }
12686         
12687         if(this.checked){
12688             input.checked = this.checked;
12689         }
12690         
12691         if (this.name) {
12692             input.name = this.name;
12693         }
12694         
12695         if (this.size) {
12696             input.cls += ' input-' + this.size;
12697         }
12698         
12699         var settings=this;
12700         ['xs','sm','md','lg'].map(function(size){
12701             if (settings[size]) {
12702                 cfg.cls += ' col-' + size + '-' + settings[size];
12703             }
12704         });
12705         
12706         var inputblock = input;
12707         
12708         if (this.before || this.after) {
12709             
12710             inputblock = {
12711                 cls : 'input-group',
12712                 cn :  [] 
12713             };
12714             if (this.before) {
12715                 inputblock.cn.push({
12716                     tag :'span',
12717                     cls : 'input-group-addon',
12718                     html : this.before
12719                 });
12720             }
12721             inputblock.cn.push(input);
12722             if (this.after) {
12723                 inputblock.cn.push({
12724                     tag :'span',
12725                     cls : 'input-group-addon',
12726                     html : this.after
12727                 });
12728             }
12729             
12730         };
12731         
12732         if (align ==='left' && this.fieldLabel.length) {
12733                 Roo.log("left and has label");
12734                 cfg.cn = [
12735                     
12736                     {
12737                         tag: 'label',
12738                         'for' :  id,
12739                         cls : 'control-label col-md-' + this.labelWidth,
12740                         html : this.fieldLabel
12741                         
12742                     },
12743                     {
12744                         cls : "col-md-" + (12 - this.labelWidth), 
12745                         cn: [
12746                             inputblock
12747                         ]
12748                     }
12749                     
12750                 ];
12751         } else if ( this.fieldLabel.length) {
12752                 Roo.log(" label");
12753                  cfg.cn = [
12754                    
12755                     {
12756                         tag: 'label',
12757                         'for': id,
12758                         cls: 'control-label box-input-label',
12759                         //cls : 'input-group-addon',
12760                         html : this.fieldLabel
12761                         
12762                     },
12763                     
12764                     inputblock
12765                     
12766                 ];
12767
12768         } else {
12769             
12770                    Roo.log(" no label && no align");
12771                 cfg.cn = [
12772                     
12773                         inputblock
12774                     
12775                 ];
12776                 
12777                 
12778         };
12779         
12780         if(this.boxLabel){
12781             cfg.cn.push({
12782                 tag: 'label',
12783                 'for': id,
12784                 cls: 'box-label',
12785                 html: this.boxLabel
12786             })
12787         }
12788         
12789         return cfg;
12790         
12791     },
12792    
12793     onClick : function()
12794     {   
12795         this.setChecked(true);
12796     },
12797     
12798     setChecked : function(state,suppressEvent)
12799     {
12800         if(state){
12801             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12802                 v.dom.checked = false;
12803             });
12804         }
12805         
12806         this.checked = state;
12807         this.inputEl().dom.checked = state;
12808         
12809         if(suppressEvent !== true){
12810             this.fireEvent('check', this, state);
12811         }
12812         
12813         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12814         
12815     },
12816     
12817     getGroupValue : function()
12818     {
12819         var value = ''
12820         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12821             if(v.dom.checked == true){
12822                 value = v.dom.value;
12823             }
12824         });
12825         
12826         return value;
12827     },
12828     
12829     /**
12830      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12831      * @return {Mixed} value The field value
12832      */
12833     getValue : function(){
12834         return this.getGroupValue();
12835     }
12836     
12837 });
12838
12839  
12840 //<script type="text/javascript">
12841
12842 /*
12843  * Based  Ext JS Library 1.1.1
12844  * Copyright(c) 2006-2007, Ext JS, LLC.
12845  * LGPL
12846  *
12847  */
12848  
12849 /**
12850  * @class Roo.HtmlEditorCore
12851  * @extends Roo.Component
12852  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12853  *
12854  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12855  */
12856
12857 Roo.HtmlEditorCore = function(config){
12858     
12859     
12860     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12861     this.addEvents({
12862         /**
12863          * @event initialize
12864          * Fires when the editor is fully initialized (including the iframe)
12865          * @param {Roo.HtmlEditorCore} this
12866          */
12867         initialize: true,
12868         /**
12869          * @event activate
12870          * Fires when the editor is first receives the focus. Any insertion must wait
12871          * until after this event.
12872          * @param {Roo.HtmlEditorCore} this
12873          */
12874         activate: true,
12875          /**
12876          * @event beforesync
12877          * Fires before the textarea is updated with content from the editor iframe. Return false
12878          * to cancel the sync.
12879          * @param {Roo.HtmlEditorCore} this
12880          * @param {String} html
12881          */
12882         beforesync: true,
12883          /**
12884          * @event beforepush
12885          * Fires before the iframe editor is updated with content from the textarea. Return false
12886          * to cancel the push.
12887          * @param {Roo.HtmlEditorCore} this
12888          * @param {String} html
12889          */
12890         beforepush: true,
12891          /**
12892          * @event sync
12893          * Fires when the textarea is updated with content from the editor iframe.
12894          * @param {Roo.HtmlEditorCore} this
12895          * @param {String} html
12896          */
12897         sync: true,
12898          /**
12899          * @event push
12900          * Fires when the iframe editor is updated with content from the textarea.
12901          * @param {Roo.HtmlEditorCore} this
12902          * @param {String} html
12903          */
12904         push: true,
12905         
12906         /**
12907          * @event editorevent
12908          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12909          * @param {Roo.HtmlEditorCore} this
12910          */
12911         editorevent: true
12912     });
12913      
12914 };
12915
12916
12917 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12918
12919
12920      /**
12921      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12922      */
12923     
12924     owner : false,
12925     
12926      /**
12927      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12928      *                        Roo.resizable.
12929      */
12930     resizable : false,
12931      /**
12932      * @cfg {Number} height (in pixels)
12933      */   
12934     height: 300,
12935    /**
12936      * @cfg {Number} width (in pixels)
12937      */   
12938     width: 500,
12939     
12940     /**
12941      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12942      * 
12943      */
12944     stylesheets: false,
12945     
12946     // id of frame..
12947     frameId: false,
12948     
12949     // private properties
12950     validationEvent : false,
12951     deferHeight: true,
12952     initialized : false,
12953     activated : false,
12954     sourceEditMode : false,
12955     onFocus : Roo.emptyFn,
12956     iframePad:3,
12957     hideMode:'offsets',
12958     
12959     clearUp: true,
12960     
12961      
12962     
12963
12964     /**
12965      * Protected method that will not generally be called directly. It
12966      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12967      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12968      */
12969     getDocMarkup : function(){
12970         // body styles..
12971         var st = '';
12972         Roo.log(this.stylesheets);
12973         
12974         // inherit styels from page...?? 
12975         if (this.stylesheets === false) {
12976             
12977             Roo.get(document.head).select('style').each(function(node) {
12978                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12979             });
12980             
12981             Roo.get(document.head).select('link').each(function(node) { 
12982                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12983             });
12984             
12985         } else if (!this.stylesheets.length) {
12986                 // simple..
12987                 st = '<style type="text/css">' +
12988                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12989                    '</style>';
12990         } else {
12991             Roo.each(this.stylesheets, function(s) {
12992                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12993             });
12994             
12995         }
12996         
12997         st +=  '<style type="text/css">' +
12998             'IMG { cursor: pointer } ' +
12999         '</style>';
13000
13001         
13002         return '<html><head>' + st  +
13003             //<style type="text/css">' +
13004             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13005             //'</style>' +
13006             ' </head><body class="roo-htmleditor-body"></body></html>';
13007     },
13008
13009     // private
13010     onRender : function(ct, position)
13011     {
13012         var _t = this;
13013         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13014         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13015         
13016         
13017         this.el.dom.style.border = '0 none';
13018         this.el.dom.setAttribute('tabIndex', -1);
13019         this.el.addClass('x-hidden hide');
13020         
13021         
13022         
13023         if(Roo.isIE){ // fix IE 1px bogus margin
13024             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13025         }
13026        
13027         
13028         this.frameId = Roo.id();
13029         
13030          
13031         
13032         var iframe = this.owner.wrap.createChild({
13033             tag: 'iframe',
13034             cls: 'form-control', // bootstrap..
13035             id: this.frameId,
13036             name: this.frameId,
13037             frameBorder : 'no',
13038             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
13039         }, this.el
13040         );
13041         
13042         
13043         this.iframe = iframe.dom;
13044
13045          this.assignDocWin();
13046         
13047         this.doc.designMode = 'on';
13048        
13049         this.doc.open();
13050         this.doc.write(this.getDocMarkup());
13051         this.doc.close();
13052
13053         
13054         var task = { // must defer to wait for browser to be ready
13055             run : function(){
13056                 //console.log("run task?" + this.doc.readyState);
13057                 this.assignDocWin();
13058                 if(this.doc.body || this.doc.readyState == 'complete'){
13059                     try {
13060                         this.doc.designMode="on";
13061                     } catch (e) {
13062                         return;
13063                     }
13064                     Roo.TaskMgr.stop(task);
13065                     this.initEditor.defer(10, this);
13066                 }
13067             },
13068             interval : 10,
13069             duration: 10000,
13070             scope: this
13071         };
13072         Roo.TaskMgr.start(task);
13073
13074         
13075          
13076     },
13077
13078     // private
13079     onResize : function(w, h)
13080     {
13081          Roo.log('resize: ' +w + ',' + h );
13082         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13083         if(!this.iframe){
13084             return;
13085         }
13086         if(typeof w == 'number'){
13087             
13088             this.iframe.style.width = w + 'px';
13089         }
13090         if(typeof h == 'number'){
13091             
13092             this.iframe.style.height = h + 'px';
13093             if(this.doc){
13094                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13095             }
13096         }
13097         
13098     },
13099
13100     /**
13101      * Toggles the editor between standard and source edit mode.
13102      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13103      */
13104     toggleSourceEdit : function(sourceEditMode){
13105         
13106         this.sourceEditMode = sourceEditMode === true;
13107         
13108         if(this.sourceEditMode){
13109  
13110             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
13111             
13112         }else{
13113             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13114             //this.iframe.className = '';
13115             this.deferFocus();
13116         }
13117         //this.setSize(this.owner.wrap.getSize());
13118         //this.fireEvent('editmodechange', this, this.sourceEditMode);
13119     },
13120
13121     
13122   
13123
13124     /**
13125      * Protected method that will not generally be called directly. If you need/want
13126      * custom HTML cleanup, this is the method you should override.
13127      * @param {String} html The HTML to be cleaned
13128      * return {String} The cleaned HTML
13129      */
13130     cleanHtml : function(html){
13131         html = String(html);
13132         if(html.length > 5){
13133             if(Roo.isSafari){ // strip safari nonsense
13134                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13135             }
13136         }
13137         if(html == '&nbsp;'){
13138             html = '';
13139         }
13140         return html;
13141     },
13142
13143     /**
13144      * HTML Editor -> Textarea
13145      * Protected method that will not generally be called directly. Syncs the contents
13146      * of the editor iframe with the textarea.
13147      */
13148     syncValue : function(){
13149         if(this.initialized){
13150             var bd = (this.doc.body || this.doc.documentElement);
13151             //this.cleanUpPaste(); -- this is done else where and causes havoc..
13152             var html = bd.innerHTML;
13153             if(Roo.isSafari){
13154                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13155                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13156                 if(m && m[1]){
13157                     html = '<div style="'+m[0]+'">' + html + '</div>';
13158                 }
13159             }
13160             html = this.cleanHtml(html);
13161             // fix up the special chars.. normaly like back quotes in word...
13162             // however we do not want to do this with chinese..
13163             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13164                 var cc = b.charCodeAt();
13165                 if (
13166                     (cc >= 0x4E00 && cc < 0xA000 ) ||
13167                     (cc >= 0x3400 && cc < 0x4E00 ) ||
13168                     (cc >= 0xf900 && cc < 0xfb00 )
13169                 ) {
13170                         return b;
13171                 }
13172                 return "&#"+cc+";" 
13173             });
13174             if(this.owner.fireEvent('beforesync', this, html) !== false){
13175                 this.el.dom.value = html;
13176                 this.owner.fireEvent('sync', this, html);
13177             }
13178         }
13179     },
13180
13181     /**
13182      * Protected method that will not generally be called directly. Pushes the value of the textarea
13183      * into the iframe editor.
13184      */
13185     pushValue : function(){
13186         if(this.initialized){
13187             var v = this.el.dom.value.trim();
13188             
13189 //            if(v.length < 1){
13190 //                v = '&#160;';
13191 //            }
13192             
13193             if(this.owner.fireEvent('beforepush', this, v) !== false){
13194                 var d = (this.doc.body || this.doc.documentElement);
13195                 d.innerHTML = v;
13196                 this.cleanUpPaste();
13197                 this.el.dom.value = d.innerHTML;
13198                 this.owner.fireEvent('push', this, v);
13199             }
13200         }
13201     },
13202
13203     // private
13204     deferFocus : function(){
13205         this.focus.defer(10, this);
13206     },
13207
13208     // doc'ed in Field
13209     focus : function(){
13210         if(this.win && !this.sourceEditMode){
13211             this.win.focus();
13212         }else{
13213             this.el.focus();
13214         }
13215     },
13216     
13217     assignDocWin: function()
13218     {
13219         var iframe = this.iframe;
13220         
13221          if(Roo.isIE){
13222             this.doc = iframe.contentWindow.document;
13223             this.win = iframe.contentWindow;
13224         } else {
13225             if (!Roo.get(this.frameId)) {
13226                 return;
13227             }
13228             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13229             this.win = Roo.get(this.frameId).dom.contentWindow;
13230         }
13231     },
13232     
13233     // private
13234     initEditor : function(){
13235         //console.log("INIT EDITOR");
13236         this.assignDocWin();
13237         
13238         
13239         
13240         this.doc.designMode="on";
13241         this.doc.open();
13242         this.doc.write(this.getDocMarkup());
13243         this.doc.close();
13244         
13245         var dbody = (this.doc.body || this.doc.documentElement);
13246         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13247         // this copies styles from the containing element into thsi one..
13248         // not sure why we need all of this..
13249         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13250         ss['background-attachment'] = 'fixed'; // w3c
13251         dbody.bgProperties = 'fixed'; // ie
13252         Roo.DomHelper.applyStyles(dbody, ss);
13253         Roo.EventManager.on(this.doc, {
13254             //'mousedown': this.onEditorEvent,
13255             'mouseup': this.onEditorEvent,
13256             'dblclick': this.onEditorEvent,
13257             'click': this.onEditorEvent,
13258             'keyup': this.onEditorEvent,
13259             buffer:100,
13260             scope: this
13261         });
13262         if(Roo.isGecko){
13263             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13264         }
13265         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13266             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13267         }
13268         this.initialized = true;
13269
13270         this.owner.fireEvent('initialize', this);
13271         this.pushValue();
13272     },
13273
13274     // private
13275     onDestroy : function(){
13276         
13277         
13278         
13279         if(this.rendered){
13280             
13281             //for (var i =0; i < this.toolbars.length;i++) {
13282             //    // fixme - ask toolbars for heights?
13283             //    this.toolbars[i].onDestroy();
13284            // }
13285             
13286             //this.wrap.dom.innerHTML = '';
13287             //this.wrap.remove();
13288         }
13289     },
13290
13291     // private
13292     onFirstFocus : function(){
13293         
13294         this.assignDocWin();
13295         
13296         
13297         this.activated = true;
13298          
13299     
13300         if(Roo.isGecko){ // prevent silly gecko errors
13301             this.win.focus();
13302             var s = this.win.getSelection();
13303             if(!s.focusNode || s.focusNode.nodeType != 3){
13304                 var r = s.getRangeAt(0);
13305                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13306                 r.collapse(true);
13307                 this.deferFocus();
13308             }
13309             try{
13310                 this.execCmd('useCSS', true);
13311                 this.execCmd('styleWithCSS', false);
13312             }catch(e){}
13313         }
13314         this.owner.fireEvent('activate', this);
13315     },
13316
13317     // private
13318     adjustFont: function(btn){
13319         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13320         //if(Roo.isSafari){ // safari
13321         //    adjust *= 2;
13322        // }
13323         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13324         if(Roo.isSafari){ // safari
13325             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13326             v =  (v < 10) ? 10 : v;
13327             v =  (v > 48) ? 48 : v;
13328             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13329             
13330         }
13331         
13332         
13333         v = Math.max(1, v+adjust);
13334         
13335         this.execCmd('FontSize', v  );
13336     },
13337
13338     onEditorEvent : function(e){
13339         this.owner.fireEvent('editorevent', this, e);
13340       //  this.updateToolbar();
13341         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13342     },
13343
13344     insertTag : function(tg)
13345     {
13346         // could be a bit smarter... -> wrap the current selected tRoo..
13347         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13348             
13349             range = this.createRange(this.getSelection());
13350             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13351             wrappingNode.appendChild(range.extractContents());
13352             range.insertNode(wrappingNode);
13353
13354             return;
13355             
13356             
13357             
13358         }
13359         this.execCmd("formatblock",   tg);
13360         
13361     },
13362     
13363     insertText : function(txt)
13364     {
13365         
13366         
13367         var range = this.createRange();
13368         range.deleteContents();
13369                //alert(Sender.getAttribute('label'));
13370                
13371         range.insertNode(this.doc.createTextNode(txt));
13372     } ,
13373     
13374      
13375
13376     /**
13377      * Executes a Midas editor command on the editor document and performs necessary focus and
13378      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13379      * @param {String} cmd The Midas command
13380      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13381      */
13382     relayCmd : function(cmd, value){
13383         this.win.focus();
13384         this.execCmd(cmd, value);
13385         this.owner.fireEvent('editorevent', this);
13386         //this.updateToolbar();
13387         this.owner.deferFocus();
13388     },
13389
13390     /**
13391      * Executes a Midas editor command directly on the editor document.
13392      * For visual commands, you should use {@link #relayCmd} instead.
13393      * <b>This should only be called after the editor is initialized.</b>
13394      * @param {String} cmd The Midas command
13395      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13396      */
13397     execCmd : function(cmd, value){
13398         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13399         this.syncValue();
13400     },
13401  
13402  
13403    
13404     /**
13405      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13406      * to insert tRoo.
13407      * @param {String} text | dom node.. 
13408      */
13409     insertAtCursor : function(text)
13410     {
13411         
13412         
13413         
13414         if(!this.activated){
13415             return;
13416         }
13417         /*
13418         if(Roo.isIE){
13419             this.win.focus();
13420             var r = this.doc.selection.createRange();
13421             if(r){
13422                 r.collapse(true);
13423                 r.pasteHTML(text);
13424                 this.syncValue();
13425                 this.deferFocus();
13426             
13427             }
13428             return;
13429         }
13430         */
13431         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13432             this.win.focus();
13433             
13434             
13435             // from jquery ui (MIT licenced)
13436             var range, node;
13437             var win = this.win;
13438             
13439             if (win.getSelection && win.getSelection().getRangeAt) {
13440                 range = win.getSelection().getRangeAt(0);
13441                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13442                 range.insertNode(node);
13443             } else if (win.document.selection && win.document.selection.createRange) {
13444                 // no firefox support
13445                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13446                 win.document.selection.createRange().pasteHTML(txt);
13447             } else {
13448                 // no firefox support
13449                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13450                 this.execCmd('InsertHTML', txt);
13451             } 
13452             
13453             this.syncValue();
13454             
13455             this.deferFocus();
13456         }
13457     },
13458  // private
13459     mozKeyPress : function(e){
13460         if(e.ctrlKey){
13461             var c = e.getCharCode(), cmd;
13462           
13463             if(c > 0){
13464                 c = String.fromCharCode(c).toLowerCase();
13465                 switch(c){
13466                     case 'b':
13467                         cmd = 'bold';
13468                         break;
13469                     case 'i':
13470                         cmd = 'italic';
13471                         break;
13472                     
13473                     case 'u':
13474                         cmd = 'underline';
13475                         break;
13476                     
13477                     case 'v':
13478                         this.cleanUpPaste.defer(100, this);
13479                         return;
13480                         
13481                 }
13482                 if(cmd){
13483                     this.win.focus();
13484                     this.execCmd(cmd);
13485                     this.deferFocus();
13486                     e.preventDefault();
13487                 }
13488                 
13489             }
13490         }
13491     },
13492
13493     // private
13494     fixKeys : function(){ // load time branching for fastest keydown performance
13495         if(Roo.isIE){
13496             return function(e){
13497                 var k = e.getKey(), r;
13498                 if(k == e.TAB){
13499                     e.stopEvent();
13500                     r = this.doc.selection.createRange();
13501                     if(r){
13502                         r.collapse(true);
13503                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13504                         this.deferFocus();
13505                     }
13506                     return;
13507                 }
13508                 
13509                 if(k == e.ENTER){
13510                     r = this.doc.selection.createRange();
13511                     if(r){
13512                         var target = r.parentElement();
13513                         if(!target || target.tagName.toLowerCase() != 'li'){
13514                             e.stopEvent();
13515                             r.pasteHTML('<br />');
13516                             r.collapse(false);
13517                             r.select();
13518                         }
13519                     }
13520                 }
13521                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13522                     this.cleanUpPaste.defer(100, this);
13523                     return;
13524                 }
13525                 
13526                 
13527             };
13528         }else if(Roo.isOpera){
13529             return function(e){
13530                 var k = e.getKey();
13531                 if(k == e.TAB){
13532                     e.stopEvent();
13533                     this.win.focus();
13534                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13535                     this.deferFocus();
13536                 }
13537                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13538                     this.cleanUpPaste.defer(100, this);
13539                     return;
13540                 }
13541                 
13542             };
13543         }else if(Roo.isSafari){
13544             return function(e){
13545                 var k = e.getKey();
13546                 
13547                 if(k == e.TAB){
13548                     e.stopEvent();
13549                     this.execCmd('InsertText','\t');
13550                     this.deferFocus();
13551                     return;
13552                 }
13553                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13554                     this.cleanUpPaste.defer(100, this);
13555                     return;
13556                 }
13557                 
13558              };
13559         }
13560     }(),
13561     
13562     getAllAncestors: function()
13563     {
13564         var p = this.getSelectedNode();
13565         var a = [];
13566         if (!p) {
13567             a.push(p); // push blank onto stack..
13568             p = this.getParentElement();
13569         }
13570         
13571         
13572         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13573             a.push(p);
13574             p = p.parentNode;
13575         }
13576         a.push(this.doc.body);
13577         return a;
13578     },
13579     lastSel : false,
13580     lastSelNode : false,
13581     
13582     
13583     getSelection : function() 
13584     {
13585         this.assignDocWin();
13586         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13587     },
13588     
13589     getSelectedNode: function() 
13590     {
13591         // this may only work on Gecko!!!
13592         
13593         // should we cache this!!!!
13594         
13595         
13596         
13597          
13598         var range = this.createRange(this.getSelection()).cloneRange();
13599         
13600         if (Roo.isIE) {
13601             var parent = range.parentElement();
13602             while (true) {
13603                 var testRange = range.duplicate();
13604                 testRange.moveToElementText(parent);
13605                 if (testRange.inRange(range)) {
13606                     break;
13607                 }
13608                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13609                     break;
13610                 }
13611                 parent = parent.parentElement;
13612             }
13613             return parent;
13614         }
13615         
13616         // is ancestor a text element.
13617         var ac =  range.commonAncestorContainer;
13618         if (ac.nodeType == 3) {
13619             ac = ac.parentNode;
13620         }
13621         
13622         var ar = ac.childNodes;
13623          
13624         var nodes = [];
13625         var other_nodes = [];
13626         var has_other_nodes = false;
13627         for (var i=0;i<ar.length;i++) {
13628             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13629                 continue;
13630             }
13631             // fullly contained node.
13632             
13633             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13634                 nodes.push(ar[i]);
13635                 continue;
13636             }
13637             
13638             // probably selected..
13639             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13640                 other_nodes.push(ar[i]);
13641                 continue;
13642             }
13643             // outer..
13644             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13645                 continue;
13646             }
13647             
13648             
13649             has_other_nodes = true;
13650         }
13651         if (!nodes.length && other_nodes.length) {
13652             nodes= other_nodes;
13653         }
13654         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13655             return false;
13656         }
13657         
13658         return nodes[0];
13659     },
13660     createRange: function(sel)
13661     {
13662         // this has strange effects when using with 
13663         // top toolbar - not sure if it's a great idea.
13664         //this.editor.contentWindow.focus();
13665         if (typeof sel != "undefined") {
13666             try {
13667                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13668             } catch(e) {
13669                 return this.doc.createRange();
13670             }
13671         } else {
13672             return this.doc.createRange();
13673         }
13674     },
13675     getParentElement: function()
13676     {
13677         
13678         this.assignDocWin();
13679         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13680         
13681         var range = this.createRange(sel);
13682          
13683         try {
13684             var p = range.commonAncestorContainer;
13685             while (p.nodeType == 3) { // text node
13686                 p = p.parentNode;
13687             }
13688             return p;
13689         } catch (e) {
13690             return null;
13691         }
13692     
13693     },
13694     /***
13695      *
13696      * Range intersection.. the hard stuff...
13697      *  '-1' = before
13698      *  '0' = hits..
13699      *  '1' = after.
13700      *         [ -- selected range --- ]
13701      *   [fail]                        [fail]
13702      *
13703      *    basically..
13704      *      if end is before start or  hits it. fail.
13705      *      if start is after end or hits it fail.
13706      *
13707      *   if either hits (but other is outside. - then it's not 
13708      *   
13709      *    
13710      **/
13711     
13712     
13713     // @see http://www.thismuchiknow.co.uk/?p=64.
13714     rangeIntersectsNode : function(range, node)
13715     {
13716         var nodeRange = node.ownerDocument.createRange();
13717         try {
13718             nodeRange.selectNode(node);
13719         } catch (e) {
13720             nodeRange.selectNodeContents(node);
13721         }
13722     
13723         var rangeStartRange = range.cloneRange();
13724         rangeStartRange.collapse(true);
13725     
13726         var rangeEndRange = range.cloneRange();
13727         rangeEndRange.collapse(false);
13728     
13729         var nodeStartRange = nodeRange.cloneRange();
13730         nodeStartRange.collapse(true);
13731     
13732         var nodeEndRange = nodeRange.cloneRange();
13733         nodeEndRange.collapse(false);
13734     
13735         return rangeStartRange.compareBoundaryPoints(
13736                  Range.START_TO_START, nodeEndRange) == -1 &&
13737                rangeEndRange.compareBoundaryPoints(
13738                  Range.START_TO_START, nodeStartRange) == 1;
13739         
13740          
13741     },
13742     rangeCompareNode : function(range, node)
13743     {
13744         var nodeRange = node.ownerDocument.createRange();
13745         try {
13746             nodeRange.selectNode(node);
13747         } catch (e) {
13748             nodeRange.selectNodeContents(node);
13749         }
13750         
13751         
13752         range.collapse(true);
13753     
13754         nodeRange.collapse(true);
13755      
13756         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13757         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13758          
13759         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13760         
13761         var nodeIsBefore   =  ss == 1;
13762         var nodeIsAfter    = ee == -1;
13763         
13764         if (nodeIsBefore && nodeIsAfter)
13765             return 0; // outer
13766         if (!nodeIsBefore && nodeIsAfter)
13767             return 1; //right trailed.
13768         
13769         if (nodeIsBefore && !nodeIsAfter)
13770             return 2;  // left trailed.
13771         // fully contined.
13772         return 3;
13773     },
13774
13775     // private? - in a new class?
13776     cleanUpPaste :  function()
13777     {
13778         // cleans up the whole document..
13779         Roo.log('cleanuppaste');
13780         
13781         this.cleanUpChildren(this.doc.body);
13782         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13783         if (clean != this.doc.body.innerHTML) {
13784             this.doc.body.innerHTML = clean;
13785         }
13786         
13787     },
13788     
13789     cleanWordChars : function(input) {// change the chars to hex code
13790         var he = Roo.HtmlEditorCore;
13791         
13792         var output = input;
13793         Roo.each(he.swapCodes, function(sw) { 
13794             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13795             
13796             output = output.replace(swapper, sw[1]);
13797         });
13798         
13799         return output;
13800     },
13801     
13802     
13803     cleanUpChildren : function (n)
13804     {
13805         if (!n.childNodes.length) {
13806             return;
13807         }
13808         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13809            this.cleanUpChild(n.childNodes[i]);
13810         }
13811     },
13812     
13813     
13814         
13815     
13816     cleanUpChild : function (node)
13817     {
13818         var ed = this;
13819         //console.log(node);
13820         if (node.nodeName == "#text") {
13821             // clean up silly Windows -- stuff?
13822             return; 
13823         }
13824         if (node.nodeName == "#comment") {
13825             node.parentNode.removeChild(node);
13826             // clean up silly Windows -- stuff?
13827             return; 
13828         }
13829         
13830         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13831             // remove node.
13832             node.parentNode.removeChild(node);
13833             return;
13834             
13835         }
13836         
13837         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13838         
13839         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13840         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13841         
13842         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13843         //    remove_keep_children = true;
13844         //}
13845         
13846         if (remove_keep_children) {
13847             this.cleanUpChildren(node);
13848             // inserts everything just before this node...
13849             while (node.childNodes.length) {
13850                 var cn = node.childNodes[0];
13851                 node.removeChild(cn);
13852                 node.parentNode.insertBefore(cn, node);
13853             }
13854             node.parentNode.removeChild(node);
13855             return;
13856         }
13857         
13858         if (!node.attributes || !node.attributes.length) {
13859             this.cleanUpChildren(node);
13860             return;
13861         }
13862         
13863         function cleanAttr(n,v)
13864         {
13865             
13866             if (v.match(/^\./) || v.match(/^\//)) {
13867                 return;
13868             }
13869             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13870                 return;
13871             }
13872             if (v.match(/^#/)) {
13873                 return;
13874             }
13875 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13876             node.removeAttribute(n);
13877             
13878         }
13879         
13880         function cleanStyle(n,v)
13881         {
13882             if (v.match(/expression/)) { //XSS?? should we even bother..
13883                 node.removeAttribute(n);
13884                 return;
13885             }
13886             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13887             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13888             
13889             
13890             var parts = v.split(/;/);
13891             var clean = [];
13892             
13893             Roo.each(parts, function(p) {
13894                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13895                 if (!p.length) {
13896                     return true;
13897                 }
13898                 var l = p.split(':').shift().replace(/\s+/g,'');
13899                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13900                 
13901                 if ( cblack.indexOf(l) > -1) {
13902 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13903                     //node.removeAttribute(n);
13904                     return true;
13905                 }
13906                 //Roo.log()
13907                 // only allow 'c whitelisted system attributes'
13908                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13909 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13910                     //node.removeAttribute(n);
13911                     return true;
13912                 }
13913                 
13914                 
13915                  
13916                 
13917                 clean.push(p);
13918                 return true;
13919             });
13920             if (clean.length) { 
13921                 node.setAttribute(n, clean.join(';'));
13922             } else {
13923                 node.removeAttribute(n);
13924             }
13925             
13926         }
13927         
13928         
13929         for (var i = node.attributes.length-1; i > -1 ; i--) {
13930             var a = node.attributes[i];
13931             //console.log(a);
13932             
13933             if (a.name.toLowerCase().substr(0,2)=='on')  {
13934                 node.removeAttribute(a.name);
13935                 continue;
13936             }
13937             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13938                 node.removeAttribute(a.name);
13939                 continue;
13940             }
13941             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13942                 cleanAttr(a.name,a.value); // fixme..
13943                 continue;
13944             }
13945             if (a.name == 'style') {
13946                 cleanStyle(a.name,a.value);
13947                 continue;
13948             }
13949             /// clean up MS crap..
13950             // tecnically this should be a list of valid class'es..
13951             
13952             
13953             if (a.name == 'class') {
13954                 if (a.value.match(/^Mso/)) {
13955                     node.className = '';
13956                 }
13957                 
13958                 if (a.value.match(/body/)) {
13959                     node.className = '';
13960                 }
13961                 continue;
13962             }
13963             
13964             // style cleanup!?
13965             // class cleanup?
13966             
13967         }
13968         
13969         
13970         this.cleanUpChildren(node);
13971         
13972         
13973     }
13974     
13975     
13976     // hide stuff that is not compatible
13977     /**
13978      * @event blur
13979      * @hide
13980      */
13981     /**
13982      * @event change
13983      * @hide
13984      */
13985     /**
13986      * @event focus
13987      * @hide
13988      */
13989     /**
13990      * @event specialkey
13991      * @hide
13992      */
13993     /**
13994      * @cfg {String} fieldClass @hide
13995      */
13996     /**
13997      * @cfg {String} focusClass @hide
13998      */
13999     /**
14000      * @cfg {String} autoCreate @hide
14001      */
14002     /**
14003      * @cfg {String} inputType @hide
14004      */
14005     /**
14006      * @cfg {String} invalidClass @hide
14007      */
14008     /**
14009      * @cfg {String} invalidText @hide
14010      */
14011     /**
14012      * @cfg {String} msgFx @hide
14013      */
14014     /**
14015      * @cfg {String} validateOnBlur @hide
14016      */
14017 });
14018
14019 Roo.HtmlEditorCore.white = [
14020         'area', 'br', 'img', 'input', 'hr', 'wbr',
14021         
14022        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
14023        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
14024        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
14025        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
14026        'table',   'ul',         'xmp', 
14027        
14028        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
14029       'thead',   'tr', 
14030      
14031       'dir', 'menu', 'ol', 'ul', 'dl',
14032        
14033       'embed',  'object'
14034 ];
14035
14036
14037 Roo.HtmlEditorCore.black = [
14038     //    'embed',  'object', // enable - backend responsiblity to clean thiese
14039         'applet', // 
14040         'base',   'basefont', 'bgsound', 'blink',  'body', 
14041         'frame',  'frameset', 'head',    'html',   'ilayer', 
14042         'iframe', 'layer',  'link',     'meta',    'object',   
14043         'script', 'style' ,'title',  'xml' // clean later..
14044 ];
14045 Roo.HtmlEditorCore.clean = [
14046     'script', 'style', 'title', 'xml'
14047 ];
14048 Roo.HtmlEditorCore.remove = [
14049     'font'
14050 ];
14051 // attributes..
14052
14053 Roo.HtmlEditorCore.ablack = [
14054     'on'
14055 ];
14056     
14057 Roo.HtmlEditorCore.aclean = [ 
14058     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
14059 ];
14060
14061 // protocols..
14062 Roo.HtmlEditorCore.pwhite= [
14063         'http',  'https',  'mailto'
14064 ];
14065
14066 // white listed style attributes.
14067 Roo.HtmlEditorCore.cwhite= [
14068       //  'text-align', /// default is to allow most things..
14069       
14070          
14071 //        'font-size'//??
14072 ];
14073
14074 // black listed style attributes.
14075 Roo.HtmlEditorCore.cblack= [
14076       //  'font-size' -- this can be set by the project 
14077 ];
14078
14079
14080 Roo.HtmlEditorCore.swapCodes   =[ 
14081     [    8211, "--" ], 
14082     [    8212, "--" ], 
14083     [    8216,  "'" ],  
14084     [    8217, "'" ],  
14085     [    8220, '"' ],  
14086     [    8221, '"' ],  
14087     [    8226, "*" ],  
14088     [    8230, "..." ]
14089 ]; 
14090
14091     /*
14092  * - LGPL
14093  *
14094  * HtmlEditor
14095  * 
14096  */
14097
14098 /**
14099  * @class Roo.bootstrap.HtmlEditor
14100  * @extends Roo.bootstrap.TextArea
14101  * Bootstrap HtmlEditor class
14102
14103  * @constructor
14104  * Create a new HtmlEditor
14105  * @param {Object} config The config object
14106  */
14107
14108 Roo.bootstrap.HtmlEditor = function(config){
14109     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14110     if (!this.toolbars) {
14111         this.toolbars = [];
14112     }
14113     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14114     this.addEvents({
14115             /**
14116              * @event initialize
14117              * Fires when the editor is fully initialized (including the iframe)
14118              * @param {HtmlEditor} this
14119              */
14120             initialize: true,
14121             /**
14122              * @event activate
14123              * Fires when the editor is first receives the focus. Any insertion must wait
14124              * until after this event.
14125              * @param {HtmlEditor} this
14126              */
14127             activate: true,
14128              /**
14129              * @event beforesync
14130              * Fires before the textarea is updated with content from the editor iframe. Return false
14131              * to cancel the sync.
14132              * @param {HtmlEditor} this
14133              * @param {String} html
14134              */
14135             beforesync: true,
14136              /**
14137              * @event beforepush
14138              * Fires before the iframe editor is updated with content from the textarea. Return false
14139              * to cancel the push.
14140              * @param {HtmlEditor} this
14141              * @param {String} html
14142              */
14143             beforepush: true,
14144              /**
14145              * @event sync
14146              * Fires when the textarea is updated with content from the editor iframe.
14147              * @param {HtmlEditor} this
14148              * @param {String} html
14149              */
14150             sync: true,
14151              /**
14152              * @event push
14153              * Fires when the iframe editor is updated with content from the textarea.
14154              * @param {HtmlEditor} this
14155              * @param {String} html
14156              */
14157             push: true,
14158              /**
14159              * @event editmodechange
14160              * Fires when the editor switches edit modes
14161              * @param {HtmlEditor} this
14162              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14163              */
14164             editmodechange: true,
14165             /**
14166              * @event editorevent
14167              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14168              * @param {HtmlEditor} this
14169              */
14170             editorevent: true,
14171             /**
14172              * @event firstfocus
14173              * Fires when on first focus - needed by toolbars..
14174              * @param {HtmlEditor} this
14175              */
14176             firstfocus: true,
14177             /**
14178              * @event autosave
14179              * Auto save the htmlEditor value as a file into Events
14180              * @param {HtmlEditor} this
14181              */
14182             autosave: true,
14183             /**
14184              * @event savedpreview
14185              * preview the saved version of htmlEditor
14186              * @param {HtmlEditor} this
14187              */
14188             savedpreview: true
14189         });
14190 };
14191
14192
14193 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
14194     
14195     
14196       /**
14197      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14198      */
14199     toolbars : false,
14200    
14201      /**
14202      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14203      *                        Roo.resizable.
14204      */
14205     resizable : false,
14206      /**
14207      * @cfg {Number} height (in pixels)
14208      */   
14209     height: 300,
14210    /**
14211      * @cfg {Number} width (in pixels)
14212      */   
14213     width: false,
14214     
14215     /**
14216      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14217      * 
14218      */
14219     stylesheets: false,
14220     
14221     // id of frame..
14222     frameId: false,
14223     
14224     // private properties
14225     validationEvent : false,
14226     deferHeight: true,
14227     initialized : false,
14228     activated : false,
14229     
14230     onFocus : Roo.emptyFn,
14231     iframePad:3,
14232     hideMode:'offsets',
14233     
14234     
14235     tbContainer : false,
14236     
14237     toolbarContainer :function() {
14238         return this.wrap.select('.x-html-editor-tb',true).first();
14239     },
14240
14241     /**
14242      * Protected method that will not generally be called directly. It
14243      * is called when the editor creates its toolbar. Override this method if you need to
14244      * add custom toolbar buttons.
14245      * @param {HtmlEditor} editor
14246      */
14247     createToolbar : function(){
14248         
14249         Roo.log("create toolbars");
14250         
14251         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14252         this.toolbars[0].render(this.toolbarContainer());
14253         
14254         return;
14255         
14256 //        if (!editor.toolbars || !editor.toolbars.length) {
14257 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14258 //        }
14259 //        
14260 //        for (var i =0 ; i < editor.toolbars.length;i++) {
14261 //            editor.toolbars[i] = Roo.factory(
14262 //                    typeof(editor.toolbars[i]) == 'string' ?
14263 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
14264 //                Roo.bootstrap.HtmlEditor);
14265 //            editor.toolbars[i].init(editor);
14266 //        }
14267     },
14268
14269      
14270     // private
14271     onRender : function(ct, position)
14272     {
14273        // Roo.log("Call onRender: " + this.xtype);
14274         var _t = this;
14275         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14276       
14277         this.wrap = this.inputEl().wrap({
14278             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14279         });
14280         
14281         this.editorcore.onRender(ct, position);
14282          
14283         if (this.resizable) {
14284             this.resizeEl = new Roo.Resizable(this.wrap, {
14285                 pinned : true,
14286                 wrap: true,
14287                 dynamic : true,
14288                 minHeight : this.height,
14289                 height: this.height,
14290                 handles : this.resizable,
14291                 width: this.width,
14292                 listeners : {
14293                     resize : function(r, w, h) {
14294                         _t.onResize(w,h); // -something
14295                     }
14296                 }
14297             });
14298             
14299         }
14300         this.createToolbar(this);
14301        
14302         
14303         if(!this.width && this.resizable){
14304             this.setSize(this.wrap.getSize());
14305         }
14306         if (this.resizeEl) {
14307             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14308             // should trigger onReize..
14309         }
14310         
14311     },
14312
14313     // private
14314     onResize : function(w, h)
14315     {
14316         Roo.log('resize: ' +w + ',' + h );
14317         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14318         var ew = false;
14319         var eh = false;
14320         
14321         if(this.inputEl() ){
14322             if(typeof w == 'number'){
14323                 var aw = w - this.wrap.getFrameWidth('lr');
14324                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14325                 ew = aw;
14326             }
14327             if(typeof h == 'number'){
14328                  var tbh = -11;  // fixme it needs to tool bar size!
14329                 for (var i =0; i < this.toolbars.length;i++) {
14330                     // fixme - ask toolbars for heights?
14331                     tbh += this.toolbars[i].el.getHeight();
14332                     //if (this.toolbars[i].footer) {
14333                     //    tbh += this.toolbars[i].footer.el.getHeight();
14334                     //}
14335                 }
14336               
14337                 
14338                 
14339                 
14340                 
14341                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14342                 ah -= 5; // knock a few pixes off for look..
14343                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14344                 var eh = ah;
14345             }
14346         }
14347         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14348         this.editorcore.onResize(ew,eh);
14349         
14350     },
14351
14352     /**
14353      * Toggles the editor between standard and source edit mode.
14354      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14355      */
14356     toggleSourceEdit : function(sourceEditMode)
14357     {
14358         this.editorcore.toggleSourceEdit(sourceEditMode);
14359         
14360         if(this.editorcore.sourceEditMode){
14361             Roo.log('editor - showing textarea');
14362             
14363 //            Roo.log('in');
14364 //            Roo.log(this.syncValue());
14365             this.syncValue();
14366             this.inputEl().removeClass('hide');
14367             this.inputEl().dom.removeAttribute('tabIndex');
14368             this.inputEl().focus();
14369         }else{
14370             Roo.log('editor - hiding textarea');
14371 //            Roo.log('out')
14372 //            Roo.log(this.pushValue()); 
14373             this.pushValue();
14374             
14375             this.inputEl().addClass('hide');
14376             this.inputEl().dom.setAttribute('tabIndex', -1);
14377             //this.deferFocus();
14378         }
14379          
14380         if(this.resizable){
14381             this.setSize(this.wrap.getSize());
14382         }
14383         
14384         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14385     },
14386  
14387     // private (for BoxComponent)
14388     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14389
14390     // private (for BoxComponent)
14391     getResizeEl : function(){
14392         return this.wrap;
14393     },
14394
14395     // private (for BoxComponent)
14396     getPositionEl : function(){
14397         return this.wrap;
14398     },
14399
14400     // private
14401     initEvents : function(){
14402         this.originalValue = this.getValue();
14403     },
14404
14405 //    /**
14406 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14407 //     * @method
14408 //     */
14409 //    markInvalid : Roo.emptyFn,
14410 //    /**
14411 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14412 //     * @method
14413 //     */
14414 //    clearInvalid : Roo.emptyFn,
14415
14416     setValue : function(v){
14417         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14418         this.editorcore.pushValue();
14419     },
14420
14421      
14422     // private
14423     deferFocus : function(){
14424         this.focus.defer(10, this);
14425     },
14426
14427     // doc'ed in Field
14428     focus : function(){
14429         this.editorcore.focus();
14430         
14431     },
14432       
14433
14434     // private
14435     onDestroy : function(){
14436         
14437         
14438         
14439         if(this.rendered){
14440             
14441             for (var i =0; i < this.toolbars.length;i++) {
14442                 // fixme - ask toolbars for heights?
14443                 this.toolbars[i].onDestroy();
14444             }
14445             
14446             this.wrap.dom.innerHTML = '';
14447             this.wrap.remove();
14448         }
14449     },
14450
14451     // private
14452     onFirstFocus : function(){
14453         //Roo.log("onFirstFocus");
14454         this.editorcore.onFirstFocus();
14455          for (var i =0; i < this.toolbars.length;i++) {
14456             this.toolbars[i].onFirstFocus();
14457         }
14458         
14459     },
14460     
14461     // private
14462     syncValue : function()
14463     {   
14464         this.editorcore.syncValue();
14465     },
14466     
14467     pushValue : function()
14468     {   
14469         this.editorcore.pushValue();
14470     }
14471      
14472     
14473     // hide stuff that is not compatible
14474     /**
14475      * @event blur
14476      * @hide
14477      */
14478     /**
14479      * @event change
14480      * @hide
14481      */
14482     /**
14483      * @event focus
14484      * @hide
14485      */
14486     /**
14487      * @event specialkey
14488      * @hide
14489      */
14490     /**
14491      * @cfg {String} fieldClass @hide
14492      */
14493     /**
14494      * @cfg {String} focusClass @hide
14495      */
14496     /**
14497      * @cfg {String} autoCreate @hide
14498      */
14499     /**
14500      * @cfg {String} inputType @hide
14501      */
14502     /**
14503      * @cfg {String} invalidClass @hide
14504      */
14505     /**
14506      * @cfg {String} invalidText @hide
14507      */
14508     /**
14509      * @cfg {String} msgFx @hide
14510      */
14511     /**
14512      * @cfg {String} validateOnBlur @hide
14513      */
14514 });
14515  
14516     
14517    
14518    
14519    
14520       
14521
14522 /**
14523  * @class Roo.bootstrap.HtmlEditorToolbar1
14524  * Basic Toolbar
14525  * 
14526  * Usage:
14527  *
14528  new Roo.bootstrap.HtmlEditor({
14529     ....
14530     toolbars : [
14531         new Roo.bootstrap.HtmlEditorToolbar1({
14532             disable : { fonts: 1 , format: 1, ..., ... , ...],
14533             btns : [ .... ]
14534         })
14535     }
14536      
14537  * 
14538  * @cfg {Object} disable List of elements to disable..
14539  * @cfg {Array} btns List of additional buttons.
14540  * 
14541  * 
14542  * NEEDS Extra CSS? 
14543  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14544  */
14545  
14546 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14547 {
14548     
14549     Roo.apply(this, config);
14550     
14551     // default disabled, based on 'good practice'..
14552     this.disable = this.disable || {};
14553     Roo.applyIf(this.disable, {
14554         fontSize : true,
14555         colors : true,
14556         specialElements : true
14557     });
14558     Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14559     
14560     this.editor = config.editor;
14561     this.editorcore = config.editor.editorcore;
14562     
14563     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14564     
14565     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14566     // dont call parent... till later.
14567 }
14568 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar,  {
14569     
14570     
14571     bar : true,
14572     
14573     editor : false,
14574     editorcore : false,
14575     
14576     
14577     formats : [
14578         "p" ,  
14579         "h1","h2","h3","h4","h5","h6", 
14580         "pre", "code", 
14581         "abbr", "acronym", "address", "cite", "samp", "var",
14582         'div','span'
14583     ],
14584     
14585     onRender : function(ct, position)
14586     {
14587        // Roo.log("Call onRender: " + this.xtype);
14588         
14589        Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14590        Roo.log(this.el);
14591        this.el.dom.style.marginBottom = '0';
14592        var _this = this;
14593        var editorcore = this.editorcore;
14594        var editor= this.editor;
14595        
14596        var children = [];
14597        var btn = function(id,cmd , toggle, handler){
14598        
14599             var  event = toggle ? 'toggle' : 'click';
14600        
14601             var a = {
14602                 size : 'sm',
14603                 xtype: 'Button',
14604                 xns: Roo.bootstrap,
14605                 glyphicon : id,
14606                 cmd : id || cmd,
14607                 enableToggle:toggle !== false,
14608                 //html : 'submit'
14609                 pressed : toggle ? false : null,
14610                 listeners : {}
14611             }
14612             a.listeners[toggle ? 'toggle' : 'click'] = function() {
14613                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
14614             }
14615             children.push(a);
14616             return a;
14617        }
14618         
14619         var style = {
14620                 xtype: 'Button',
14621                 size : 'sm',
14622                 xns: Roo.bootstrap,
14623                 glyphicon : 'font',
14624                 //html : 'submit'
14625                 menu : {
14626                     xtype: 'Menu',
14627                     xns: Roo.bootstrap,
14628                     items:  []
14629                 }
14630         };
14631         Roo.each(this.formats, function(f) {
14632             style.menu.items.push({
14633                 xtype :'MenuItem',
14634                 xns: Roo.bootstrap,
14635                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14636                 tagname : f,
14637                 listeners : {
14638                     click : function()
14639                     {
14640                         editorcore.insertTag(this.tagname);
14641                         editor.focus();
14642                     }
14643                 }
14644                 
14645             });
14646         });
14647          children.push(style);   
14648             
14649             
14650         btn('bold',false,true);
14651         btn('italic',false,true);
14652         btn('align-left', 'justifyleft',true);
14653         btn('align-center', 'justifycenter',true);
14654         btn('align-right' , 'justifyright',true);
14655         btn('link', false, false, function(btn) {
14656             //Roo.log("create link?");
14657             var url = prompt(this.createLinkText, this.defaultLinkValue);
14658             if(url && url != 'http:/'+'/'){
14659                 this.editorcore.relayCmd('createlink', url);
14660             }
14661         }),
14662         btn('list','insertunorderedlist',true);
14663         btn('pencil', false,true, function(btn){
14664                 Roo.log(this);
14665                 
14666                 this.toggleSourceEdit(btn.pressed);
14667         });
14668         /*
14669         var cog = {
14670                 xtype: 'Button',
14671                 size : 'sm',
14672                 xns: Roo.bootstrap,
14673                 glyphicon : 'cog',
14674                 //html : 'submit'
14675                 menu : {
14676                     xtype: 'Menu',
14677                     xns: Roo.bootstrap,
14678                     items:  []
14679                 }
14680         };
14681         
14682         cog.menu.items.push({
14683             xtype :'MenuItem',
14684             xns: Roo.bootstrap,
14685             html : Clean styles,
14686             tagname : f,
14687             listeners : {
14688                 click : function()
14689                 {
14690                     editorcore.insertTag(this.tagname);
14691                     editor.focus();
14692                 }
14693             }
14694             
14695         });
14696        */
14697         
14698          
14699        this.xtype = 'Navbar';
14700         
14701         for(var i=0;i< children.length;i++) {
14702             
14703             this.buttons.add(this.addxtypeChild(children[i]));
14704             
14705         }
14706         
14707         editor.on('editorevent', this.updateToolbar, this);
14708     },
14709     onBtnClick : function(id)
14710     {
14711        this.editorcore.relayCmd(id);
14712        this.editorcore.focus();
14713     },
14714     
14715     /**
14716      * Protected method that will not generally be called directly. It triggers
14717      * a toolbar update by reading the markup state of the current selection in the editor.
14718      */
14719     updateToolbar: function(){
14720
14721         if(!this.editorcore.activated){
14722             this.editor.onFirstFocus(); // is this neeed?
14723             return;
14724         }
14725
14726         var btns = this.buttons; 
14727         var doc = this.editorcore.doc;
14728         btns.get('bold').setActive(doc.queryCommandState('bold'));
14729         btns.get('italic').setActive(doc.queryCommandState('italic'));
14730         //btns.get('underline').setActive(doc.queryCommandState('underline'));
14731         
14732         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14733         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14734         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14735         
14736         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14737         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14738          /*
14739         
14740         var ans = this.editorcore.getAllAncestors();
14741         if (this.formatCombo) {
14742             
14743             
14744             var store = this.formatCombo.store;
14745             this.formatCombo.setValue("");
14746             for (var i =0; i < ans.length;i++) {
14747                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14748                     // select it..
14749                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14750                     break;
14751                 }
14752             }
14753         }
14754         
14755         
14756         
14757         // hides menus... - so this cant be on a menu...
14758         Roo.bootstrap.MenuMgr.hideAll();
14759         */
14760         Roo.bootstrap.MenuMgr.hideAll();
14761         //this.editorsyncValue();
14762     },
14763     onFirstFocus: function() {
14764         this.buttons.each(function(item){
14765            item.enable();
14766         });
14767     },
14768     toggleSourceEdit : function(sourceEditMode){
14769         
14770           
14771         if(sourceEditMode){
14772             Roo.log("disabling buttons");
14773            this.buttons.each( function(item){
14774                 if(item.cmd != 'pencil'){
14775                     item.disable();
14776                 }
14777             });
14778           
14779         }else{
14780             Roo.log("enabling buttons");
14781             if(this.editorcore.initialized){
14782                 this.buttons.each( function(item){
14783                     item.enable();
14784                 });
14785             }
14786             
14787         }
14788         Roo.log("calling toggole on editor");
14789         // tell the editor that it's been pressed..
14790         this.editor.toggleSourceEdit(sourceEditMode);
14791        
14792     }
14793 });
14794
14795
14796
14797
14798
14799 /**
14800  * @class Roo.bootstrap.Table.AbstractSelectionModel
14801  * @extends Roo.util.Observable
14802  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14803  * implemented by descendant classes.  This class should not be directly instantiated.
14804  * @constructor
14805  */
14806 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14807     this.locked = false;
14808     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14809 };
14810
14811
14812 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14813     /** @ignore Called by the grid automatically. Do not call directly. */
14814     init : function(grid){
14815         this.grid = grid;
14816         this.initEvents();
14817     },
14818
14819     /**
14820      * Locks the selections.
14821      */
14822     lock : function(){
14823         this.locked = true;
14824     },
14825
14826     /**
14827      * Unlocks the selections.
14828      */
14829     unlock : function(){
14830         this.locked = false;
14831     },
14832
14833     /**
14834      * Returns true if the selections are locked.
14835      * @return {Boolean}
14836      */
14837     isLocked : function(){
14838         return this.locked;
14839     }
14840 });
14841 /**
14842  * @class Roo.bootstrap.Table.ColumnModel
14843  * @extends Roo.util.Observable
14844  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14845  * the columns in the table.
14846  
14847  * @constructor
14848  * @param {Object} config An Array of column config objects. See this class's
14849  * config objects for details.
14850 */
14851 Roo.bootstrap.Table.ColumnModel = function(config){
14852         /**
14853      * The config passed into the constructor
14854      */
14855     this.config = config;
14856     this.lookup = {};
14857
14858     // if no id, create one
14859     // if the column does not have a dataIndex mapping,
14860     // map it to the order it is in the config
14861     for(var i = 0, len = config.length; i < len; i++){
14862         var c = config[i];
14863         if(typeof c.dataIndex == "undefined"){
14864             c.dataIndex = i;
14865         }
14866         if(typeof c.renderer == "string"){
14867             c.renderer = Roo.util.Format[c.renderer];
14868         }
14869         if(typeof c.id == "undefined"){
14870             c.id = Roo.id();
14871         }
14872 //        if(c.editor && c.editor.xtype){
14873 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14874 //        }
14875 //        if(c.editor && c.editor.isFormField){
14876 //            c.editor = new Roo.grid.GridEditor(c.editor);
14877 //        }
14878
14879         this.lookup[c.id] = c;
14880     }
14881
14882     /**
14883      * The width of columns which have no width specified (defaults to 100)
14884      * @type Number
14885      */
14886     this.defaultWidth = 100;
14887
14888     /**
14889      * Default sortable of columns which have no sortable specified (defaults to false)
14890      * @type Boolean
14891      */
14892     this.defaultSortable = false;
14893
14894     this.addEvents({
14895         /**
14896              * @event widthchange
14897              * Fires when the width of a column changes.
14898              * @param {ColumnModel} this
14899              * @param {Number} columnIndex The column index
14900              * @param {Number} newWidth The new width
14901              */
14902             "widthchange": true,
14903         /**
14904              * @event headerchange
14905              * Fires when the text of a header changes.
14906              * @param {ColumnModel} this
14907              * @param {Number} columnIndex The column index
14908              * @param {Number} newText The new header text
14909              */
14910             "headerchange": true,
14911         /**
14912              * @event hiddenchange
14913              * Fires when a column is hidden or "unhidden".
14914              * @param {ColumnModel} this
14915              * @param {Number} columnIndex The column index
14916              * @param {Boolean} hidden true if hidden, false otherwise
14917              */
14918             "hiddenchange": true,
14919             /**
14920          * @event columnmoved
14921          * Fires when a column is moved.
14922          * @param {ColumnModel} this
14923          * @param {Number} oldIndex
14924          * @param {Number} newIndex
14925          */
14926         "columnmoved" : true,
14927         /**
14928          * @event columlockchange
14929          * Fires when a column's locked state is changed
14930          * @param {ColumnModel} this
14931          * @param {Number} colIndex
14932          * @param {Boolean} locked true if locked
14933          */
14934         "columnlockchange" : true
14935     });
14936     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14937 };
14938 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14939     /**
14940      * @cfg {String} header The header text to display in the Grid view.
14941      */
14942     /**
14943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14945      * specified, the column's index is used as an index into the Record's data Array.
14946      */
14947     /**
14948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14950      */
14951     /**
14952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14953      * Defaults to the value of the {@link #defaultSortable} property.
14954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14955      */
14956     /**
14957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14958      */
14959     /**
14960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14961      */
14962     /**
14963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14964      */
14965     /**
14966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14967      */
14968     /**
14969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14971      * default renderer uses the raw data value.
14972      */
14973     /**
14974      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14975      */
14976
14977     /**
14978      * Returns the id of the column at the specified index.
14979      * @param {Number} index The column index
14980      * @return {String} the id
14981      */
14982     getColumnId : function(index){
14983         return this.config[index].id;
14984     },
14985
14986     /**
14987      * Returns the column for a specified id.
14988      * @param {String} id The column id
14989      * @return {Object} the column
14990      */
14991     getColumnById : function(id){
14992         return this.lookup[id];
14993     },
14994
14995     
14996     /**
14997      * Returns the column for a specified dataIndex.
14998      * @param {String} dataIndex The column dataIndex
14999      * @return {Object|Boolean} the column or false if not found
15000      */
15001     getColumnByDataIndex: function(dataIndex){
15002         var index = this.findColumnIndex(dataIndex);
15003         return index > -1 ? this.config[index] : false;
15004     },
15005     
15006     /**
15007      * Returns the index for a specified column id.
15008      * @param {String} id The column id
15009      * @return {Number} the index, or -1 if not found
15010      */
15011     getIndexById : function(id){
15012         for(var i = 0, len = this.config.length; i < len; i++){
15013             if(this.config[i].id == id){
15014                 return i;
15015             }
15016         }
15017         return -1;
15018     },
15019     
15020     /**
15021      * Returns the index for a specified column dataIndex.
15022      * @param {String} dataIndex The column dataIndex
15023      * @return {Number} the index, or -1 if not found
15024      */
15025     
15026     findColumnIndex : function(dataIndex){
15027         for(var i = 0, len = this.config.length; i < len; i++){
15028             if(this.config[i].dataIndex == dataIndex){
15029                 return i;
15030             }
15031         }
15032         return -1;
15033     },
15034     
15035     
15036     moveColumn : function(oldIndex, newIndex){
15037         var c = this.config[oldIndex];
15038         this.config.splice(oldIndex, 1);
15039         this.config.splice(newIndex, 0, c);
15040         this.dataMap = null;
15041         this.fireEvent("columnmoved", this, oldIndex, newIndex);
15042     },
15043
15044     isLocked : function(colIndex){
15045         return this.config[colIndex].locked === true;
15046     },
15047
15048     setLocked : function(colIndex, value, suppressEvent){
15049         if(this.isLocked(colIndex) == value){
15050             return;
15051         }
15052         this.config[colIndex].locked = value;
15053         if(!suppressEvent){
15054             this.fireEvent("columnlockchange", this, colIndex, value);
15055         }
15056     },
15057
15058     getTotalLockedWidth : function(){
15059         var totalWidth = 0;
15060         for(var i = 0; i < this.config.length; i++){
15061             if(this.isLocked(i) && !this.isHidden(i)){
15062                 this.totalWidth += this.getColumnWidth(i);
15063             }
15064         }
15065         return totalWidth;
15066     },
15067
15068     getLockedCount : function(){
15069         for(var i = 0, len = this.config.length; i < len; i++){
15070             if(!this.isLocked(i)){
15071                 return i;
15072             }
15073         }
15074     },
15075
15076     /**
15077      * Returns the number of columns.
15078      * @return {Number}
15079      */
15080     getColumnCount : function(visibleOnly){
15081         if(visibleOnly === true){
15082             var c = 0;
15083             for(var i = 0, len = this.config.length; i < len; i++){
15084                 if(!this.isHidden(i)){
15085                     c++;
15086                 }
15087             }
15088             return c;
15089         }
15090         return this.config.length;
15091     },
15092
15093     /**
15094      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15095      * @param {Function} fn
15096      * @param {Object} scope (optional)
15097      * @return {Array} result
15098      */
15099     getColumnsBy : function(fn, scope){
15100         var r = [];
15101         for(var i = 0, len = this.config.length; i < len; i++){
15102             var c = this.config[i];
15103             if(fn.call(scope||this, c, i) === true){
15104                 r[r.length] = c;
15105             }
15106         }
15107         return r;
15108     },
15109
15110     /**
15111      * Returns true if the specified column is sortable.
15112      * @param {Number} col The column index
15113      * @return {Boolean}
15114      */
15115     isSortable : function(col){
15116         if(typeof this.config[col].sortable == "undefined"){
15117             return this.defaultSortable;
15118         }
15119         return this.config[col].sortable;
15120     },
15121
15122     /**
15123      * Returns the rendering (formatting) function defined for the column.
15124      * @param {Number} col The column index.
15125      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15126      */
15127     getRenderer : function(col){
15128         if(!this.config[col].renderer){
15129             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15130         }
15131         return this.config[col].renderer;
15132     },
15133
15134     /**
15135      * Sets the rendering (formatting) function for a column.
15136      * @param {Number} col The column index
15137      * @param {Function} fn The function to use to process the cell's raw data
15138      * to return HTML markup for the grid view. The render function is called with
15139      * the following parameters:<ul>
15140      * <li>Data value.</li>
15141      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15142      * <li>css A CSS style string to apply to the table cell.</li>
15143      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15144      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15145      * <li>Row index</li>
15146      * <li>Column index</li>
15147      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15148      */
15149     setRenderer : function(col, fn){
15150         this.config[col].renderer = fn;
15151     },
15152
15153     /**
15154      * Returns the width for the specified column.
15155      * @param {Number} col The column index
15156      * @return {Number}
15157      */
15158     getColumnWidth : function(col){
15159         return this.config[col].width * 1 || this.defaultWidth;
15160     },
15161
15162     /**
15163      * Sets the width for a column.
15164      * @param {Number} col The column index
15165      * @param {Number} width The new width
15166      */
15167     setColumnWidth : function(col, width, suppressEvent){
15168         this.config[col].width = width;
15169         this.totalWidth = null;
15170         if(!suppressEvent){
15171              this.fireEvent("widthchange", this, col, width);
15172         }
15173     },
15174
15175     /**
15176      * Returns the total width of all columns.
15177      * @param {Boolean} includeHidden True to include hidden column widths
15178      * @return {Number}
15179      */
15180     getTotalWidth : function(includeHidden){
15181         if(!this.totalWidth){
15182             this.totalWidth = 0;
15183             for(var i = 0, len = this.config.length; i < len; i++){
15184                 if(includeHidden || !this.isHidden(i)){
15185                     this.totalWidth += this.getColumnWidth(i);
15186                 }
15187             }
15188         }
15189         return this.totalWidth;
15190     },
15191
15192     /**
15193      * Returns the header for the specified column.
15194      * @param {Number} col The column index
15195      * @return {String}
15196      */
15197     getColumnHeader : function(col){
15198         return this.config[col].header;
15199     },
15200
15201     /**
15202      * Sets the header for a column.
15203      * @param {Number} col The column index
15204      * @param {String} header The new header
15205      */
15206     setColumnHeader : function(col, header){
15207         this.config[col].header = header;
15208         this.fireEvent("headerchange", this, col, header);
15209     },
15210
15211     /**
15212      * Returns the tooltip for the specified column.
15213      * @param {Number} col The column index
15214      * @return {String}
15215      */
15216     getColumnTooltip : function(col){
15217             return this.config[col].tooltip;
15218     },
15219     /**
15220      * Sets the tooltip for a column.
15221      * @param {Number} col The column index
15222      * @param {String} tooltip The new tooltip
15223      */
15224     setColumnTooltip : function(col, tooltip){
15225             this.config[col].tooltip = tooltip;
15226     },
15227
15228     /**
15229      * Returns the dataIndex for the specified column.
15230      * @param {Number} col The column index
15231      * @return {Number}
15232      */
15233     getDataIndex : function(col){
15234         return this.config[col].dataIndex;
15235     },
15236
15237     /**
15238      * Sets the dataIndex for a column.
15239      * @param {Number} col The column index
15240      * @param {Number} dataIndex The new dataIndex
15241      */
15242     setDataIndex : function(col, dataIndex){
15243         this.config[col].dataIndex = dataIndex;
15244     },
15245
15246     
15247     
15248     /**
15249      * Returns true if the cell is editable.
15250      * @param {Number} colIndex The column index
15251      * @param {Number} rowIndex The row index
15252      * @return {Boolean}
15253      */
15254     isCellEditable : function(colIndex, rowIndex){
15255         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15256     },
15257
15258     /**
15259      * Returns the editor defined for the cell/column.
15260      * return false or null to disable editing.
15261      * @param {Number} colIndex The column index
15262      * @param {Number} rowIndex The row index
15263      * @return {Object}
15264      */
15265     getCellEditor : function(colIndex, rowIndex){
15266         return this.config[colIndex].editor;
15267     },
15268
15269     /**
15270      * Sets if a column is editable.
15271      * @param {Number} col The column index
15272      * @param {Boolean} editable True if the column is editable
15273      */
15274     setEditable : function(col, editable){
15275         this.config[col].editable = editable;
15276     },
15277
15278
15279     /**
15280      * Returns true if the column is hidden.
15281      * @param {Number} colIndex The column index
15282      * @return {Boolean}
15283      */
15284     isHidden : function(colIndex){
15285         return this.config[colIndex].hidden;
15286     },
15287
15288
15289     /**
15290      * Returns true if the column width cannot be changed
15291      */
15292     isFixed : function(colIndex){
15293         return this.config[colIndex].fixed;
15294     },
15295
15296     /**
15297      * Returns true if the column can be resized
15298      * @return {Boolean}
15299      */
15300     isResizable : function(colIndex){
15301         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15302     },
15303     /**
15304      * Sets if a column is hidden.
15305      * @param {Number} colIndex The column index
15306      * @param {Boolean} hidden True if the column is hidden
15307      */
15308     setHidden : function(colIndex, hidden){
15309         this.config[colIndex].hidden = hidden;
15310         this.totalWidth = null;
15311         this.fireEvent("hiddenchange", this, colIndex, hidden);
15312     },
15313
15314     /**
15315      * Sets the editor for a column.
15316      * @param {Number} col The column index
15317      * @param {Object} editor The editor object
15318      */
15319     setEditor : function(col, editor){
15320         this.config[col].editor = editor;
15321     }
15322 });
15323
15324 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15325         if(typeof value == "string" && value.length < 1){
15326             return "&#160;";
15327         }
15328         return value;
15329 };
15330
15331 // Alias for backwards compatibility
15332 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15333
15334 /**
15335  * @extends Roo.bootstrap.Table.AbstractSelectionModel
15336  * @class Roo.bootstrap.Table.RowSelectionModel
15337  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15338  * It supports multiple selections and keyboard selection/navigation. 
15339  * @constructor
15340  * @param {Object} config
15341  */
15342
15343 Roo.bootstrap.Table.RowSelectionModel = function(config){
15344     Roo.apply(this, config);
15345     this.selections = new Roo.util.MixedCollection(false, function(o){
15346         return o.id;
15347     });
15348
15349     this.last = false;
15350     this.lastActive = false;
15351
15352     this.addEvents({
15353         /**
15354              * @event selectionchange
15355              * Fires when the selection changes
15356              * @param {SelectionModel} this
15357              */
15358             "selectionchange" : true,
15359         /**
15360              * @event afterselectionchange
15361              * Fires after the selection changes (eg. by key press or clicking)
15362              * @param {SelectionModel} this
15363              */
15364             "afterselectionchange" : true,
15365         /**
15366              * @event beforerowselect
15367              * Fires when a row is selected being selected, return false to cancel.
15368              * @param {SelectionModel} this
15369              * @param {Number} rowIndex The selected index
15370              * @param {Boolean} keepExisting False if other selections will be cleared
15371              */
15372             "beforerowselect" : true,
15373         /**
15374              * @event rowselect
15375              * Fires when a row is selected.
15376              * @param {SelectionModel} this
15377              * @param {Number} rowIndex The selected index
15378              * @param {Roo.data.Record} r The record
15379              */
15380             "rowselect" : true,
15381         /**
15382              * @event rowdeselect
15383              * Fires when a row is deselected.
15384              * @param {SelectionModel} this
15385              * @param {Number} rowIndex The selected index
15386              */
15387         "rowdeselect" : true
15388     });
15389     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15390     this.locked = false;
15391 };
15392
15393 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
15394     /**
15395      * @cfg {Boolean} singleSelect
15396      * True to allow selection of only one row at a time (defaults to false)
15397      */
15398     singleSelect : false,
15399
15400     // private
15401     initEvents : function(){
15402
15403         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15404             this.grid.on("mousedown", this.handleMouseDown, this);
15405         }else{ // allow click to work like normal
15406             this.grid.on("rowclick", this.handleDragableRowClick, this);
15407         }
15408
15409         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15410             "up" : function(e){
15411                 if(!e.shiftKey){
15412                     this.selectPrevious(e.shiftKey);
15413                 }else if(this.last !== false && this.lastActive !== false){
15414                     var last = this.last;
15415                     this.selectRange(this.last,  this.lastActive-1);
15416                     this.grid.getView().focusRow(this.lastActive);
15417                     if(last !== false){
15418                         this.last = last;
15419                     }
15420                 }else{
15421                     this.selectFirstRow();
15422                 }
15423                 this.fireEvent("afterselectionchange", this);
15424             },
15425             "down" : function(e){
15426                 if(!e.shiftKey){
15427                     this.selectNext(e.shiftKey);
15428                 }else if(this.last !== false && this.lastActive !== false){
15429                     var last = this.last;
15430                     this.selectRange(this.last,  this.lastActive+1);
15431                     this.grid.getView().focusRow(this.lastActive);
15432                     if(last !== false){
15433                         this.last = last;
15434                     }
15435                 }else{
15436                     this.selectFirstRow();
15437                 }
15438                 this.fireEvent("afterselectionchange", this);
15439             },
15440             scope: this
15441         });
15442
15443         var view = this.grid.view;
15444         view.on("refresh", this.onRefresh, this);
15445         view.on("rowupdated", this.onRowUpdated, this);
15446         view.on("rowremoved", this.onRemove, this);
15447     },
15448
15449     // private
15450     onRefresh : function(){
15451         var ds = this.grid.dataSource, i, v = this.grid.view;
15452         var s = this.selections;
15453         s.each(function(r){
15454             if((i = ds.indexOfId(r.id)) != -1){
15455                 v.onRowSelect(i);
15456             }else{
15457                 s.remove(r);
15458             }
15459         });
15460     },
15461
15462     // private
15463     onRemove : function(v, index, r){
15464         this.selections.remove(r);
15465     },
15466
15467     // private
15468     onRowUpdated : function(v, index, r){
15469         if(this.isSelected(r)){
15470             v.onRowSelect(index);
15471         }
15472     },
15473
15474     /**
15475      * Select records.
15476      * @param {Array} records The records to select
15477      * @param {Boolean} keepExisting (optional) True to keep existing selections
15478      */
15479     selectRecords : function(records, keepExisting){
15480         if(!keepExisting){
15481             this.clearSelections();
15482         }
15483         var ds = this.grid.dataSource;
15484         for(var i = 0, len = records.length; i < len; i++){
15485             this.selectRow(ds.indexOf(records[i]), true);
15486         }
15487     },
15488
15489     /**
15490      * Gets the number of selected rows.
15491      * @return {Number}
15492      */
15493     getCount : function(){
15494         return this.selections.length;
15495     },
15496
15497     /**
15498      * Selects the first row in the grid.
15499      */
15500     selectFirstRow : function(){
15501         this.selectRow(0);
15502     },
15503
15504     /**
15505      * Select the last row.
15506      * @param {Boolean} keepExisting (optional) True to keep existing selections
15507      */
15508     selectLastRow : function(keepExisting){
15509         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15510     },
15511
15512     /**
15513      * Selects the row immediately following the last selected row.
15514      * @param {Boolean} keepExisting (optional) True to keep existing selections
15515      */
15516     selectNext : function(keepExisting){
15517         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15518             this.selectRow(this.last+1, keepExisting);
15519             this.grid.getView().focusRow(this.last);
15520         }
15521     },
15522
15523     /**
15524      * Selects the row that precedes the last selected row.
15525      * @param {Boolean} keepExisting (optional) True to keep existing selections
15526      */
15527     selectPrevious : function(keepExisting){
15528         if(this.last){
15529             this.selectRow(this.last-1, keepExisting);
15530             this.grid.getView().focusRow(this.last);
15531         }
15532     },
15533
15534     /**
15535      * Returns the selected records
15536      * @return {Array} Array of selected records
15537      */
15538     getSelections : function(){
15539         return [].concat(this.selections.items);
15540     },
15541
15542     /**
15543      * Returns the first selected record.
15544      * @return {Record}
15545      */
15546     getSelected : function(){
15547         return this.selections.itemAt(0);
15548     },
15549
15550
15551     /**
15552      * Clears all selections.
15553      */
15554     clearSelections : function(fast){
15555         if(this.locked) return;
15556         if(fast !== true){
15557             var ds = this.grid.dataSource;
15558             var s = this.selections;
15559             s.each(function(r){
15560                 this.deselectRow(ds.indexOfId(r.id));
15561             }, this);
15562             s.clear();
15563         }else{
15564             this.selections.clear();
15565         }
15566         this.last = false;
15567     },
15568
15569
15570     /**
15571      * Selects all rows.
15572      */
15573     selectAll : function(){
15574         if(this.locked) return;
15575         this.selections.clear();
15576         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15577             this.selectRow(i, true);
15578         }
15579     },
15580
15581     /**
15582      * Returns True if there is a selection.
15583      * @return {Boolean}
15584      */
15585     hasSelection : function(){
15586         return this.selections.length > 0;
15587     },
15588
15589     /**
15590      * Returns True if the specified row is selected.
15591      * @param {Number/Record} record The record or index of the record to check
15592      * @return {Boolean}
15593      */
15594     isSelected : function(index){
15595         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15596         return (r && this.selections.key(r.id) ? true : false);
15597     },
15598
15599     /**
15600      * Returns True if the specified record id is selected.
15601      * @param {String} id The id of record to check
15602      * @return {Boolean}
15603      */
15604     isIdSelected : function(id){
15605         return (this.selections.key(id) ? true : false);
15606     },
15607
15608     // private
15609     handleMouseDown : function(e, t){
15610         var view = this.grid.getView(), rowIndex;
15611         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15612             return;
15613         };
15614         if(e.shiftKey && this.last !== false){
15615             var last = this.last;
15616             this.selectRange(last, rowIndex, e.ctrlKey);
15617             this.last = last; // reset the last
15618             view.focusRow(rowIndex);
15619         }else{
15620             var isSelected = this.isSelected(rowIndex);
15621             if(e.button !== 0 && isSelected){
15622                 view.focusRow(rowIndex);
15623             }else if(e.ctrlKey && isSelected){
15624                 this.deselectRow(rowIndex);
15625             }else if(!isSelected){
15626                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15627                 view.focusRow(rowIndex);
15628             }
15629         }
15630         this.fireEvent("afterselectionchange", this);
15631     },
15632     // private
15633     handleDragableRowClick :  function(grid, rowIndex, e) 
15634     {
15635         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15636             this.selectRow(rowIndex, false);
15637             grid.view.focusRow(rowIndex);
15638              this.fireEvent("afterselectionchange", this);
15639         }
15640     },
15641     
15642     /**
15643      * Selects multiple rows.
15644      * @param {Array} rows Array of the indexes of the row to select
15645      * @param {Boolean} keepExisting (optional) True to keep existing selections
15646      */
15647     selectRows : function(rows, keepExisting){
15648         if(!keepExisting){
15649             this.clearSelections();
15650         }
15651         for(var i = 0, len = rows.length; i < len; i++){
15652             this.selectRow(rows[i], true);
15653         }
15654     },
15655
15656     /**
15657      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15658      * @param {Number} startRow The index of the first row in the range
15659      * @param {Number} endRow The index of the last row in the range
15660      * @param {Boolean} keepExisting (optional) True to retain existing selections
15661      */
15662     selectRange : function(startRow, endRow, keepExisting){
15663         if(this.locked) return;
15664         if(!keepExisting){
15665             this.clearSelections();
15666         }
15667         if(startRow <= endRow){
15668             for(var i = startRow; i <= endRow; i++){
15669                 this.selectRow(i, true);
15670             }
15671         }else{
15672             for(var i = startRow; i >= endRow; i--){
15673                 this.selectRow(i, true);
15674             }
15675         }
15676     },
15677
15678     /**
15679      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15680      * @param {Number} startRow The index of the first row in the range
15681      * @param {Number} endRow The index of the last row in the range
15682      */
15683     deselectRange : function(startRow, endRow, preventViewNotify){
15684         if(this.locked) return;
15685         for(var i = startRow; i <= endRow; i++){
15686             this.deselectRow(i, preventViewNotify);
15687         }
15688     },
15689
15690     /**
15691      * Selects a row.
15692      * @param {Number} row The index of the row to select
15693      * @param {Boolean} keepExisting (optional) True to keep existing selections
15694      */
15695     selectRow : function(index, keepExisting, preventViewNotify){
15696         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15697         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15698             if(!keepExisting || this.singleSelect){
15699                 this.clearSelections();
15700             }
15701             var r = this.grid.dataSource.getAt(index);
15702             this.selections.add(r);
15703             this.last = this.lastActive = index;
15704             if(!preventViewNotify){
15705                 this.grid.getView().onRowSelect(index);
15706             }
15707             this.fireEvent("rowselect", this, index, r);
15708             this.fireEvent("selectionchange", this);
15709         }
15710     },
15711
15712     /**
15713      * Deselects a row.
15714      * @param {Number} row The index of the row to deselect
15715      */
15716     deselectRow : function(index, preventViewNotify){
15717         if(this.locked) return;
15718         if(this.last == index){
15719             this.last = false;
15720         }
15721         if(this.lastActive == index){
15722             this.lastActive = false;
15723         }
15724         var r = this.grid.dataSource.getAt(index);
15725         this.selections.remove(r);
15726         if(!preventViewNotify){
15727             this.grid.getView().onRowDeselect(index);
15728         }
15729         this.fireEvent("rowdeselect", this, index);
15730         this.fireEvent("selectionchange", this);
15731     },
15732
15733     // private
15734     restoreLast : function(){
15735         if(this._last){
15736             this.last = this._last;
15737         }
15738     },
15739
15740     // private
15741     acceptsNav : function(row, col, cm){
15742         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15743     },
15744
15745     // private
15746     onEditorKey : function(field, e){
15747         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15748         if(k == e.TAB){
15749             e.stopEvent();
15750             ed.completeEdit();
15751             if(e.shiftKey){
15752                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15753             }else{
15754                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15755             }
15756         }else if(k == e.ENTER && !e.ctrlKey){
15757             e.stopEvent();
15758             ed.completeEdit();
15759             if(e.shiftKey){
15760                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15761             }else{
15762                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15763             }
15764         }else if(k == e.ESC){
15765             ed.cancelEdit();
15766         }
15767         if(newCell){
15768             g.startEditing(newCell[0], newCell[1]);
15769         }
15770     }
15771 });/*
15772  * - LGPL
15773  *
15774  * element
15775  * 
15776  */
15777
15778 /**
15779  * @class Roo.bootstrap.MessageBar
15780  * @extends Roo.bootstrap.Component
15781  * Bootstrap MessageBar class
15782  * @cfg {String} html contents of the MessageBar
15783  * @cfg {String} weight (info | success | warning | danger) default info
15784  * @cfg {String} beforeClass insert the bar before the given class
15785  * @cfg {Boolean} closable (true | false) default false
15786  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15787  * 
15788  * @constructor
15789  * Create a new Element
15790  * @param {Object} config The config object
15791  */
15792
15793 Roo.bootstrap.MessageBar = function(config){
15794     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15795 };
15796
15797 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
15798     
15799     html: '',
15800     weight: 'info',
15801     closable: false,
15802     fixed: false,
15803     beforeClass: 'bootstrap-sticky-wrap',
15804     
15805     getAutoCreate : function(){
15806         
15807         var cfg = {
15808             tag: 'div',
15809             cls: 'alert alert-dismissable alert-' + this.weight,
15810             cn: [
15811                 {
15812                     tag: 'span',
15813                     cls: 'message',
15814                     html: this.html || ''
15815                 }
15816             ]
15817         }
15818         
15819         if(this.fixed){
15820             cfg.cls += ' alert-messages-fixed';
15821         }
15822         
15823         if(this.closable){
15824             cfg.cn.push({
15825                 tag: 'button',
15826                 cls: 'close',
15827                 html: 'x'
15828             });
15829         }
15830         
15831         return cfg;
15832     },
15833     
15834     onRender : function(ct, position)
15835     {
15836         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15837         
15838         if(!this.el){
15839             var cfg = Roo.apply({},  this.getAutoCreate());
15840             cfg.id = Roo.id();
15841             
15842             if (this.cls) {
15843                 cfg.cls += ' ' + this.cls;
15844             }
15845             if (this.style) {
15846                 cfg.style = this.style;
15847             }
15848             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15849             
15850             this.el.setVisibilityMode(Roo.Element.DISPLAY);
15851         }
15852         
15853         this.el.select('>button.close').on('click', this.hide, this);
15854         
15855     },
15856     
15857     show : function()
15858     {
15859         if (!this.rendered) {
15860             this.render();
15861         }
15862         
15863         this.el.show();
15864         
15865         this.fireEvent('show', this);
15866         
15867     },
15868     
15869     hide : function()
15870     {
15871         if (!this.rendered) {
15872             this.render();
15873         }
15874         
15875         this.el.hide();
15876         
15877         this.fireEvent('hide', this);
15878     },
15879     
15880     update : function()
15881     {
15882 //        var e = this.el.dom.firstChild;
15883 //        
15884 //        if(this.closable){
15885 //            e = e.nextSibling;
15886 //        }
15887 //        
15888 //        e.data = this.html || '';
15889
15890         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
15891     }
15892    
15893 });
15894
15895  
15896
15897