buildSDK/dependancy_bootstrap.txt
[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  * page container.
282  * 
283  */ 
284 Roo.bootstrap.Body = function(config){
285     Roo.bootstrap.Body.superclass.constructor.call(this, config);
286     this.el = Roo.get(document.body);
287 };
288
289 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
290       
291         autoCreate : {
292         cls: 'container'
293     },
294     onRender : function(ct, position){
295         
296         
297         //this.el.addClass([this.fieldClass, this.cls]);
298         
299     }
300     
301     
302  
303    
304 });
305
306  /*
307  * - LGPL
308  *
309  * button group
310  * 
311  */
312
313
314 /**
315  * @class Roo.bootstrap.ButtonGroup
316  * @extends Roo.bootstrap.Component
317  * Bootstrap ButtonGroup class
318  * @cfg {String} size lg | sm | xs (default empty normal)
319  * @cfg {String} align vertical | justified  (default none)
320  * @cfg {String} direction up | down (default down)
321  * @cfg {Boolean} toolbar false | true
322  * @cfg {Boolean} btn true | false
323  * 
324  * 
325  * @constructor
326  * Create a new Input
327  * @param {Object} config The config object
328  */
329
330 Roo.bootstrap.ButtonGroup = function(config){
331     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
332 };
333
334 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
335     
336     size: '',
337     align: '',
338     direction: '',
339     toolbar: false,
340     btn: true,
341
342     getAutoCreate : function(){
343         var cfg = {
344             cls: 'btn-group',
345             html : null
346         }
347         
348         cfg.html = this.html || cfg.html;
349         
350         if (this.toolbar) {
351             cfg = {
352                 cls: 'btn-toolbar',
353                 html: null
354             }
355             
356             return cfg;
357         }
358         
359         if (['vertical','justified'].indexOf(this.align)!==-1) {
360             cfg.cls = 'btn-group-' + this.align;
361             
362             if (this.align == 'justified') {
363                 console.log(this.items);
364             }
365         }
366         
367         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
368             cfg.cls += ' btn-group-' + this.size;
369         }
370         
371         if (this.direction == 'up') {
372             cfg.cls += ' dropup' ;
373         }
374         
375         return cfg;
376     }
377    
378 });
379
380  /*
381  * - LGPL
382  *
383  * button
384  * 
385  */
386
387 /**
388  * @class Roo.bootstrap.Button
389  * @extends Roo.bootstrap.Component
390  * Bootstrap Button class
391  * @cfg {String} html The button content
392  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
393  * @cfg {String} size empty | lg | sm | xs
394  * @cfg {String} tag empty | a | input | submit
395  * @cfg {String} href empty or href
396  * @cfg {Boolean} disabled false | true
397  * @cfg {Boolean} isClose false | true
398  * @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
399  * @cfg {String} badge text for badge
400  * @cfg {String} theme default (or empty) | glow
401  * @cfg {Boolean} inverse false | true
402  * @cfg {Boolean} toggle false | true
403  * @cfg {String} ontext text for on toggle state
404  * @cfg {String} offtext text for off toggle state
405  * @cfg {Boolean} defaulton true | false
406  * @cfg {Boolean} preventDefault (true | false) default true
407  * @cfg {Boolean} removeClass true | false remove the standard class..
408  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
409  * 
410  * @constructor
411  * Create a new button
412  * @param {Object} config The config object
413  */
414
415
416 Roo.bootstrap.Button = function(config){
417     Roo.bootstrap.Button.superclass.constructor.call(this, config);
418     this.addEvents({
419         // raw events
420         /**
421          * @event click
422          * When a butotn is pressed
423          * @param {Roo.EventObject} e
424          */
425         "click" : true,
426          /**
427          * @event toggle
428          * After the button has been toggles
429          * @param {Roo.EventObject} e
430          * @param {boolean} pressed (also available as button.pressed)
431          */
432         "toggle" : true
433     });
434 };
435
436 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
437     html: false,
438     active: false,
439     weight: '',
440     size: '',
441     tag: 'button',
442     href: '',
443     disabled: false,
444     isClose: false,
445     glyphicon: '',
446     badge: '',
447     theme: 'default',
448     inverse: false,
449     
450     toggle: false,
451     ontext: 'ON',
452     offtext: 'OFF',
453     defaulton: true,
454     preventDefault: true,
455     removeClass: false,
456     name: false,
457     target: false,
458     
459     
460     pressed : null,
461     
462     
463     getAutoCreate : function(){
464         
465         var cfg = {
466             tag : 'button',
467             cls : 'roo-button',
468             html: ''
469         };
470         
471         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
472             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
473             this.tag = 'button';
474         } else {
475             cfg.tag = this.tag;
476         }
477         cfg.html = this.html || cfg.html;
478         
479         if (this.toggle == true) {
480             cfg={
481                 tag: 'div',
482                 cls: 'slider-frame roo-button',
483                 cn: [
484                     {
485                         tag: 'span',
486                         'data-on-text':'ON',
487                         'data-off-text':'OFF',
488                         cls: 'slider-button',
489                         html: this.offtext
490                     }
491                 ]
492             };
493             
494             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
495                 cfg.cls += ' '+this.weight;
496             }
497             
498             return cfg;
499         }
500         
501         if (this.isClose) {
502             cfg.cls += ' close';
503             
504             cfg["aria-hidden"] = true;
505             
506             cfg.html = "&times;";
507             
508             return cfg;
509         }
510         
511          
512         if (this.theme==='default') {
513             cfg.cls = 'btn roo-button';
514             
515             //if (this.parentType != 'Navbar') {
516             this.weight = this.weight.length ?  this.weight : 'default';
517             //}
518             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
519                 
520                 cfg.cls += ' btn-' + this.weight;
521             }
522         } else if (this.theme==='glow') {
523             
524             cfg.tag = 'a';
525             cfg.cls = 'btn-glow roo-button';
526             
527             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
528                 
529                 cfg.cls += ' ' + this.weight;
530             }
531         }
532    
533         
534         if (this.inverse) {
535             this.cls += ' inverse';
536         }
537         
538         
539         if (this.active) {
540             cfg.cls += ' active';
541         }
542         
543         if (this.disabled) {
544             cfg.disabled = 'disabled';
545         }
546         
547         if (this.items) {
548             Roo.log('changing to ul' );
549             cfg.tag = 'ul';
550             this.glyphicon = 'caret';
551         }
552         
553         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
554          
555         //gsRoo.log(this.parentType);
556         if (this.parentType === 'Navbar' && !this.parent().bar) {
557             Roo.log('changing to li?');
558             
559             cfg.tag = 'li';
560             
561             cfg.cls = '';
562             cfg.cn =  [{
563                 tag : 'a',
564                 cls : 'roo-button',
565                 html : this.html,
566                 href : this.href || '#'
567             }];
568             if (this.menu) {
569                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
570                 cfg.cls += ' dropdown';
571             }   
572             
573             delete cfg.html;
574             
575         }
576         
577        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
578         
579         if (this.glyphicon) {
580             cfg.html = ' ' + cfg.html;
581             
582             cfg.cn = [
583                 {
584                     tag: 'span',
585                     cls: 'glyphicon glyphicon-' + this.glyphicon
586                 }
587             ];
588         }
589         
590         if (this.badge) {
591             cfg.html += ' ';
592             
593             cfg.tag = 'a';
594             
595 //            cfg.cls='btn roo-button';
596             
597             cfg.href=this.href;
598             
599             var value = cfg.html;
600             
601             if(this.glyphicon){
602                 value = {
603                             tag: 'span',
604                             cls: 'glyphicon glyphicon-' + this.glyphicon,
605                             html: this.html
606                         };
607                 
608             }
609             
610             cfg.cn = [
611                 value,
612                 {
613                     tag: 'span',
614                     cls: 'badge',
615                     html: this.badge
616                 }
617             ];
618             
619             cfg.html='';
620         }
621         
622         if (this.menu) {
623             cfg.cls += ' dropdown';
624             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
625         }
626         
627         if (cfg.tag !== 'a' && this.href !== '') {
628             throw "Tag must be a to set href.";
629         } else if (this.href.length > 0) {
630             cfg.href = this.href;
631         }
632         
633         if(this.removeClass){
634             cfg.cls = '';
635         }
636         
637         if(this.target){
638             cfg.target = this.target;
639         }
640         
641         return cfg;
642     },
643     initEvents: function() {
644        // Roo.log('init events?');
645 //        Roo.log(this.el.dom);
646        if (this.el.hasClass('roo-button')) {
647             this.el.on('click', this.onClick, this);
648        } else {
649             this.el.select('.roo-button').on('click', this.onClick, this);
650        }
651        
652        
653         
654     },
655     onClick : function(e)
656     {
657         if (this.disabled) {
658             return;
659         }
660         
661         Roo.log('button on click ');
662         if(this.preventDefault){
663             e.preventDefault();
664         }
665         if (this.pressed === true || this.pressed === false) {
666             this.pressed = !this.pressed;
667             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
668             this.fireEvent('toggle', this, e, this.pressed);
669         }
670         
671         
672         this.fireEvent('click', this, e);
673     },
674     
675     /**
676      * Enables this button
677      */
678     enable : function()
679     {
680         this.disabled = false;
681         this.el.removeClass('disabled');
682     },
683     
684     /**
685      * Disable this button
686      */
687     disable : function()
688     {
689         this.disabled = true;
690         this.el.addClass('disabled');
691     },
692      /**
693      * sets the active state on/off, 
694      * @param {Boolean} state (optional) Force a particular state
695      */
696     setActive : function(v) {
697         
698         this.el[v ? 'addClass' : 'removeClass']('active');
699     },
700      /**
701      * toggles the current active state 
702      */
703     toggleActive : function()
704     {
705        var active = this.el.hasClass('active');
706        this.setActive(!active);
707        
708         
709     }
710     
711     
712     
713 });
714
715  /*
716  * - LGPL
717  *
718  * column
719  * 
720  */
721
722 /**
723  * @class Roo.bootstrap.Column
724  * @extends Roo.bootstrap.Component
725  * Bootstrap Column class
726  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
727  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
728  * @cfg {Number} md colspan out of 12 for computer-sized screens
729  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
730  * @cfg {String} html content of column.
731  * 
732  * @constructor
733  * Create a new Column
734  * @param {Object} config The config object
735  */
736
737 Roo.bootstrap.Column = function(config){
738     Roo.bootstrap.Column.superclass.constructor.call(this, config);
739 };
740
741 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
742     
743     xs: null,
744     sm: null,
745     md: null,
746     lg: null,
747     html: '',
748     offset: 0,
749     
750     getAutoCreate : function(){
751         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
752         
753         cfg = {
754             tag: 'div',
755             cls: 'column'
756         };
757         
758         var settings=this;
759         ['xs','sm','md','lg'].map(function(size){
760             if (settings[size]) {
761                 cfg.cls += ' col-' + size + '-' + settings[size];
762             }
763         });
764         if (this.html.length) {
765             cfg.html = this.html;
766         }
767         
768         return cfg;
769     }
770    
771 });
772
773  
774
775  /*
776  * - LGPL
777  *
778  * page container.
779  * 
780  */
781
782
783 /**
784  * @class Roo.bootstrap.Container
785  * @extends Roo.bootstrap.Component
786  * Bootstrap Container class
787  * @cfg {Boolean} jumbotron is it a jumbotron element
788  * @cfg {String} html content of element
789  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
790  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
791  * @cfg {String} header content of header (for panel)
792  * @cfg {String} footer content of footer (for panel)
793  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
794  *     
795  * @constructor
796  * Create a new Container
797  * @param {Object} config The config object
798  */
799
800 Roo.bootstrap.Container = function(config){
801     Roo.bootstrap.Container.superclass.constructor.call(this, config);
802 };
803
804 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
805     
806     jumbotron : false,
807     well: '',
808     panel : '',
809     header: '',
810     footer : '',
811     sticky: '',
812   
813      
814     getChildContainer : function() {
815         
816         if(!this.el){
817             return false;
818         }
819         
820         if (this.panel.length) {
821             return this.el.select('.panel-body',true).first();
822         }
823         
824         return this.el;
825     },
826     
827     
828     getAutoCreate : function(){
829         
830         var cfg = {
831             html : '',
832             cls : ''
833         };
834         if (this.jumbotron) {
835             cfg.cls = 'jumbotron';
836         }
837         if (this.cls) {
838             cfg.cls = this.cls + '';
839         }
840         
841         if (this.sticky.length) {
842             
843             var bd = Roo.get(document.body);
844             if (!bd.hasClass('bootstrap-sticky')) {
845                 bd.addClass('bootstrap-sticky');
846                 Roo.select('html',true).setStyle('height', '100%');
847             }
848              
849             cfg.cls += 'bootstrap-sticky-' + this.sticky;
850         }
851         
852         
853         if (this.well.length) {
854             switch (this.well) {
855                 case 'lg':
856                 case 'sm':
857                     cfg.cls +=' well well-' +this.well;
858                     break;
859                 default:
860                     cfg.cls +=' well';
861                     break;
862             }
863         }
864         
865         var body = cfg;
866         
867         if (this.panel.length) {
868             cfg.cls += ' panel panel-' + this.panel;
869             cfg.cn = [];
870             if (this.header.length) {
871                 cfg.cn.push({
872                     
873                     cls : 'panel-heading',
874                     cn : [{
875                         tag: 'h3',
876                         cls : 'panel-title',
877                         html : this.header
878                     }]
879                     
880                 });
881             }
882             body = false;
883             cfg.cn.push({
884                 cls : 'panel-body',
885                 html : this.html
886             });
887             
888             
889             if (this.footer.length) {
890                 cfg.cn.push({
891                     cls : 'panel-footer',
892                     html : this.footer
893                     
894                 });
895             }
896             
897         }
898         if (body) {
899             body.html = this.html || cfg.html;
900         }
901         if (!cfg.cls.length) {
902             cfg.cls =  'container';
903         }
904         
905         return cfg;
906     }
907    
908 });
909
910  /*
911  * - LGPL
912  *
913  * image
914  * 
915  */
916
917
918 /**
919  * @class Roo.bootstrap.Img
920  * @extends Roo.bootstrap.Component
921  * Bootstrap Img class
922  * @cfg {Boolean} imgResponsive false | true
923  * @cfg {String} border rounded | circle | thumbnail
924  * @cfg {String} src image source
925  * @cfg {String} alt image alternative text
926  * @cfg {String} href a tag href
927  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
928  * 
929  * @constructor
930  * Create a new Input
931  * @param {Object} config The config object
932  */
933
934 Roo.bootstrap.Img = function(config){
935     Roo.bootstrap.Img.superclass.constructor.call(this, config);
936     
937     this.addEvents({
938         // img events
939         /**
940          * @event click
941          * The img click event for the img.
942          * @param {Roo.EventObject} e
943          */
944         "click" : true
945     });
946 };
947
948 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
949     
950     imgResponsive: true,
951     border: '',
952     src: '',
953     href: false,
954     target: false,
955
956     getAutoCreate : function(){
957         
958         var cfg = {
959             tag: 'img',
960             cls: 'img-responsive',
961             html : null
962         }
963         
964         cfg.html = this.html || cfg.html;
965         
966         cfg.src = this.src || cfg.src;
967         
968         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
969             cfg.cls += ' img-' + this.border;
970         }
971         
972         if(this.alt){
973             cfg.alt = this.alt;
974         }
975         
976         if(this.href){
977             var a = {
978                 tag: 'a',
979                 href: this.href,
980                 cn: [
981                     cfg
982                 ]
983             }
984             
985             if(this.target){
986                 a.target = this.target;
987             }
988             
989         }
990         
991         
992         return (this.href) ? a : cfg;
993     },
994     
995     initEvents: function() {
996         
997         if(!this.href){
998             this.el.on('click', this.onClick, this);
999         }
1000     },
1001     
1002     onClick : function(e)
1003     {
1004         Roo.log('img onclick');
1005         this.fireEvent('click', this, e);
1006     }
1007    
1008 });
1009
1010  /*
1011  * - LGPL
1012  *
1013  * header
1014  * 
1015  */
1016
1017 /**
1018  * @class Roo.bootstrap.Header
1019  * @extends Roo.bootstrap.Component
1020  * Bootstrap Header class
1021  * @cfg {String} html content of header
1022  * @cfg {Number} level (1|2|3|4|5|6) default 1
1023  * 
1024  * @constructor
1025  * Create a new Header
1026  * @param {Object} config The config object
1027  */
1028
1029
1030 Roo.bootstrap.Header  = function(config){
1031     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1032 };
1033
1034 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1035     
1036     //href : false,
1037     html : false,
1038     level : 1,
1039     
1040     
1041     
1042     getAutoCreate : function(){
1043         
1044         var cfg = {
1045             tag: 'h' + (1 *this.level),
1046             html: this.html || 'fill in html'
1047         } ;
1048         
1049         return cfg;
1050     }
1051    
1052 });
1053
1054  
1055
1056  /*
1057  * Based on:
1058  * Ext JS Library 1.1.1
1059  * Copyright(c) 2006-2007, Ext JS, LLC.
1060  *
1061  * Originally Released Under LGPL - original licence link has changed is not relivant.
1062  *
1063  * Fork - LGPL
1064  * <script type="text/javascript">
1065  */
1066  
1067 /**
1068  * @class Roo.bootstrap.MenuMgr
1069  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1070  * @singleton
1071  */
1072 Roo.bootstrap.MenuMgr = function(){
1073    var menus, active, groups = {}, attached = false, lastShow = new Date();
1074
1075    // private - called when first menu is created
1076    function init(){
1077        menus = {};
1078        active = new Roo.util.MixedCollection();
1079        Roo.get(document).addKeyListener(27, function(){
1080            if(active.length > 0){
1081                hideAll();
1082            }
1083        });
1084    }
1085
1086    // private
1087    function hideAll(){
1088        if(active && active.length > 0){
1089            var c = active.clone();
1090            c.each(function(m){
1091                m.hide();
1092            });
1093        }
1094    }
1095
1096    // private
1097    function onHide(m){
1098        active.remove(m);
1099        if(active.length < 1){
1100            Roo.get(document).un("mouseup", onMouseDown);
1101             
1102            attached = false;
1103        }
1104    }
1105
1106    // private
1107    function onShow(m){
1108        var last = active.last();
1109        lastShow = new Date();
1110        active.add(m);
1111        if(!attached){
1112           Roo.get(document).on("mouseup", onMouseDown);
1113            
1114            attached = true;
1115        }
1116        if(m.parentMenu){
1117           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1118           m.parentMenu.activeChild = m;
1119        }else if(last && last.isVisible()){
1120           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1121        }
1122    }
1123
1124    // private
1125    function onBeforeHide(m){
1126        if(m.activeChild){
1127            m.activeChild.hide();
1128        }
1129        if(m.autoHideTimer){
1130            clearTimeout(m.autoHideTimer);
1131            delete m.autoHideTimer;
1132        }
1133    }
1134
1135    // private
1136    function onBeforeShow(m){
1137        var pm = m.parentMenu;
1138        if(!pm && !m.allowOtherMenus){
1139            hideAll();
1140        }else if(pm && pm.activeChild && active != m){
1141            pm.activeChild.hide();
1142        }
1143    }
1144
1145    // private
1146    function onMouseDown(e){
1147         Roo.log("on MouseDown");
1148         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1149            hideAll();
1150         }
1151         
1152         
1153    }
1154
1155    // private
1156    function onBeforeCheck(mi, state){
1157        if(state){
1158            var g = groups[mi.group];
1159            for(var i = 0, l = g.length; i < l; i++){
1160                if(g[i] != mi){
1161                    g[i].setChecked(false);
1162                }
1163            }
1164        }
1165    }
1166
1167    return {
1168
1169        /**
1170         * Hides all menus that are currently visible
1171         */
1172        hideAll : function(){
1173             hideAll();  
1174        },
1175
1176        // private
1177        register : function(menu){
1178            if(!menus){
1179                init();
1180            }
1181            menus[menu.id] = menu;
1182            menu.on("beforehide", onBeforeHide);
1183            menu.on("hide", onHide);
1184            menu.on("beforeshow", onBeforeShow);
1185            menu.on("show", onShow);
1186            var g = menu.group;
1187            if(g && menu.events["checkchange"]){
1188                if(!groups[g]){
1189                    groups[g] = [];
1190                }
1191                groups[g].push(menu);
1192                menu.on("checkchange", onCheck);
1193            }
1194        },
1195
1196         /**
1197          * Returns a {@link Roo.menu.Menu} object
1198          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1199          * be used to generate and return a new Menu instance.
1200          */
1201        get : function(menu){
1202            if(typeof menu == "string"){ // menu id
1203                return menus[menu];
1204            }else if(menu.events){  // menu instance
1205                return menu;
1206            }
1207            /*else if(typeof menu.length == 'number'){ // array of menu items?
1208                return new Roo.bootstrap.Menu({items:menu});
1209            }else{ // otherwise, must be a config
1210                return new Roo.bootstrap.Menu(menu);
1211            }
1212            */
1213            return false;
1214        },
1215
1216        // private
1217        unregister : function(menu){
1218            delete menus[menu.id];
1219            menu.un("beforehide", onBeforeHide);
1220            menu.un("hide", onHide);
1221            menu.un("beforeshow", onBeforeShow);
1222            menu.un("show", onShow);
1223            var g = menu.group;
1224            if(g && menu.events["checkchange"]){
1225                groups[g].remove(menu);
1226                menu.un("checkchange", onCheck);
1227            }
1228        },
1229
1230        // private
1231        registerCheckable : function(menuItem){
1232            var g = menuItem.group;
1233            if(g){
1234                if(!groups[g]){
1235                    groups[g] = [];
1236                }
1237                groups[g].push(menuItem);
1238                menuItem.on("beforecheckchange", onBeforeCheck);
1239            }
1240        },
1241
1242        // private
1243        unregisterCheckable : function(menuItem){
1244            var g = menuItem.group;
1245            if(g){
1246                groups[g].remove(menuItem);
1247                menuItem.un("beforecheckchange", onBeforeCheck);
1248            }
1249        }
1250    };
1251 }();/*
1252  * - LGPL
1253  *
1254  * menu
1255  * 
1256  */
1257
1258 /**
1259  * @class Roo.bootstrap.Menu
1260  * @extends Roo.bootstrap.Component
1261  * Bootstrap Menu class - container for MenuItems
1262  * @cfg {String} type type of menu
1263  * 
1264  * @constructor
1265  * Create a new Menu
1266  * @param {Object} config The config object
1267  */
1268
1269
1270 Roo.bootstrap.Menu = function(config){
1271     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1272     if (this.registerMenu) {
1273         Roo.bootstrap.MenuMgr.register(this);
1274     }
1275     this.addEvents({
1276         /**
1277          * @event beforeshow
1278          * Fires before this menu is displayed
1279          * @param {Roo.menu.Menu} this
1280          */
1281         beforeshow : true,
1282         /**
1283          * @event beforehide
1284          * Fires before this menu is hidden
1285          * @param {Roo.menu.Menu} this
1286          */
1287         beforehide : true,
1288         /**
1289          * @event show
1290          * Fires after this menu is displayed
1291          * @param {Roo.menu.Menu} this
1292          */
1293         show : true,
1294         /**
1295          * @event hide
1296          * Fires after this menu is hidden
1297          * @param {Roo.menu.Menu} this
1298          */
1299         hide : true,
1300         /**
1301          * @event click
1302          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1303          * @param {Roo.menu.Menu} this
1304          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1305          * @param {Roo.EventObject} e
1306          */
1307         click : true,
1308         /**
1309          * @event mouseover
1310          * Fires when the mouse is hovering over this menu
1311          * @param {Roo.menu.Menu} this
1312          * @param {Roo.EventObject} e
1313          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1314          */
1315         mouseover : true,
1316         /**
1317          * @event mouseout
1318          * Fires when the mouse exits this menu
1319          * @param {Roo.menu.Menu} this
1320          * @param {Roo.EventObject} e
1321          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1322          */
1323         mouseout : true,
1324         /**
1325          * @event itemclick
1326          * Fires when a menu item contained in this menu is clicked
1327          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1328          * @param {Roo.EventObject} e
1329          */
1330         itemclick: true
1331     });
1332     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1333 };
1334
1335 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1336     
1337    /// html : false,
1338     //align : '',
1339     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1340     type: false,
1341     /**
1342      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1343      */
1344     registerMenu : true,
1345     
1346     menuItems :false, // stores the menu items..
1347     
1348     hidden:true,
1349     
1350     parentMenu : false,
1351     
1352     getChildContainer : function() {
1353         return this.el;  
1354     },
1355     
1356     getAutoCreate : function(){
1357          
1358         //if (['right'].indexOf(this.align)!==-1) {
1359         //    cfg.cn[1].cls += ' pull-right'
1360         //}
1361         var cfg = {
1362             tag : 'ul',
1363             cls : 'dropdown-menu' ,
1364             style : 'z-index:1000'
1365             
1366         }
1367         
1368         if (this.type === 'submenu') {
1369             cfg.cls = 'submenu active'
1370         }
1371         
1372         return cfg;
1373     },
1374     initEvents : function() {
1375         
1376        // Roo.log("ADD event");
1377        // Roo.log(this.triggerEl.dom);
1378         this.triggerEl.on('click', this.onTriggerPress, this);
1379         this.triggerEl.addClass('dropdown-toggle');
1380         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1381
1382         this.el.on("mouseover", this.onMouseOver, this);
1383         this.el.on("mouseout", this.onMouseOut, this);
1384         
1385         
1386     },
1387     findTargetItem : function(e){
1388         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1389         if(!t){
1390             return false;
1391         }
1392         //Roo.log(t);         Roo.log(t.id);
1393         if(t && t.id){
1394             //Roo.log(this.menuitems);
1395             return this.menuitems.get(t.id);
1396             
1397             //return this.items.get(t.menuItemId);
1398         }
1399         
1400         return false;
1401     },
1402     onClick : function(e){
1403         Roo.log("menu.onClick");
1404         var t = this.findTargetItem(e);
1405         if(!t){
1406             return;
1407         }
1408         Roo.log(e);
1409         /*
1410         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1411             if(t == this.activeItem && t.shouldDeactivate(e)){
1412                 this.activeItem.deactivate();
1413                 delete this.activeItem;
1414                 return;
1415             }
1416             if(t.canActivate){
1417                 this.setActiveItem(t, true);
1418             }
1419             return;
1420             
1421             
1422         }
1423         */
1424         Roo.log('pass click event');
1425         
1426         t.onClick(e);
1427         
1428         this.fireEvent("click", this, t, e);
1429         
1430         this.hide();
1431     },
1432      onMouseOver : function(e){
1433         var t  = this.findTargetItem(e);
1434         //Roo.log(t);
1435         //if(t){
1436         //    if(t.canActivate && !t.disabled){
1437         //        this.setActiveItem(t, true);
1438         //    }
1439         //}
1440         
1441         this.fireEvent("mouseover", this, e, t);
1442     },
1443     isVisible : function(){
1444         return !this.hidden;
1445     },
1446      onMouseOut : function(e){
1447         var t  = this.findTargetItem(e);
1448         
1449         //if(t ){
1450         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1451         //        this.activeItem.deactivate();
1452         //        delete this.activeItem;
1453         //    }
1454         //}
1455         this.fireEvent("mouseout", this, e, t);
1456     },
1457     
1458     
1459     /**
1460      * Displays this menu relative to another element
1461      * @param {String/HTMLElement/Roo.Element} element The element to align to
1462      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1463      * the element (defaults to this.defaultAlign)
1464      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1465      */
1466     show : function(el, pos, parentMenu){
1467         this.parentMenu = parentMenu;
1468         if(!this.el){
1469             this.render();
1470         }
1471         this.fireEvent("beforeshow", this);
1472         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1473     },
1474      /**
1475      * Displays this menu at a specific xy position
1476      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1477      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1478      */
1479     showAt : function(xy, parentMenu, /* private: */_e){
1480         this.parentMenu = parentMenu;
1481         if(!this.el){
1482             this.render();
1483         }
1484         if(_e !== false){
1485             this.fireEvent("beforeshow", this);
1486             
1487             //xy = this.el.adjustForConstraints(xy);
1488         }
1489         //this.el.setXY(xy);
1490         //this.el.show();
1491         this.hideMenuItems();
1492         this.hidden = false;
1493         this.triggerEl.addClass('open');
1494         this.focus();
1495         this.fireEvent("show", this);
1496     },
1497     
1498     focus : function(){
1499         return;
1500         if(!this.hidden){
1501             this.doFocus.defer(50, this);
1502         }
1503     },
1504
1505     doFocus : function(){
1506         if(!this.hidden){
1507             this.focusEl.focus();
1508         }
1509     },
1510
1511     /**
1512      * Hides this menu and optionally all parent menus
1513      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1514      */
1515     hide : function(deep){
1516         
1517         this.hideMenuItems();
1518         if(this.el && this.isVisible()){
1519             this.fireEvent("beforehide", this);
1520             if(this.activeItem){
1521                 this.activeItem.deactivate();
1522                 this.activeItem = null;
1523             }
1524             this.triggerEl.removeClass('open');;
1525             this.hidden = true;
1526             this.fireEvent("hide", this);
1527         }
1528         if(deep === true && this.parentMenu){
1529             this.parentMenu.hide(true);
1530         }
1531     },
1532     
1533     onTriggerPress  : function(e)
1534     {
1535         
1536         Roo.log('trigger press');
1537         //Roo.log(e.getTarget());
1538        // Roo.log(this.triggerEl.dom);
1539         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1540             return;
1541         }
1542         if (this.isVisible()) {
1543             Roo.log('hide');
1544             this.hide();
1545         } else {
1546             this.show(this.triggerEl, false, false);
1547         }
1548         
1549         
1550     },
1551     
1552          
1553        
1554     
1555     hideMenuItems : function()
1556     {
1557         //$(backdrop).remove()
1558         Roo.select('.open',true).each(function(aa) {
1559             
1560             aa.removeClass('open');
1561           //var parent = getParent($(this))
1562           //var relatedTarget = { relatedTarget: this }
1563           
1564            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1565           //if (e.isDefaultPrevented()) return
1566            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1567         })
1568     },
1569     addxtypeChild : function (tree, cntr) {
1570         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1571           
1572         this.menuitems.add(comp);
1573         return comp;
1574
1575     },
1576     getEl : function()
1577     {
1578         Roo.log(this.el);
1579         return this.el;
1580     }
1581 });
1582
1583  
1584  /*
1585  * - LGPL
1586  *
1587  * menu item
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.MenuItem
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap MenuItem class
1596  * @cfg {String} html the menu label
1597  * @cfg {String} href the link
1598  * @cfg {Boolean} preventDefault (true | false) default true
1599  * 
1600  * 
1601  * @constructor
1602  * Create a new MenuItem
1603  * @param {Object} config The config object
1604  */
1605
1606
1607 Roo.bootstrap.MenuItem = function(config){
1608     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1609     this.addEvents({
1610         // raw events
1611         /**
1612          * @event click
1613          * The raw click event for the entire grid.
1614          * @param {Roo.EventObject} e
1615          */
1616         "click" : true
1617     });
1618 };
1619
1620 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1621     
1622     href : false,
1623     html : false,
1624     preventDefault: true,
1625     
1626     getAutoCreate : function(){
1627         var cfg= {
1628             tag: 'li',
1629         cls: 'dropdown-menu-item',
1630             cn: [
1631             {
1632                 tag : 'a',
1633                 href : '#',
1634                 html : 'Link'
1635             }
1636             ]
1637     };
1638         
1639         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1640         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1641         return cfg;
1642     },
1643     
1644     initEvents: function() {
1645         
1646         //this.el.select('a').on('click', this.onClick, this);
1647         
1648     },
1649     onClick : function(e)
1650     {
1651         Roo.log('item on click ');
1652         //if(this.preventDefault){
1653         //    e.preventDefault();
1654         //}
1655         //this.parent().hideMenuItems();
1656         
1657         this.fireEvent('click', this, e);
1658     },
1659     getEl : function()
1660     {
1661         return this.el;
1662     }
1663 });
1664
1665  
1666
1667  /*
1668  * - LGPL
1669  *
1670  * menu separator
1671  * 
1672  */
1673
1674
1675 /**
1676  * @class Roo.bootstrap.MenuSeparator
1677  * @extends Roo.bootstrap.Component
1678  * Bootstrap MenuSeparator class
1679  * 
1680  * @constructor
1681  * Create a new MenuItem
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.MenuSeparator = function(config){
1687     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1691     
1692     getAutoCreate : function(){
1693         var cfg = {
1694             cls: 'divider',
1695             tag : 'li'
1696         };
1697         
1698         return cfg;
1699     }
1700    
1701 });
1702
1703  
1704
1705  
1706 /*
1707 <div class="modal fade">
1708   <div class="modal-dialog">
1709     <div class="modal-content">
1710       <div class="modal-header">
1711         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1712         <h4 class="modal-title">Modal title</h4>
1713       </div>
1714       <div class="modal-body">
1715         <p>One fine body&hellip;</p>
1716       </div>
1717       <div class="modal-footer">
1718         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1719         <button type="button" class="btn btn-primary">Save changes</button>
1720       </div>
1721     </div><!-- /.modal-content -->
1722   </div><!-- /.modal-dialog -->
1723 </div><!-- /.modal -->
1724 */
1725 /*
1726  * - LGPL
1727  *
1728  * page contgainer.
1729  * 
1730  */
1731
1732 /**
1733  * @class Roo.bootstrap.Modal
1734  * @extends Roo.bootstrap.Component
1735  * Bootstrap Modal class
1736  * @cfg {String} title Title of dialog
1737  * @cfg {Array} buttons Array of buttons or standard button set..
1738  * 
1739  * @constructor
1740  * Create a new Modal Dialog
1741  * @param {Object} config The config object
1742  */
1743
1744 Roo.bootstrap.Modal = function(config){
1745     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1746     this.addEvents({
1747         // raw events
1748         /**
1749          * @event btnclick
1750          * The raw btnclick event for the button
1751          * @param {Roo.EventObject} e
1752          */
1753         "btnclick" : true
1754     });
1755 };
1756
1757 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1758     
1759     title : 'test dialog',
1760    
1761     buttons : false,
1762     
1763     onRender : function(ct, position)
1764     {
1765         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1766      
1767         if(!this.el){
1768             var cfg = Roo.apply({},  this.getAutoCreate());
1769             cfg.id = Roo.id();
1770             //if(!cfg.name){
1771             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1772             //}
1773             //if (!cfg.name.length) {
1774             //    delete cfg.name;
1775            // }
1776             if (this.cls) {
1777                 cfg.cls += ' ' + this.cls;
1778             }
1779             if (this.style) {
1780                 cfg.style = this.style;
1781             }
1782             this.el = Roo.get(document.body).createChild(cfg, position);
1783         }
1784         //var type = this.el.dom.type;
1785         
1786         if(this.tabIndex !== undefined){
1787             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1788         }
1789         
1790         
1791         
1792         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1793         this.maskEl.enableDisplayMode("block");
1794         this.maskEl.hide();
1795         //this.el.addClass("x-dlg-modal");
1796     
1797         if (this.buttons) {
1798             Roo.each(this.buttons, function(bb) {
1799                 b = Roo.apply({}, bb);
1800                 b.xns = b.xns || Roo.bootstrap;
1801                 b.xtype = b.xtype || 'Button';
1802                 if (typeof(b.listeners) == 'undefined') {
1803                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1804                 }
1805                 
1806                 var btn = Roo.factory(b);
1807                 
1808                 btn.onRender(this.el.select('.modal-footer').first());
1809                 
1810             },this);
1811         }
1812         // render the children.
1813         var nitems = [];
1814         
1815         if(typeof(this.items) != 'undefined'){
1816             var items = this.items;
1817             delete this.items;
1818
1819             for(var i =0;i < items.length;i++) {
1820                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1821             }
1822         }
1823         
1824         this.items = nitems;
1825         this.initEvents();
1826         //this.el.addClass([this.fieldClass, this.cls]);
1827         
1828     },
1829     getAutoCreate : function(){
1830         
1831         
1832         var bdy = {
1833                 cls : 'modal-body',
1834                 html : this.html || ''
1835         };
1836         
1837          
1838         return modal = {
1839             cls: "modal fade",
1840             cn : [
1841                 {
1842                     cls: "modal-dialog",
1843                     cn : [
1844                         {
1845                             cls : "modal-content",
1846                             cn : [
1847                                 {
1848                                     cls : 'modal-header',
1849                                     cn : [
1850                                         {
1851                                             tag: 'button',
1852                                             cls : 'close',
1853                                             html : '&times'
1854                                         },
1855                                         {
1856                                             tag: 'h4',
1857                                             cls : 'modal-title',
1858                                             html : this.title
1859                                         }
1860                                     
1861                                     ]
1862                                 },
1863                                 bdy,
1864                                 {
1865                                     cls : 'modal-footer' 
1866                                 }
1867                                 
1868                                 
1869                             ]
1870                             
1871                         }
1872                     ]
1873                         
1874                 }
1875             ]
1876             
1877             
1878         };
1879           
1880     },
1881     getChildContainer : function() {
1882          
1883          return this.el.select('.modal-body',true).first();
1884         
1885     },
1886     getButtonContainer : function() {
1887          return this.el.select('.modal-footer',true).first();
1888         
1889     },
1890     initEvents : function()
1891     {
1892         this.el.select('.modal-header .close').on('click', this.hide, this);
1893 //        
1894 //        this.addxtype(this);
1895     },
1896     show : function() {
1897         
1898         if (!this.rendered) {
1899             this.render();
1900         }
1901        
1902         this.el.addClass('on');
1903         this.el.removeClass('fade');
1904         this.el.setStyle('display', 'block');
1905         Roo.get(document.body).addClass("x-body-masked");
1906         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1907         this.maskEl.show();
1908         this.el.setStyle('zIndex', '10001');
1909         this.fireEvent('show', this);
1910         
1911         
1912     },
1913     hide : function()
1914     {
1915         Roo.log('Modal hide?!');
1916         this.maskEl.hide();
1917         Roo.get(document.body).removeClass("x-body-masked");
1918         this.el.removeClass('on');
1919         this.el.addClass('fade');
1920         this.el.setStyle('display', 'none');
1921         this.fireEvent('hide', this);
1922     },
1923     onButtonClick: function(btn,e)
1924     {
1925         //Roo.log([a,b,c]);
1926         this.fireEvent('btnclick', btn.name, e);
1927     }
1928 });
1929
1930
1931 Roo.apply(Roo.bootstrap.Modal,  {
1932     /**
1933          * Button config that displays a single OK button
1934          * @type Object
1935          */
1936         OK :  [{
1937             name : 'ok',
1938             weight : 'primary',
1939             html : 'OK'
1940         }], 
1941         /**
1942          * Button config that displays Yes and No buttons
1943          * @type Object
1944          */
1945         YESNO : [
1946             {
1947                 name  : 'no',
1948                 html : 'No'
1949             },
1950             {
1951                 name  :'yes',
1952                 weight : 'primary',
1953                 html : 'Yes'
1954             }
1955         ],
1956         
1957         /**
1958          * Button config that displays OK and Cancel buttons
1959          * @type Object
1960          */
1961         OKCANCEL : [
1962             {
1963                name : 'cancel',
1964                 html : 'Cancel'
1965             },
1966             {
1967                 name : 'ok',
1968                 weight : 'primary',
1969                 html : 'OK'
1970             }
1971         ],
1972         /**
1973          * Button config that displays Yes, No and Cancel buttons
1974          * @type Object
1975          */
1976         YESNOCANCEL : [
1977             {
1978                 name : 'yes',
1979                 weight : 'primary',
1980                 html : 'Yes'
1981             },
1982             {
1983                 name : 'no',
1984                 html : 'No'
1985             },
1986             {
1987                 name : 'cancel',
1988                 html : 'Cancel'
1989             }
1990         ]
1991 });
1992  /*
1993  * - LGPL
1994  *
1995  * navbar
1996  * 
1997  */
1998
1999 /**
2000  * @class Roo.bootstrap.Navbar
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap Navbar class
2003  * @cfg {Boolean} sidebar has side bar
2004  * @cfg {Boolean} bar is a bar?
2005  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2006  * @cfg {String} brand what is brand
2007  * @cfg {Boolean} inverse is inverted color
2008  * @cfg {String} type (nav | pills | tabs)
2009  * @cfg {Boolean} arrangement stacked | justified
2010  * @cfg {String} align (left | right) alignment
2011  * @cfg {String} brand_href href of the brand
2012  * @cfg {Boolean} main (true|false) main nav bar? default false
2013  *
2014  * 
2015  * @constructor
2016  * Create a new Navbar
2017  * @param {Object} config The config object
2018  */
2019
2020
2021 Roo.bootstrap.Navbar = function(config){
2022     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2023 };
2024
2025 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2026     
2027     sidebar: false,
2028     
2029     bar: false,
2030     brand: '',
2031     inverse: false,
2032     position: '',
2033     align : false,
2034     type: 'nav',
2035     arrangement: '',
2036     brand_href: false,
2037     main : false,
2038     
2039     getAutoCreate : function(){
2040         var cfg = {
2041             cls : 'navbar'
2042         };
2043         
2044         if (this.sidebar === true) {
2045             cfg = {
2046                 tag: 'div',
2047                 cls: 'sidebar-nav'
2048             };
2049             return cfg;
2050         }
2051         
2052         if (this.bar === true) {
2053             cfg = {
2054                 tag: 'nav',
2055                 cls: 'navbar',
2056                 role: 'navigation',
2057                 cn: [
2058                     {
2059                         tag: 'div',
2060                         cls: 'navbar-header',
2061                         cn: [
2062                             {
2063                             tag: 'button',
2064                             type: 'button',
2065                             cls: 'navbar-toggle',
2066                             'data-toggle': 'collapse',
2067                             cn: [
2068                                 {
2069                                     tag: 'span',
2070                                     cls: 'sr-only',
2071                                     html: 'Toggle navigation'
2072                                 },
2073                                 {
2074                                     tag: 'span',
2075                                     cls: 'icon-bar'
2076                                 },
2077                                 {
2078                                     tag: 'span',
2079                                     cls: 'icon-bar'
2080                                 },
2081                                 {
2082                                     tag: 'span',
2083                                     cls: 'icon-bar'
2084                                 }
2085                             ]
2086                             }
2087                         ]
2088                     },
2089                     {
2090                     tag: 'div',
2091                     cls: 'collapse navbar-collapse'
2092                     }
2093                 ]
2094             };
2095             
2096             cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2097             
2098             if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2099                 cfg.cls += ' navbar-' + this.position;
2100                 cfg.tag = this.position  == 'fixed-bottom' ? 'footer' : 'header';
2101             }
2102             
2103             if (this.brand !== '') {
2104                 cfg.cn[0].cn.push({
2105                     tag: 'a',
2106                     href: this.brand_href ? this.brand_href : '#',
2107                     cls: 'navbar-brand',
2108                     cn: [
2109                     this.brand
2110                     ]
2111                 });
2112             }
2113             
2114             if(this.main){
2115                 cfg.cls += ' main-nav';
2116             }
2117             
2118             
2119             return cfg;
2120         
2121         } else if (this.bar === false) {
2122             
2123         } else {
2124             Roo.log('Property \'bar\' in of Navbar must be either true or false')
2125         }
2126         
2127         cfg.cn = [
2128             {
2129                 cls: 'nav',
2130                 tag : 'ul'
2131             }
2132         ];
2133         
2134         if (['tabs','pills'].indexOf(this.type)!==-1) {
2135             cfg.cn[0].cls += ' nav-' + this.type
2136         } else {
2137             if (this.type!=='nav') {
2138             Roo.log('nav type must be nav/tabs/pills')
2139             }
2140             cfg.cn[0].cls += ' navbar-nav'
2141         }
2142         
2143         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2144             cfg.cn[0].cls += ' nav-' + this.arrangement;
2145         }
2146         
2147         if (this.align === 'right') {
2148             cfg.cn[0].cls += ' navbar-right';
2149         }
2150         if (this.inverse) {
2151             cfg.cls += ' navbar-inverse';
2152             
2153         }
2154         
2155         
2156         return cfg;
2157     },
2158     
2159     initEvents :function ()
2160     {
2161         //Roo.log(this.el.select('.navbar-toggle',true));
2162         this.el.select('.navbar-toggle',true).on('click', function() {
2163            // Roo.log('click');
2164             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2165         }, this);
2166     },
2167     
2168     
2169     getChildContainer : function()
2170     {
2171         if (this.bar === true) {
2172             return this.el.select('.collapse',true).first();
2173         }
2174         console.log(this);
2175         return this.el;
2176     }
2177    
2178 });
2179
2180  
2181
2182  /*
2183  * - LGPL
2184  *
2185  * nav group
2186  * 
2187  */
2188
2189 /**
2190  * @class Roo.bootstrap.NavGroup
2191  * @extends Roo.bootstrap.Component
2192  * Bootstrap NavGroup class
2193  * @cfg {String} align left | right
2194  * @cfg {Boolean} inverse false | true
2195  * @cfg {String} type (nav|pills|tab) default nav
2196  * 
2197  * @constructor
2198  * Create a new nav group
2199  * @param {Object} config The config object
2200  */
2201
2202 Roo.bootstrap.NavGroup = function(config){
2203     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2204 };
2205
2206 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
2207     
2208     align: '',
2209     inverse: false,
2210     form: false,
2211     type: 'nav',
2212     
2213     getAutoCreate : function(){
2214         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2215         
2216         cfg = {
2217             tag : 'ul',
2218             cls: 'nav' 
2219         }
2220         
2221         if (['tabs','pills'].indexOf(this.type)!==-1) {
2222             cfg.cls += ' nav-' + this.type
2223         } else {
2224             if (this.type!=='nav') {
2225                 Roo.log('nav type must be nav/tabs/pills')
2226             }
2227             cfg.cls += ' navbar-nav'
2228         }
2229         
2230         if (this.parent().sidebar === true) {
2231             cfg = {
2232                 tag: 'ul',
2233                 cls: 'dashboard-menu'
2234             }
2235             
2236             return cfg;
2237         }
2238         
2239         if (this.form === true) {
2240             cfg = {
2241                 tag: 'form',
2242                 cls: 'navbar-form'
2243             }
2244             
2245             if (this.align === 'right') {
2246                 cfg.cls += ' navbar-right';
2247             } else {
2248                 cfg.cls += ' navbar-left';
2249             }
2250         }
2251         
2252         if (this.align === 'right') {
2253             cfg.cls += ' navbar-right';
2254         }
2255         
2256         if (this.inverse) {
2257             cfg.cls += ' navbar-inverse';
2258             
2259         }
2260         
2261         
2262         return cfg;
2263     }
2264    
2265 });
2266
2267  
2268
2269  /*
2270  * - LGPL
2271  *
2272  * row
2273  * 
2274  */
2275
2276 /**
2277  * @class Roo.bootstrap.Navbar.Item
2278  * @extends Roo.bootstrap.Component
2279  * Bootstrap Navbar.Button class
2280  * @cfg {String} href  link to
2281  * @cfg {String} html content of button
2282  * @cfg {String} badge text inside badge
2283  * @cfg {String} glyphicon name of glyphicon
2284  * @cfg {String} icon name of font awesome icon
2285  * @cfg {Boolena} active Is item active
2286  * @cfg {Boolean} preventDefault (true | false) default false
2287   
2288  * @constructor
2289  * Create a new Navbar Button
2290  * @param {Object} config The config object
2291  */
2292 Roo.bootstrap.Navbar.Item = function(config){
2293     Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2294     this.addEvents({
2295         // raw events
2296         /**
2297          * @event click
2298          * The raw click event for the entire grid.
2299          * @param {Roo.EventObject} e
2300          */
2301         "click" : true
2302     });
2303 };
2304
2305 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component,  {
2306     
2307     href: false,
2308     html: '',
2309     badge: '',
2310     icon: false,
2311     glyphicon: false,
2312     icon: false,
2313     active: false,
2314     preventDefault : false,
2315     
2316     getAutoCreate : function(){
2317         
2318         var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2319         
2320         if (this.parent().parent().sidebar === true) {
2321             cfg = {
2322                 tag: 'li',
2323                 cls: '',
2324                 cn: [
2325                     {
2326                         tag: 'p',
2327                         cls: ''
2328                     }
2329                 ]
2330             }
2331             
2332             if (this.html) {
2333                 cfg.cn[0].html = this.html;
2334             }
2335             
2336             if (this.active) {
2337                 this.cls += ' active';
2338             }
2339             
2340             if (this.menu) {
2341                 cfg.cn[0].cls += ' dropdown-toggle';
2342                 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2343             }
2344             
2345             if (this.href) {
2346                 cfg.cn[0].tag = 'a',
2347                 cfg.cn[0].href = this.href;
2348             }
2349             
2350             if (this.glyphicon) {
2351                 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2352             }
2353             
2354             if (this.icon) {
2355                 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2356             }
2357             
2358             return cfg;
2359         }
2360         
2361         cfg = {
2362             tag: 'li',
2363             cls: 'nav-item'
2364         }
2365         
2366         if (this.active) {
2367             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2368         }
2369             
2370         cfg.cn = [
2371             {
2372                 tag: 'p',
2373                 html: 'Text'
2374             }
2375         ];
2376         
2377         if (this.glyphicon) {
2378             if(cfg.html){cfg.html = ' ' + this.html};
2379             cfg.cn=[
2380                 {
2381                     tag: 'span',
2382                     cls: 'glyphicon glyphicon-' + this.glyphicon
2383                 }
2384             ];
2385         }
2386         
2387         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2388         
2389         if (this.menu) {
2390             cfg.cn[0].tag='a';
2391             cfg.cn[0].href='#';
2392             cfg.cn[0].html += " <span class='caret'></span>";
2393         //}else if (!this.href) {
2394         //    cfg.cn[0].tag='p';
2395         //    cfg.cn[0].cls='navbar-text';
2396         } else {
2397             cfg.cn[0].tag='a';
2398             cfg.cn[0].href=this.href||'#';
2399             cfg.cn[0].html=this.html;
2400         }
2401         
2402         if (this.badge !== '') {
2403             
2404             cfg.cn[0].cn=[
2405                 cfg.cn[0].html + ' ',
2406                 {
2407                     tag: 'span',
2408                     cls: 'badge',
2409                     html: this.badge
2410                 }
2411             ];
2412             cfg.cn[0].html=''
2413         }
2414          
2415         if (this.icon) {
2416             cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2417         }
2418         
2419         return cfg;
2420     },
2421     initEvents: function() {
2422        // Roo.log('init events?');
2423        // Roo.log(this.el.dom);
2424         this.el.select('a',true).on('click', this.onClick, this);
2425     },
2426     
2427     onClick : function(e)
2428     {
2429         if(this.preventDefault){
2430             e.preventDefault();
2431         }
2432         
2433         if(this.fireEvent('click', this, e) === false){
2434             return;
2435         };
2436         
2437         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2438             this.onTabsClick(e);
2439         } 
2440     },
2441     
2442     onTabsClick : function(e)
2443     {
2444         Roo.each(this.parent().el.select('.active',true).elements, function(v){
2445             v.removeClass('active');
2446         })
2447
2448         this.el.addClass('active');
2449
2450         if(this.href && this.href.substring(0,1) == '#'){
2451             var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2452
2453             Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2454                 v.removeClass('active');
2455             });
2456
2457             tab.addClass('active');
2458         }
2459     }
2460    
2461 });
2462  
2463
2464  /*
2465  * - LGPL
2466  *
2467  * row
2468  * 
2469  */
2470
2471 /**
2472  * @class Roo.bootstrap.Row
2473  * @extends Roo.bootstrap.Component
2474  * Bootstrap Row class (contains columns...)
2475  * 
2476  * @constructor
2477  * Create a new Row
2478  * @param {Object} config The config object
2479  */
2480
2481 Roo.bootstrap.Row = function(config){
2482     Roo.bootstrap.Row.superclass.constructor.call(this, config);
2483 };
2484
2485 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
2486     
2487     getAutoCreate : function(){
2488        return {
2489             cls: 'row clearfix'
2490        };
2491     }
2492     
2493     
2494 });
2495
2496  
2497
2498  /*
2499  * - LGPL
2500  *
2501  * element
2502  * 
2503  */
2504
2505 /**
2506  * @class Roo.bootstrap.Element
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap Element class
2509  * @cfg {String} html contents of the element
2510  * @cfg {String} tag tag of the element
2511  * @cfg {String} cls class of the element
2512  * 
2513  * @constructor
2514  * Create a new Element
2515  * @param {Object} config The config object
2516  */
2517
2518 Roo.bootstrap.Element = function(config){
2519     Roo.bootstrap.Element.superclass.constructor.call(this, config);
2520 };
2521
2522 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
2523     
2524     tag: 'div',
2525     cls: '',
2526     html: '',
2527      
2528     
2529     getAutoCreate : function(){
2530         
2531         var cfg = {
2532             tag: this.tag,
2533             cls: this.cls,
2534             html: this.html
2535         }
2536         
2537         
2538         
2539         return cfg;
2540     }
2541    
2542 });
2543
2544  
2545
2546  /*
2547  * - LGPL
2548  *
2549  * pagination
2550  * 
2551  */
2552
2553 /**
2554  * @class Roo.bootstrap.Pagination
2555  * @extends Roo.bootstrap.Component
2556  * Bootstrap Pagination class
2557  * @cfg {String} size xs | sm | md | lg
2558  * @cfg {Boolean} inverse false | true
2559  * 
2560  * @constructor
2561  * Create a new Pagination
2562  * @param {Object} config The config object
2563  */
2564
2565 Roo.bootstrap.Pagination = function(config){
2566     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2567 };
2568
2569 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
2570     
2571     cls: false,
2572     size: false,
2573     inverse: false,
2574     
2575     getAutoCreate : function(){
2576         var cfg = {
2577             tag: 'ul',
2578                 cls: 'pagination'
2579         };
2580         if (this.inverse) {
2581             cfg.cls += ' inverse';
2582         }
2583         if (this.html) {
2584             cfg.html=this.html;
2585         }
2586         if (this.cls) {
2587             cfg.cls += " " + this.cls;
2588         }
2589         return cfg;
2590     }
2591    
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * Pagination item
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.PaginationItem
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap PaginationItem class
2608  * @cfg {String} html text
2609  * @cfg {String} href the link
2610  * @cfg {Boolean} preventDefault (true | false) default true
2611  * @cfg {Boolean} active (true | false) default false
2612  * 
2613  * 
2614  * @constructor
2615  * Create a new PaginationItem
2616  * @param {Object} config The config object
2617  */
2618
2619
2620 Roo.bootstrap.PaginationItem = function(config){
2621     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2622     this.addEvents({
2623         // raw events
2624         /**
2625          * @event click
2626          * The raw click event for the entire grid.
2627          * @param {Roo.EventObject} e
2628          */
2629         "click" : true
2630     });
2631 };
2632
2633 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
2634     
2635     href : false,
2636     html : false,
2637     preventDefault: true,
2638     active : false,
2639     cls : false,
2640     
2641     getAutoCreate : function(){
2642         var cfg= {
2643             tag: 'li',
2644             cn: [
2645                 {
2646                     tag : 'a',
2647                     href : this.href ? this.href : '#',
2648                     html : this.html ? this.html : ''
2649                 }
2650             ]
2651         };
2652         
2653         if(this.cls){
2654             cfg.cls = this.cls;
2655         }
2656         
2657         if(this.active){
2658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2659         }
2660         
2661         return cfg;
2662     },
2663     
2664     initEvents: function() {
2665         
2666         this.el.on('click', this.onClick, this);
2667         
2668     },
2669     onClick : function(e)
2670     {
2671         Roo.log('PaginationItem on click ');
2672         if(this.preventDefault){
2673             e.preventDefault();
2674         }
2675         
2676         this.fireEvent('click', this, e);
2677     }
2678    
2679 });
2680
2681  
2682
2683  /*
2684  * - LGPL
2685  *
2686  * slider
2687  * 
2688  */
2689
2690
2691 /**
2692  * @class Roo.bootstrap.Slider
2693  * @extends Roo.bootstrap.Component
2694  * Bootstrap Slider class
2695  *    
2696  * @constructor
2697  * Create a new Slider
2698  * @param {Object} config The config object
2699  */
2700
2701 Roo.bootstrap.Slider = function(config){
2702     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2703 };
2704
2705 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
2706     
2707     getAutoCreate : function(){
2708         
2709         var cfg = {
2710             tag: 'div',
2711             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2712             cn: [
2713                 {
2714                     tag: 'a',
2715                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
2716                 }
2717             ]
2718         }
2719         
2720         return cfg;
2721     }
2722    
2723 });
2724
2725  /*
2726  * - LGPL
2727  *
2728  * table
2729  * 
2730  */
2731
2732 /**
2733  * @class Roo.bootstrap.Table
2734  * @extends Roo.bootstrap.Component
2735  * Bootstrap Table class
2736  * @cfg {String} cls table class
2737  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2738  * @cfg {String} bgcolor Specifies the background color for a table
2739  * @cfg {Number} border Specifies whether the table cells should have borders or not
2740  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2741  * @cfg {Number} cellspacing Specifies the space between cells
2742  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2743  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2744  * @cfg {String} sortable Specifies that the table should be sortable
2745  * @cfg {String} summary Specifies a summary of the content of a table
2746  * @cfg {Number} width Specifies the width of a table
2747  * 
2748  * @cfg {boolean} striped Should the rows be alternative striped
2749  * @cfg {boolean} bordered Add borders to the table
2750  * @cfg {boolean} hover Add hover highlighting
2751  * @cfg {boolean} condensed Format condensed
2752  * @cfg {boolean} responsive Format condensed
2753  *
2754  
2755  
2756  * 
2757  * @constructor
2758  * Create a new Table
2759  * @param {Object} config The config object
2760  */
2761
2762 Roo.bootstrap.Table = function(config){
2763     Roo.bootstrap.Table.superclass.constructor.call(this, config);
2764     
2765     if (this.sm) {
2766         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2767         this.sm = this.selModel;
2768         this.sm.xmodule = this.xmodule || false;
2769     }
2770     if (this.cm && typeof(this.cm.config) == 'undefined') {
2771         this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2772         this.cm = this.colModel;
2773         this.cm.xmodule = this.xmodule || false;
2774     }
2775     if (this.store) {
2776         this.store= Roo.factory(this.store, Roo.data);
2777         this.ds = this.store;
2778         this.ds.xmodule = this.xmodule || false;
2779          
2780     }
2781 };
2782
2783 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
2784     
2785     cls: false,
2786     align: false,
2787     bgcolor: false,
2788     border: false,
2789     cellpadding: false,
2790     cellspacing: false,
2791     frame: false,
2792     rules: false,
2793     sortable: false,
2794     summary: false,
2795     width: false,
2796     striped : false,
2797     bordered: false,
2798     hover:  false,
2799     condensed : false,
2800     responsive : false,
2801     sm : false,
2802     cm : false,
2803     store : false,
2804     
2805     getAutoCreate : function(){
2806         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2807         
2808         cfg = {
2809             tag: 'table',
2810             cls : 'table',
2811             cn : []
2812         }
2813             
2814         if (this.striped) {
2815             cfg.cls += ' table-striped';
2816         }
2817         if (this.hover) {
2818             cfg.cls += ' table-hover';
2819         }
2820         if (this.bordered) {
2821             cfg.cls += ' table-bordered';
2822         }
2823         if (this.condensed) {
2824             cfg.cls += ' table-condensed';
2825         }
2826         if (this.responsive) {
2827             cfg.cls += ' table-responsive';
2828         }
2829         
2830           
2831         
2832         
2833         if (this.cls) {
2834             cfg.cls+=  ' ' +this.cls;
2835         }
2836         
2837         // this lot should be simplifed...
2838         
2839         if (this.align) {
2840             cfg.align=this.align;
2841         }
2842         if (this.bgcolor) {
2843             cfg.bgcolor=this.bgcolor;
2844         }
2845         if (this.border) {
2846             cfg.border=this.border;
2847         }
2848         if (this.cellpadding) {
2849             cfg.cellpadding=this.cellpadding;
2850         }
2851         if (this.cellspacing) {
2852             cfg.cellspacing=this.cellspacing;
2853         }
2854         if (this.frame) {
2855             cfg.frame=this.frame;
2856         }
2857         if (this.rules) {
2858             cfg.rules=this.rules;
2859         }
2860         if (this.sortable) {
2861             cfg.sortable=this.sortable;
2862         }
2863         if (this.summary) {
2864             cfg.summary=this.summary;
2865         }
2866         if (this.width) {
2867             cfg.width=this.width;
2868         }
2869         
2870         if(this.store || this.cm){
2871             cfg.cn.push(this.renderHeader());
2872             cfg.cn.push(this.renderBody());
2873             cfg.cn.push(this.renderFooter());
2874             
2875             cfg.cls+=  ' TableGrid';
2876         }
2877         
2878         return cfg;
2879     },
2880 //    
2881 //    initTableGrid : function()
2882 //    {
2883 //        var cfg = {};
2884 //        
2885 //        var header = {
2886 //            tag: 'thead',
2887 //            cn : []
2888 //        };
2889 //        
2890 //        var cm = this.cm;
2891 //        
2892 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2893 //            header.cn.push({
2894 //                tag: 'th',
2895 //                html: cm.getColumnHeader(i)
2896 //            })
2897 //        }
2898 //        
2899 //        cfg.push(header);
2900 //        
2901 //        return cfg;
2902 //        
2903 //        
2904 //    },
2905     
2906     initEvents : function()
2907     {   
2908         if(!this.store || !this.cm){
2909             return;
2910         }
2911         
2912         Roo.log('initEvents with ds!!!!');
2913         
2914 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2915 //        this.maskEl.enableDisplayMode("block");
2916 //        this.maskEl.show();
2917         
2918         this.store.on('load', this.onLoad, this);
2919         this.store.on('beforeload', this.onBeforeLoad, this);
2920         
2921         this.store.load();
2922         
2923         
2924         
2925     },
2926     
2927     renderHeader : function()
2928     {
2929         var header = {
2930             tag: 'thead',
2931             cn : []
2932         };
2933         
2934         var cm = this.cm;
2935         
2936         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2937             header.cn.push({
2938                 tag: 'th',
2939                 html: cm.getColumnHeader(i)
2940             })
2941         }
2942         
2943         return header;
2944     },
2945     
2946     renderBody : function()
2947     {
2948         var body = {
2949             tag: 'tbody',
2950             cn : []
2951         };
2952         
2953         return body;
2954     },
2955     
2956     renderFooter : function()
2957     {
2958         var footer = {
2959             tag: 'tfoot',
2960             cn : []
2961         };
2962         
2963         return footer;
2964     },
2965     
2966     onLoad : function()
2967     {
2968         Roo.log('ds onload');
2969         
2970         var cm = this.cm;
2971         
2972         var tbody = this.el.select('tbody', true).first();
2973         
2974         var renders = [];
2975         
2976         if(this.store.getCount() > 0){
2977             this.store.data.each(function(d){
2978                 var row = {
2979                     tag : 'tr',
2980                     cn : []
2981                 };
2982                 
2983                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2984                     var renderer = cm.getRenderer(i);
2985                     var value = '';
2986                     var id = Roo.id();
2987                     
2988                     if(typeof(renderer) !== 'undefined'){
2989                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
2990                     }
2991                     
2992                     if(typeof(value) === 'object'){
2993                         renders.push({
2994                             id : id,
2995                             cfg : value 
2996                         })
2997                     }
2998                     
2999                     row.cn.push({
3000                         tag: 'td',
3001                         id: id,
3002                         html: (typeof(value) === 'object') ? '' : value
3003                     })
3004                    
3005                 }
3006                 
3007                 tbody.createChild(row);
3008                 
3009             });
3010         }
3011         
3012         if(renders.length){
3013             Roo.each(renders, function(r){
3014                 r.cfg.render(Roo.get(r.id));
3015             })
3016         }
3017 //        
3018 //        if(this.loadMask){
3019 //            this.maskEl.hide();
3020 //        }
3021     },
3022     
3023     onBeforeLoad : function()
3024     {
3025         Roo.log('ds onBeforeLoad');
3026         
3027         this.clear();
3028         
3029 //        if(this.loadMask){
3030 //            this.maskEl.show();
3031 //        }
3032     },
3033     
3034     clear : function()
3035     {
3036         this.el.select('tbody', true).first().dom.innerHTML = '';
3037     },
3038     
3039     getSelectionModel : function(){
3040         if(!this.selModel){
3041             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3042         }
3043         return this.selModel;
3044     }
3045    
3046 });
3047
3048  
3049
3050  /*
3051  * - LGPL
3052  *
3053  * table cell
3054  * 
3055  */
3056
3057 /**
3058  * @class Roo.bootstrap.TableCell
3059  * @extends Roo.bootstrap.Component
3060  * Bootstrap TableCell class
3061  * @cfg {String} html cell contain text
3062  * @cfg {String} cls cell class
3063  * @cfg {String} tag cell tag (td|th) default td
3064  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3065  * @cfg {String} align Aligns the content in a cell
3066  * @cfg {String} axis Categorizes cells
3067  * @cfg {String} bgcolor Specifies the background color of a cell
3068  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3069  * @cfg {Number} colspan Specifies the number of columns a cell should span
3070  * @cfg {String} headers Specifies one or more header cells a cell is related to
3071  * @cfg {Number} height Sets the height of a cell
3072  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3073  * @cfg {Number} rowspan Sets the number of rows a cell should span
3074  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3075  * @cfg {String} valign Vertical aligns the content in a cell
3076  * @cfg {Number} width Specifies the width of a cell
3077  * 
3078  * @constructor
3079  * Create a new TableCell
3080  * @param {Object} config The config object
3081  */
3082
3083 Roo.bootstrap.TableCell = function(config){
3084     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3085 };
3086
3087 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
3088     
3089     html: false,
3090     cls: false,
3091     tag: false,
3092     abbr: false,
3093     align: false,
3094     axis: false,
3095     bgcolor: false,
3096     charoff: false,
3097     colspan: false,
3098     headers: false,
3099     height: false,
3100     nowrap: false,
3101     rowspan: false,
3102     scope: false,
3103     valign: false,
3104     width: false,
3105     
3106     
3107     getAutoCreate : function(){
3108         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3109         
3110         cfg = {
3111             tag: 'td'
3112         }
3113         
3114         if(this.tag){
3115             cfg.tag = this.tag;
3116         }
3117         
3118         if (this.html) {
3119             cfg.html=this.html
3120         }
3121         if (this.cls) {
3122             cfg.cls=this.cls
3123         }
3124         if (this.abbr) {
3125             cfg.abbr=this.abbr
3126         }
3127         if (this.align) {
3128             cfg.align=this.align
3129         }
3130         if (this.axis) {
3131             cfg.axis=this.axis
3132         }
3133         if (this.bgcolor) {
3134             cfg.bgcolor=this.bgcolor
3135         }
3136         if (this.charoff) {
3137             cfg.charoff=this.charoff
3138         }
3139         if (this.colspan) {
3140             cfg.colspan=this.colspan
3141         }
3142         if (this.headers) {
3143             cfg.headers=this.headers
3144         }
3145         if (this.height) {
3146             cfg.height=this.height
3147         }
3148         if (this.nowrap) {
3149             cfg.nowrap=this.nowrap
3150         }
3151         if (this.rowspan) {
3152             cfg.rowspan=this.rowspan
3153         }
3154         if (this.scope) {
3155             cfg.scope=this.scope
3156         }
3157         if (this.valign) {
3158             cfg.valign=this.valign
3159         }
3160         if (this.width) {
3161             cfg.width=this.width
3162         }
3163         
3164         
3165         return cfg;
3166     }
3167    
3168 });
3169
3170  
3171
3172  /*
3173  * - LGPL
3174  *
3175  * table row
3176  * 
3177  */
3178
3179 /**
3180  * @class Roo.bootstrap.TableRow
3181  * @extends Roo.bootstrap.Component
3182  * Bootstrap TableRow class
3183  * @cfg {String} cls row class
3184  * @cfg {String} align Aligns the content in a table row
3185  * @cfg {String} bgcolor Specifies a background color for a table row
3186  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3187  * @cfg {String} valign Vertical aligns the content in a table row
3188  * 
3189  * @constructor
3190  * Create a new TableRow
3191  * @param {Object} config The config object
3192  */
3193
3194 Roo.bootstrap.TableRow = function(config){
3195     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3196 };
3197
3198 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
3199     
3200     cls: false,
3201     align: false,
3202     bgcolor: false,
3203     charoff: false,
3204     valign: false,
3205     
3206     getAutoCreate : function(){
3207         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag: 'tr'
3211         }
3212             
3213         if(this.cls){
3214             cfg.cls = this.cls;
3215         }
3216         if(this.align){
3217             cfg.align = this.align;
3218         }
3219         if(this.bgcolor){
3220             cfg.bgcolor = this.bgcolor;
3221         }
3222         if(this.charoff){
3223             cfg.charoff = this.charoff;
3224         }
3225         if(this.valign){
3226             cfg.valign = this.valign;
3227         }
3228         
3229         return cfg;
3230     }
3231    
3232 });
3233
3234  
3235
3236  /*
3237  * - LGPL
3238  *
3239  * table body
3240  * 
3241  */
3242
3243 /**
3244  * @class Roo.bootstrap.TableBody
3245  * @extends Roo.bootstrap.Component
3246  * Bootstrap TableBody class
3247  * @cfg {String} cls element class
3248  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3249  * @cfg {String} align Aligns the content inside the element
3250  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3251  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3252  * 
3253  * @constructor
3254  * Create a new TableBody
3255  * @param {Object} config The config object
3256  */
3257
3258 Roo.bootstrap.TableBody = function(config){
3259     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3260 };
3261
3262 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
3263     
3264     cls: false,
3265     tag: false,
3266     align: false,
3267     charoff: false,
3268     valign: false,
3269     
3270     getAutoCreate : function(){
3271         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3272         
3273         cfg = {
3274             tag: 'tbody'
3275         }
3276             
3277         if (this.cls) {
3278             cfg.cls=this.cls
3279         }
3280         if(this.tag){
3281             cfg.tag = this.tag;
3282         }
3283         
3284         if(this.align){
3285             cfg.align = this.align;
3286         }
3287         if(this.charoff){
3288             cfg.charoff = this.charoff;
3289         }
3290         if(this.valign){
3291             cfg.valign = this.valign;
3292         }
3293         
3294         return cfg;
3295     }
3296     
3297     
3298 //    initEvents : function()
3299 //    {
3300 //        
3301 //        if(!this.store){
3302 //            return;
3303 //        }
3304 //        
3305 //        this.store = Roo.factory(this.store, Roo.data);
3306 //        this.store.on('load', this.onLoad, this);
3307 //        
3308 //        this.store.load();
3309 //        
3310 //    },
3311 //    
3312 //    onLoad: function () 
3313 //    {   
3314 //        this.fireEvent('load', this);
3315 //    }
3316 //    
3317 //   
3318 });
3319
3320  
3321
3322  /*
3323  * Based on:
3324  * Ext JS Library 1.1.1
3325  * Copyright(c) 2006-2007, Ext JS, LLC.
3326  *
3327  * Originally Released Under LGPL - original licence link has changed is not relivant.
3328  *
3329  * Fork - LGPL
3330  * <script type="text/javascript">
3331  */
3332
3333 // as we use this in bootstrap.
3334 Roo.namespace('Roo.form');
3335  /**
3336  * @class Roo.form.Action
3337  * Internal Class used to handle form actions
3338  * @constructor
3339  * @param {Roo.form.BasicForm} el The form element or its id
3340  * @param {Object} config Configuration options
3341  */
3342
3343  
3344  
3345 // define the action interface
3346 Roo.form.Action = function(form, options){
3347     this.form = form;
3348     this.options = options || {};
3349 };
3350 /**
3351  * Client Validation Failed
3352  * @const 
3353  */
3354 Roo.form.Action.CLIENT_INVALID = 'client';
3355 /**
3356  * Server Validation Failed
3357  * @const 
3358  */
3359 Roo.form.Action.SERVER_INVALID = 'server';
3360  /**
3361  * Connect to Server Failed
3362  * @const 
3363  */
3364 Roo.form.Action.CONNECT_FAILURE = 'connect';
3365 /**
3366  * Reading Data from Server Failed
3367  * @const 
3368  */
3369 Roo.form.Action.LOAD_FAILURE = 'load';
3370
3371 Roo.form.Action.prototype = {
3372     type : 'default',
3373     failureType : undefined,
3374     response : undefined,
3375     result : undefined,
3376
3377     // interface method
3378     run : function(options){
3379
3380     },
3381
3382     // interface method
3383     success : function(response){
3384
3385     },
3386
3387     // interface method
3388     handleResponse : function(response){
3389
3390     },
3391
3392     // default connection failure
3393     failure : function(response){
3394         
3395         this.response = response;
3396         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3397         this.form.afterAction(this, false);
3398     },
3399
3400     processResponse : function(response){
3401         this.response = response;
3402         if(!response.responseText){
3403             return true;
3404         }
3405         this.result = this.handleResponse(response);
3406         return this.result;
3407     },
3408
3409     // utility functions used internally
3410     getUrl : function(appendParams){
3411         var url = this.options.url || this.form.url || this.form.el.dom.action;
3412         if(appendParams){
3413             var p = this.getParams();
3414             if(p){
3415                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3416             }
3417         }
3418         return url;
3419     },
3420
3421     getMethod : function(){
3422         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3423     },
3424
3425     getParams : function(){
3426         var bp = this.form.baseParams;
3427         var p = this.options.params;
3428         if(p){
3429             if(typeof p == "object"){
3430                 p = Roo.urlEncode(Roo.applyIf(p, bp));
3431             }else if(typeof p == 'string' && bp){
3432                 p += '&' + Roo.urlEncode(bp);
3433             }
3434         }else if(bp){
3435             p = Roo.urlEncode(bp);
3436         }
3437         return p;
3438     },
3439
3440     createCallback : function(){
3441         return {
3442             success: this.success,
3443             failure: this.failure,
3444             scope: this,
3445             timeout: (this.form.timeout*1000),
3446             upload: this.form.fileUpload ? this.success : undefined
3447         };
3448     }
3449 };
3450
3451 Roo.form.Action.Submit = function(form, options){
3452     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3453 };
3454
3455 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3456     type : 'submit',
3457
3458     haveProgress : false,
3459     uploadComplete : false,
3460     
3461     // uploadProgress indicator.
3462     uploadProgress : function()
3463     {
3464         if (!this.form.progressUrl) {
3465             return;
3466         }
3467         
3468         if (!this.haveProgress) {
3469             Roo.MessageBox.progress("Uploading", "Uploading");
3470         }
3471         if (this.uploadComplete) {
3472            Roo.MessageBox.hide();
3473            return;
3474         }
3475         
3476         this.haveProgress = true;
3477    
3478         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3479         
3480         var c = new Roo.data.Connection();
3481         c.request({
3482             url : this.form.progressUrl,
3483             params: {
3484                 id : uid
3485             },
3486             method: 'GET',
3487             success : function(req){
3488                //console.log(data);
3489                 var rdata = false;
3490                 var edata;
3491                 try  {
3492                    rdata = Roo.decode(req.responseText)
3493                 } catch (e) {
3494                     Roo.log("Invalid data from server..");
3495                     Roo.log(edata);
3496                     return;
3497                 }
3498                 if (!rdata || !rdata.success) {
3499                     Roo.log(rdata);
3500                     Roo.MessageBox.alert(Roo.encode(rdata));
3501                     return;
3502                 }
3503                 var data = rdata.data;
3504                 
3505                 if (this.uploadComplete) {
3506                    Roo.MessageBox.hide();
3507                    return;
3508                 }
3509                    
3510                 if (data){
3511                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3512                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3513                     );
3514                 }
3515                 this.uploadProgress.defer(2000,this);
3516             },
3517        
3518             failure: function(data) {
3519                 Roo.log('progress url failed ');
3520                 Roo.log(data);
3521             },
3522             scope : this
3523         });
3524            
3525     },
3526     
3527     
3528     run : function()
3529     {
3530         // run get Values on the form, so it syncs any secondary forms.
3531         this.form.getValues();
3532         
3533         var o = this.options;
3534         var method = this.getMethod();
3535         var isPost = method == 'POST';
3536         if(o.clientValidation === false || this.form.isValid()){
3537             
3538             if (this.form.progressUrl) {
3539                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3540                     (new Date() * 1) + '' + Math.random());
3541                     
3542             } 
3543             
3544             
3545             Roo.Ajax.request(Roo.apply(this.createCallback(), {
3546                 form:this.form.el.dom,
3547                 url:this.getUrl(!isPost),
3548                 method: method,
3549                 params:isPost ? this.getParams() : null,
3550                 isUpload: this.form.fileUpload
3551             }));
3552             
3553             this.uploadProgress();
3554
3555         }else if (o.clientValidation !== false){ // client validation failed
3556             this.failureType = Roo.form.Action.CLIENT_INVALID;
3557             this.form.afterAction(this, false);
3558         }
3559     },
3560
3561     success : function(response)
3562     {
3563         this.uploadComplete= true;
3564         if (this.haveProgress) {
3565             Roo.MessageBox.hide();
3566         }
3567         
3568         
3569         var result = this.processResponse(response);
3570         if(result === true || result.success){
3571             this.form.afterAction(this, true);
3572             return;
3573         }
3574         if(result.errors){
3575             this.form.markInvalid(result.errors);
3576             this.failureType = Roo.form.Action.SERVER_INVALID;
3577         }
3578         this.form.afterAction(this, false);
3579     },
3580     failure : function(response)
3581     {
3582         this.uploadComplete= true;
3583         if (this.haveProgress) {
3584             Roo.MessageBox.hide();
3585         }
3586         
3587         this.response = response;
3588         this.failureType = Roo.form.Action.CONNECT_FAILURE;
3589         this.form.afterAction(this, false);
3590     },
3591     
3592     handleResponse : function(response){
3593         if(this.form.errorReader){
3594             var rs = this.form.errorReader.read(response);
3595             var errors = [];
3596             if(rs.records){
3597                 for(var i = 0, len = rs.records.length; i < len; i++) {
3598                     var r = rs.records[i];
3599                     errors[i] = r.data;
3600                 }
3601             }
3602             if(errors.length < 1){
3603                 errors = null;
3604             }
3605             return {
3606                 success : rs.success,
3607                 errors : errors
3608             };
3609         }
3610         var ret = false;
3611         try {
3612             ret = Roo.decode(response.responseText);
3613         } catch (e) {
3614             ret = {
3615                 success: false,
3616                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3617                 errors : []
3618             };
3619         }
3620         return ret;
3621         
3622     }
3623 });
3624
3625
3626 Roo.form.Action.Load = function(form, options){
3627     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3628     this.reader = this.form.reader;
3629 };
3630
3631 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3632     type : 'load',
3633
3634     run : function(){
3635         
3636         Roo.Ajax.request(Roo.apply(
3637                 this.createCallback(), {
3638                     method:this.getMethod(),
3639                     url:this.getUrl(false),
3640                     params:this.getParams()
3641         }));
3642     },
3643
3644     success : function(response){
3645         
3646         var result = this.processResponse(response);
3647         if(result === true || !result.success || !result.data){
3648             this.failureType = Roo.form.Action.LOAD_FAILURE;
3649             this.form.afterAction(this, false);
3650             return;
3651         }
3652         this.form.clearInvalid();
3653         this.form.setValues(result.data);
3654         this.form.afterAction(this, true);
3655     },
3656
3657     handleResponse : function(response){
3658         if(this.form.reader){
3659             var rs = this.form.reader.read(response);
3660             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3661             return {
3662                 success : rs.success,
3663                 data : data
3664             };
3665         }
3666         return Roo.decode(response.responseText);
3667     }
3668 });
3669
3670 Roo.form.Action.ACTION_TYPES = {
3671     'load' : Roo.form.Action.Load,
3672     'submit' : Roo.form.Action.Submit
3673 };/*
3674  * - LGPL
3675  *
3676  * form
3677  * 
3678  */
3679
3680 /**
3681  * @class Roo.bootstrap.Form
3682  * @extends Roo.bootstrap.Component
3683  * Bootstrap Form class
3684  * @cfg {String} method  GET | POST (default POST)
3685  * @cfg {String} labelAlign top | left (default top)
3686   * @cfg {String} align left  | right - for navbars
3687
3688  * 
3689  * @constructor
3690  * Create a new Form
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.Form = function(config){
3696     Roo.bootstrap.Form.superclass.constructor.call(this, config);
3697     this.addEvents({
3698         /**
3699          * @event clientvalidation
3700          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3701          * @param {Form} this
3702          * @param {Boolean} valid true if the form has passed client-side validation
3703          */
3704         clientvalidation: true,
3705         /**
3706          * @event beforeaction
3707          * Fires before any action is performed. Return false to cancel the action.
3708          * @param {Form} this
3709          * @param {Action} action The action to be performed
3710          */
3711         beforeaction: true,
3712         /**
3713          * @event actionfailed
3714          * Fires when an action fails.
3715          * @param {Form} this
3716          * @param {Action} action The action that failed
3717          */
3718         actionfailed : true,
3719         /**
3720          * @event actioncomplete
3721          * Fires when an action is completed.
3722          * @param {Form} this
3723          * @param {Action} action The action that completed
3724          */
3725         actioncomplete : true
3726     });
3727     
3728 };
3729
3730 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
3731       
3732      /**
3733      * @cfg {String} method
3734      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3735      */
3736     method : 'POST',
3737     /**
3738      * @cfg {String} url
3739      * The URL to use for form actions if one isn't supplied in the action options.
3740      */
3741     /**
3742      * @cfg {Boolean} fileUpload
3743      * Set to true if this form is a file upload.
3744      */
3745      
3746     /**
3747      * @cfg {Object} baseParams
3748      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3749      */
3750       
3751     /**
3752      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3753      */
3754     timeout: 30,
3755     /**
3756      * @cfg {Sting} align (left|right) for navbar forms
3757      */
3758     align : 'left',
3759
3760     // private
3761     activeAction : null,
3762  
3763     /**
3764      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3765      * element by passing it or its id or mask the form itself by passing in true.
3766      * @type Mixed
3767      */
3768     waitMsgTarget : false,
3769     
3770      
3771     
3772     /**
3773      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3774      * element by passing it or its id or mask the form itself by passing in true.
3775      * @type Mixed
3776      */
3777     
3778     getAutoCreate : function(){
3779         
3780         var cfg = {
3781             tag: 'form',
3782             method : this.method || 'POST',
3783             id : this.id || Roo.id(),
3784             cls : ''
3785         }
3786         if (this.parent().xtype.match(/^Nav/)) {
3787             cfg.cls = 'navbar-form navbar-' + this.align;
3788             
3789         }
3790         
3791         if (this.labelAlign == 'left' ) {
3792             cfg.cls += ' form-horizontal';
3793         }
3794         
3795         
3796         return cfg;
3797     },
3798     initEvents : function()
3799     {
3800         this.el.on('submit', this.onSubmit, this);
3801         
3802         
3803     },
3804     // private
3805     onSubmit : function(e){
3806         e.stopEvent();
3807     },
3808     
3809      /**
3810      * Returns true if client-side validation on the form is successful.
3811      * @return Boolean
3812      */
3813     isValid : function(){
3814         var items = this.getItems();
3815         var valid = true;
3816         items.each(function(f){
3817            if(!f.validate()){
3818                valid = false;
3819                
3820            }
3821         });
3822         return valid;
3823     },
3824     /**
3825      * Returns true if any fields in this form have changed since their original load.
3826      * @return Boolean
3827      */
3828     isDirty : function(){
3829         var dirty = false;
3830         var items = this.getItems();
3831         items.each(function(f){
3832            if(f.isDirty()){
3833                dirty = true;
3834                return false;
3835            }
3836            return true;
3837         });
3838         return dirty;
3839     },
3840      /**
3841      * Performs a predefined action (submit or load) or custom actions you define on this form.
3842      * @param {String} actionName The name of the action type
3843      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
3844      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3845      * accept other config options):
3846      * <pre>
3847 Property          Type             Description
3848 ----------------  ---------------  ----------------------------------------------------------------------------------
3849 url               String           The url for the action (defaults to the form's url)
3850 method            String           The form method to use (defaults to the form's method, or POST if not defined)
3851 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
3852 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
3853                                    validate the form on the client (defaults to false)
3854      * </pre>
3855      * @return {BasicForm} this
3856      */
3857     doAction : function(action, options){
3858         if(typeof action == 'string'){
3859             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3860         }
3861         if(this.fireEvent('beforeaction', this, action) !== false){
3862             this.beforeAction(action);
3863             action.run.defer(100, action);
3864         }
3865         return this;
3866     },
3867     
3868     // private
3869     beforeAction : function(action){
3870         var o = action.options;
3871         
3872         // not really supported yet.. ??
3873         
3874         //if(this.waitMsgTarget === true){
3875             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3876         //}else if(this.waitMsgTarget){
3877         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3878         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3879         //}else {
3880         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3881        // }
3882          
3883     },
3884
3885     // private
3886     afterAction : function(action, success){
3887         this.activeAction = null;
3888         var o = action.options;
3889         
3890         //if(this.waitMsgTarget === true){
3891             this.el.unmask();
3892         //}else if(this.waitMsgTarget){
3893         //    this.waitMsgTarget.unmask();
3894         //}else{
3895         //    Roo.MessageBox.updateProgress(1);
3896         //    Roo.MessageBox.hide();
3897        // }
3898         // 
3899         if(success){
3900             if(o.reset){
3901                 this.reset();
3902             }
3903             Roo.callback(o.success, o.scope, [this, action]);
3904             this.fireEvent('actioncomplete', this, action);
3905             
3906         }else{
3907             
3908             // failure condition..
3909             // we have a scenario where updates need confirming.
3910             // eg. if a locking scenario exists..
3911             // we look for { errors : { needs_confirm : true }} in the response.
3912             if (
3913                 (typeof(action.result) != 'undefined')  &&
3914                 (typeof(action.result.errors) != 'undefined')  &&
3915                 (typeof(action.result.errors.needs_confirm) != 'undefined')
3916            ){
3917                 var _t = this;
3918                 Roo.log("not supported yet");
3919                  /*
3920                 
3921                 Roo.MessageBox.confirm(
3922                     "Change requires confirmation",
3923                     action.result.errorMsg,
3924                     function(r) {
3925                         if (r != 'yes') {
3926                             return;
3927                         }
3928                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
3929                     }
3930                     
3931                 );
3932                 */
3933                 
3934                 
3935                 return;
3936             }
3937             
3938             Roo.callback(o.failure, o.scope, [this, action]);
3939             // show an error message if no failed handler is set..
3940             if (!this.hasListener('actionfailed')) {
3941                 Roo.log("need to add dialog support");
3942                 /*
3943                 Roo.MessageBox.alert("Error",
3944                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3945                         action.result.errorMsg :
3946                         "Saving Failed, please check your entries or try again"
3947                 );
3948                 */
3949             }
3950             
3951             this.fireEvent('actionfailed', this, action);
3952         }
3953         
3954     },
3955     /**
3956      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3957      * @param {String} id The value to search for
3958      * @return Field
3959      */
3960     findField : function(id){
3961         var items = this.getItems();
3962         var field = items.get(id);
3963         if(!field){
3964              items.each(function(f){
3965                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
3966                     field = f;
3967                     return false;
3968                 }
3969                 return true;
3970             });
3971         }
3972         return field || null;
3973     },
3974      /**
3975      * Mark fields in this form invalid in bulk.
3976      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
3977      * @return {BasicForm} this
3978      */
3979     markInvalid : function(errors){
3980         if(errors instanceof Array){
3981             for(var i = 0, len = errors.length; i < len; i++){
3982                 var fieldError = errors[i];
3983                 var f = this.findField(fieldError.id);
3984                 if(f){
3985                     f.markInvalid(fieldError.msg);
3986                 }
3987             }
3988         }else{
3989             var field, id;
3990             for(id in errors){
3991                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
3992                     field.markInvalid(errors[id]);
3993                 }
3994             }
3995         }
3996         //Roo.each(this.childForms || [], function (f) {
3997         //    f.markInvalid(errors);
3998         //});
3999         
4000         return this;
4001     },
4002
4003     /**
4004      * Set values for fields in this form in bulk.
4005      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4006      * @return {BasicForm} this
4007      */
4008     setValues : function(values){
4009         if(values instanceof Array){ // array of objects
4010             for(var i = 0, len = values.length; i < len; i++){
4011                 var v = values[i];
4012                 var f = this.findField(v.id);
4013                 if(f){
4014                     f.setValue(v.value);
4015                     if(this.trackResetOnLoad){
4016                         f.originalValue = f.getValue();
4017                     }
4018                 }
4019             }
4020         }else{ // object hash
4021             var field, id;
4022             for(id in values){
4023                 if(typeof values[id] != 'function' && (field = this.findField(id))){
4024                     
4025                     if (field.setFromData && 
4026                         field.valueField && 
4027                         field.displayField &&
4028                         // combos' with local stores can 
4029                         // be queried via setValue()
4030                         // to set their value..
4031                         (field.store && !field.store.isLocal)
4032                         ) {
4033                         // it's a combo
4034                         var sd = { };
4035                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4036                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4037                         field.setFromData(sd);
4038                         
4039                     } else {
4040                         field.setValue(values[id]);
4041                     }
4042                     
4043                     
4044                     if(this.trackResetOnLoad){
4045                         field.originalValue = field.getValue();
4046                     }
4047                 }
4048             }
4049         }
4050          
4051         //Roo.each(this.childForms || [], function (f) {
4052         //    f.setValues(values);
4053         //});
4054                 
4055         return this;
4056     },
4057
4058     /**
4059      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4060      * they are returned as an array.
4061      * @param {Boolean} asString
4062      * @return {Object}
4063      */
4064     getValues : function(asString){
4065         //if (this.childForms) {
4066             // copy values from the child forms
4067         //    Roo.each(this.childForms, function (f) {
4068         //        this.setValues(f.getValues());
4069         //    }, this);
4070         //}
4071         
4072         
4073         
4074         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4075         if(asString === true){
4076             return fs;
4077         }
4078         return Roo.urlDecode(fs);
4079     },
4080     
4081     /**
4082      * Returns the fields in this form as an object with key/value pairs. 
4083      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4084      * @return {Object}
4085      */
4086     getFieldValues : function(with_hidden)
4087     {
4088         var items = this.getItems();
4089         var ret = {};
4090         items.each(function(f){
4091             if (!f.getName()) {
4092                 return;
4093             }
4094             var v = f.getValue();
4095             if (f.inputType =='radio') {
4096                 if (typeof(ret[f.getName()]) == 'undefined') {
4097                     ret[f.getName()] = ''; // empty..
4098                 }
4099                 
4100                 if (!f.el.dom.checked) {
4101                     return;
4102                     
4103                 }
4104                 v = f.el.dom.value;
4105                 
4106             }
4107             
4108             // not sure if this supported any more..
4109             if ((typeof(v) == 'object') && f.getRawValue) {
4110                 v = f.getRawValue() ; // dates..
4111             }
4112             // combo boxes where name != hiddenName...
4113             if (f.name != f.getName()) {
4114                 ret[f.name] = f.getRawValue();
4115             }
4116             ret[f.getName()] = v;
4117         });
4118         
4119         return ret;
4120     },
4121
4122     /**
4123      * Clears all invalid messages in this form.
4124      * @return {BasicForm} this
4125      */
4126     clearInvalid : function(){
4127         var items = this.getItems();
4128         
4129         items.each(function(f){
4130            f.clearInvalid();
4131         });
4132         
4133         
4134         
4135         return this;
4136     },
4137
4138     /**
4139      * Resets this form.
4140      * @return {BasicForm} this
4141      */
4142     reset : function(){
4143         var items = this.getItems();
4144         items.each(function(f){
4145             f.reset();
4146         });
4147         
4148         Roo.each(this.childForms || [], function (f) {
4149             f.reset();
4150         });
4151        
4152         
4153         return this;
4154     },
4155     getItems : function()
4156     {
4157         var r=new Roo.util.MixedCollection(false, function(o){
4158             return o.id || (o.id = Roo.id());
4159         });
4160         var iter = function(el) {
4161             if (el.inputEl) {
4162                 r.add(el);
4163             }
4164             if (!el.items) {
4165                 return;
4166             }
4167             Roo.each(el.items,function(e) {
4168                 iter(e);
4169             });
4170             
4171             
4172         };
4173         iter(this);
4174         return r;
4175         
4176         
4177         
4178         
4179     }
4180     
4181 });
4182
4183  
4184 /*
4185  * Based on:
4186  * Ext JS Library 1.1.1
4187  * Copyright(c) 2006-2007, Ext JS, LLC.
4188  *
4189  * Originally Released Under LGPL - original licence link has changed is not relivant.
4190  *
4191  * Fork - LGPL
4192  * <script type="text/javascript">
4193  */
4194 /**
4195  * @class Roo.form.VTypes
4196  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4197  * @singleton
4198  */
4199 Roo.form.VTypes = function(){
4200     // closure these in so they are only created once.
4201     var alpha = /^[a-zA-Z_]+$/;
4202     var alphanum = /^[a-zA-Z0-9_]+$/;
4203     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4204     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4205
4206     // All these messages and functions are configurable
4207     return {
4208         /**
4209          * The function used to validate email addresses
4210          * @param {String} value The email address
4211          */
4212         'email' : function(v){
4213             return email.test(v);
4214         },
4215         /**
4216          * The error text to display when the email validation function returns false
4217          * @type String
4218          */
4219         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4220         /**
4221          * The keystroke filter mask to be applied on email input
4222          * @type RegExp
4223          */
4224         'emailMask' : /[a-z0-9_\.\-@]/i,
4225
4226         /**
4227          * The function used to validate URLs
4228          * @param {String} value The URL
4229          */
4230         'url' : function(v){
4231             return url.test(v);
4232         },
4233         /**
4234          * The error text to display when the url validation function returns false
4235          * @type String
4236          */
4237         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4238         
4239         /**
4240          * The function used to validate alpha values
4241          * @param {String} value The value
4242          */
4243         'alpha' : function(v){
4244             return alpha.test(v);
4245         },
4246         /**
4247          * The error text to display when the alpha validation function returns false
4248          * @type String
4249          */
4250         'alphaText' : 'This field should only contain letters and _',
4251         /**
4252          * The keystroke filter mask to be applied on alpha input
4253          * @type RegExp
4254          */
4255         'alphaMask' : /[a-z_]/i,
4256
4257         /**
4258          * The function used to validate alphanumeric values
4259          * @param {String} value The value
4260          */
4261         'alphanum' : function(v){
4262             return alphanum.test(v);
4263         },
4264         /**
4265          * The error text to display when the alphanumeric validation function returns false
4266          * @type String
4267          */
4268         'alphanumText' : 'This field should only contain letters, numbers and _',
4269         /**
4270          * The keystroke filter mask to be applied on alphanumeric input
4271          * @type RegExp
4272          */
4273         'alphanumMask' : /[a-z0-9_]/i
4274     };
4275 }();/*
4276  * - LGPL
4277  *
4278  * Input
4279  * 
4280  */
4281
4282 /**
4283  * @class Roo.bootstrap.Input
4284  * @extends Roo.bootstrap.Component
4285  * Bootstrap Input class
4286  * @cfg {Boolean} disabled is it disabled
4287  * @cfg {String} fieldLabel - the label associated
4288  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4289  * @cfg {String} name name of the input
4290  * @cfg {string} fieldLabel - the label associated
4291  * @cfg {string}  inputType - input / file submit ...
4292  * @cfg {string} placeholder - placeholder to put in text.
4293  * @cfg {string}  before - input group add on before
4294  * @cfg {string} after - input group add on after
4295  * @cfg {string} size - (lg|sm) or leave empty..
4296  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4297  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4298  * @cfg {Number} md colspan out of 12 for computer-sized screens
4299  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4300  * @cfg {string} value default value of the input
4301  * @cfg {Number} labelWidth set the width of label (0-12)
4302  * @cfg {String} labelAlign (top|left)
4303  * 
4304  * 
4305  * @constructor
4306  * Create a new Input
4307  * @param {Object} config The config object
4308  */
4309
4310 Roo.bootstrap.Input = function(config){
4311     Roo.bootstrap.Input.superclass.constructor.call(this, config);
4312    
4313         this.addEvents({
4314             /**
4315              * @event focus
4316              * Fires when this field receives input focus.
4317              * @param {Roo.form.Field} this
4318              */
4319             focus : true,
4320             /**
4321              * @event blur
4322              * Fires when this field loses input focus.
4323              * @param {Roo.form.Field} this
4324              */
4325             blur : true,
4326             /**
4327              * @event specialkey
4328              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
4329              * {@link Roo.EventObject#getKey} to determine which key was pressed.
4330              * @param {Roo.form.Field} this
4331              * @param {Roo.EventObject} e The event object
4332              */
4333             specialkey : true,
4334             /**
4335              * @event change
4336              * Fires just before the field blurs if the field value has changed.
4337              * @param {Roo.form.Field} this
4338              * @param {Mixed} newValue The new value
4339              * @param {Mixed} oldValue The original value
4340              */
4341             change : true,
4342             /**
4343              * @event invalid
4344              * Fires after the field has been marked as invalid.
4345              * @param {Roo.form.Field} this
4346              * @param {String} msg The validation message
4347              */
4348             invalid : true,
4349             /**
4350              * @event valid
4351              * Fires after the field has been validated with no errors.
4352              * @param {Roo.form.Field} this
4353              */
4354             valid : true,
4355              /**
4356              * @event keyup
4357              * Fires after the key up
4358              * @param {Roo.form.Field} this
4359              * @param {Roo.EventObject}  e The event Object
4360              */
4361             keyup : true
4362         });
4363 };
4364
4365 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
4366      /**
4367      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4368       automatic validation (defaults to "keyup").
4369      */
4370     validationEvent : "keyup",
4371      /**
4372      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4373      */
4374     validateOnBlur : true,
4375     /**
4376      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4377      */
4378     validationDelay : 250,
4379      /**
4380      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4381      */
4382     focusClass : "x-form-focus",  // not needed???
4383     
4384        
4385     /**
4386      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4387      */
4388     invalidClass : "has-error",
4389     
4390     /**
4391      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4392      */
4393     selectOnFocus : false,
4394     
4395      /**
4396      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4397      */
4398     maskRe : null,
4399        /**
4400      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4401      */
4402     vtype : null,
4403     
4404       /**
4405      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4406      */
4407     disableKeyFilter : false,
4408     
4409        /**
4410      * @cfg {Boolean} disabled True to disable the field (defaults to false).
4411      */
4412     disabled : false,
4413      /**
4414      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4415      */
4416     allowBlank : true,
4417     /**
4418      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4419      */
4420     blankText : "This field is required",
4421     
4422      /**
4423      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4424      */
4425     minLength : 0,
4426     /**
4427      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4428      */
4429     maxLength : Number.MAX_VALUE,
4430     /**
4431      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4432      */
4433     minLengthText : "The minimum length for this field is {0}",
4434     /**
4435      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4436      */
4437     maxLengthText : "The maximum length for this field is {0}",
4438   
4439     
4440     /**
4441      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4442      * If available, this function will be called only after the basic validators all return true, and will be passed the
4443      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4444      */
4445     validator : null,
4446     /**
4447      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4448      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4449      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
4450      */
4451     regex : null,
4452     /**
4453      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4454      */
4455     regexText : "",
4456     
4457     
4458     
4459     fieldLabel : '',
4460     inputType : 'text',
4461     
4462     name : false,
4463     placeholder: false,
4464     before : false,
4465     after : false,
4466     size : false,
4467     // private
4468     hasFocus : false,
4469     preventMark: false,
4470     isFormField : true,
4471     value : '',
4472     labelWidth : 2,
4473     labelAlign : false,
4474     
4475     parentLabelAlign : function()
4476     {
4477         var parent = this;
4478         while (parent.parent()) {
4479             parent = parent.parent();
4480             if (typeof(parent.labelAlign) !='undefined') {
4481                 return parent.labelAlign;
4482             }
4483         }
4484         return 'left';
4485         
4486     },
4487     
4488     getAutoCreate : function(){
4489         
4490         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4491         
4492         var id = Roo.id();
4493         
4494         var cfg = {};
4495         
4496         if(this.inputType != 'hidden'){
4497             cfg.cls = 'form-group' //input-group
4498         }
4499         
4500         var input =  {
4501             tag: 'input',
4502             id : id,
4503             type : this.inputType,
4504             value : this.value,
4505             cls : 'form-control',
4506             placeholder : this.placeholder || ''
4507             
4508         };
4509         
4510         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4511             input.maxLength = this.maxLength;
4512         }
4513         
4514         if (this.disabled) {
4515             input.disabled=true;
4516         }
4517         
4518         if (this.name) {
4519             input.name = this.name;
4520         }
4521         if (this.size) {
4522             input.cls += ' input-' + this.size;
4523         }
4524         var settings=this;
4525         ['xs','sm','md','lg'].map(function(size){
4526             if (settings[size]) {
4527                 cfg.cls += ' col-' + size + '-' + settings[size];
4528             }
4529         });
4530         
4531         var inputblock = input;
4532         
4533         if (this.before || this.after) {
4534             
4535             inputblock = {
4536                 cls : 'input-group',
4537                 cn :  [] 
4538             };
4539             if (this.before) {
4540                 inputblock.cn.push({
4541                     tag :'span',
4542                     cls : 'input-group-addon',
4543                     html : this.before
4544                 });
4545             }
4546             inputblock.cn.push(input);
4547             if (this.after) {
4548                 inputblock.cn.push({
4549                     tag :'span',
4550                     cls : 'input-group-addon',
4551                     html : this.after
4552                 });
4553             }
4554             
4555         };
4556         
4557         if (align ==='left' && this.fieldLabel.length) {
4558                 Roo.log("left and has label");
4559                 cfg.cn = [
4560                     
4561                     {
4562                         tag: 'label',
4563                         'for' :  id,
4564                         cls : 'control-label col-sm-' + this.labelWidth,
4565                         html : this.fieldLabel
4566                         
4567                     },
4568                     {
4569                         cls : "col-sm-" + (12 - this.labelWidth), 
4570                         cn: [
4571                             inputblock
4572                         ]
4573                     }
4574                     
4575                 ];
4576         } else if ( this.fieldLabel.length) {
4577                 Roo.log(" label");
4578                  cfg.cn = [
4579                    
4580                     {
4581                         tag: 'label',
4582                         //cls : 'input-group-addon',
4583                         html : this.fieldLabel
4584                         
4585                     },
4586                     
4587                     inputblock
4588                     
4589                 ];
4590
4591         } else {
4592             
4593                 Roo.log(" no label && no align");
4594                 cfg.cn = [
4595                     
4596                         inputblock
4597                     
4598                 ];
4599                 
4600                 
4601         };
4602         Roo.log('input-parentType: ' + this.parentType);
4603         
4604         if (this.parentType === 'Navbar' &&  this.parent().bar) {
4605            cfg.cls += ' navbar-form';
4606            Roo.log(cfg);
4607         }
4608         
4609         return cfg;
4610         
4611     },
4612     /**
4613      * return the real input element.
4614      */
4615     inputEl: function ()
4616     {
4617         return this.el.select('input.form-control',true).first();
4618     },
4619     setDisabled : function(v)
4620     {
4621         var i  = this.inputEl().dom;
4622         if (!v) {
4623             i.removeAttribute('disabled');
4624             return;
4625             
4626         }
4627         i.setAttribute('disabled','true');
4628     },
4629     initEvents : function()
4630     {
4631         
4632         this.inputEl().on("keydown" , this.fireKey,  this);
4633         this.inputEl().on("focus", this.onFocus,  this);
4634         this.inputEl().on("blur", this.onBlur,  this);
4635         
4636         this.inputEl().relayEvent('keyup', this);
4637
4638         // reference to original value for reset
4639         this.originalValue = this.getValue();
4640         //Roo.form.TextField.superclass.initEvents.call(this);
4641         if(this.validationEvent == 'keyup'){
4642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4643             this.inputEl().on('keyup', this.filterValidation, this);
4644         }
4645         else if(this.validationEvent !== false){
4646             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4647         }
4648         
4649         if(this.selectOnFocus){
4650             this.on("focus", this.preFocus, this);
4651             
4652         }
4653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4654             this.inputEl().on("keypress", this.filterKeys, this);
4655         }
4656        /* if(this.grow){
4657             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
4658             this.el.on("click", this.autoSize,  this);
4659         }
4660         */
4661         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4662             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4663         }
4664         
4665     },
4666     filterValidation : function(e){
4667         if(!e.isNavKeyPress()){
4668             this.validationTask.delay(this.validationDelay);
4669         }
4670     },
4671      /**
4672      * Validates the field value
4673      * @return {Boolean} True if the value is valid, else false
4674      */
4675     validate : function(){
4676         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4677         if(this.disabled || this.validateValue(this.getRawValue())){
4678             this.clearInvalid();
4679             return true;
4680         }
4681         return false;
4682     },
4683     
4684     
4685     /**
4686      * Validates a value according to the field's validation rules and marks the field as invalid
4687      * if the validation fails
4688      * @param {Mixed} value The value to validate
4689      * @return {Boolean} True if the value is valid, else false
4690      */
4691     validateValue : function(value){
4692         if(value.length < 1)  { // if it's blank
4693              if(this.allowBlank){
4694                 this.clearInvalid();
4695                 return true;
4696              }else{
4697                 this.markInvalid(this.blankText);
4698                 return false;
4699              }
4700         }
4701         if(value.length < this.minLength){
4702             this.markInvalid(String.format(this.minLengthText, this.minLength));
4703             return false;
4704         }
4705         if(value.length > this.maxLength){
4706             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4707             return false;
4708         }
4709         if(this.vtype){
4710             var vt = Roo.form.VTypes;
4711             if(!vt[this.vtype](value, this)){
4712                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4713                 return false;
4714             }
4715         }
4716         if(typeof this.validator == "function"){
4717             var msg = this.validator(value);
4718             if(msg !== true){
4719                 this.markInvalid(msg);
4720                 return false;
4721             }
4722         }
4723         if(this.regex && !this.regex.test(value)){
4724             this.markInvalid(this.regexText);
4725             return false;
4726         }
4727         return true;
4728     },
4729
4730     
4731     
4732      // private
4733     fireKey : function(e){
4734         //Roo.log('field ' + e.getKey());
4735         if(e.isNavKeyPress()){
4736             this.fireEvent("specialkey", this, e);
4737         }
4738     },
4739     focus : function (selectText){
4740         if(this.rendered){
4741             this.inputEl().focus();
4742             if(selectText === true){
4743                 this.inputEl().dom.select();
4744             }
4745         }
4746         return this;
4747     } ,
4748     
4749     onFocus : function(){
4750         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4751            // this.el.addClass(this.focusClass);
4752         }
4753         if(!this.hasFocus){
4754             this.hasFocus = true;
4755             this.startValue = this.getValue();
4756             this.fireEvent("focus", this);
4757         }
4758     },
4759     
4760     beforeBlur : Roo.emptyFn,
4761
4762     
4763     // private
4764     onBlur : function(){
4765         this.beforeBlur();
4766         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4767             //this.el.removeClass(this.focusClass);
4768         }
4769         this.hasFocus = false;
4770         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4771             this.validate();
4772         }
4773         var v = this.getValue();
4774         if(String(v) !== String(this.startValue)){
4775             this.fireEvent('change', this, v, this.startValue);
4776         }
4777         this.fireEvent("blur", this);
4778     },
4779     
4780     /**
4781      * Resets the current field value to the originally loaded value and clears any validation messages
4782      */
4783     reset : function(){
4784         this.setValue(this.originalValue);
4785         this.clearInvalid();
4786     },
4787      /**
4788      * Returns the name of the field
4789      * @return {Mixed} name The name field
4790      */
4791     getName: function(){
4792         return this.name;
4793     },
4794      /**
4795      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
4796      * @return {Mixed} value The field value
4797      */
4798     getValue : function(){
4799         return this.inputEl().getValue();
4800     },
4801     /**
4802      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
4803      * @return {Mixed} value The field value
4804      */
4805     getRawValue : function(){
4806         var v = this.inputEl().getValue();
4807         
4808         return v;
4809     },
4810     
4811     /**
4812      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
4813      * @param {Mixed} value The value to set
4814      */
4815     setRawValue : function(v){
4816         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4817     },
4818     
4819     selectText : function(start, end){
4820         var v = this.getRawValue();
4821         if(v.length > 0){
4822             start = start === undefined ? 0 : start;
4823             end = end === undefined ? v.length : end;
4824             var d = this.inputEl().dom;
4825             if(d.setSelectionRange){
4826                 d.setSelectionRange(start, end);
4827             }else if(d.createTextRange){
4828                 var range = d.createTextRange();
4829                 range.moveStart("character", start);
4830                 range.moveEnd("character", v.length-end);
4831                 range.select();
4832             }
4833         }
4834     },
4835     
4836     /**
4837      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
4838      * @param {Mixed} value The value to set
4839      */
4840     setValue : function(v){
4841         this.value = v;
4842         if(this.rendered){
4843             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4844             this.validate();
4845         }
4846     },
4847     
4848     /*
4849     processValue : function(value){
4850         if(this.stripCharsRe){
4851             var newValue = value.replace(this.stripCharsRe, '');
4852             if(newValue !== value){
4853                 this.setRawValue(newValue);
4854                 return newValue;
4855             }
4856         }
4857         return value;
4858     },
4859   */
4860     preFocus : function(){
4861         
4862         if(this.selectOnFocus){
4863             this.inputEl().dom.select();
4864         }
4865     },
4866     filterKeys : function(e){
4867         var k = e.getKey();
4868         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4869             return;
4870         }
4871         var c = e.getCharCode(), cc = String.fromCharCode(c);
4872         if(Roo.isIE && (e.isSpecialKey() || !cc)){
4873             return;
4874         }
4875         if(!this.maskRe.test(cc)){
4876             e.stopEvent();
4877         }
4878     },
4879      /**
4880      * Clear any invalid styles/messages for this field
4881      */
4882     clearInvalid : function(){
4883         
4884         if(!this.el || this.preventMark){ // not rendered
4885             return;
4886         }
4887         this.el.removeClass(this.invalidClass);
4888         /*
4889         switch(this.msgTarget){
4890             case 'qtip':
4891                 this.el.dom.qtip = '';
4892                 break;
4893             case 'title':
4894                 this.el.dom.title = '';
4895                 break;
4896             case 'under':
4897                 if(this.errorEl){
4898                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4899                 }
4900                 break;
4901             case 'side':
4902                 if(this.errorIcon){
4903                     this.errorIcon.dom.qtip = '';
4904                     this.errorIcon.hide();
4905                     this.un('resize', this.alignErrorIcon, this);
4906                 }
4907                 break;
4908             default:
4909                 var t = Roo.getDom(this.msgTarget);
4910                 t.innerHTML = '';
4911                 t.style.display = 'none';
4912                 break;
4913         }
4914         */
4915         this.fireEvent('valid', this);
4916     },
4917      /**
4918      * Mark this field as invalid
4919      * @param {String} msg The validation message
4920      */
4921     markInvalid : function(msg){
4922         if(!this.el  || this.preventMark){ // not rendered
4923             return;
4924         }
4925         this.el.addClass(this.invalidClass);
4926         /*
4927         msg = msg || this.invalidText;
4928         switch(this.msgTarget){
4929             case 'qtip':
4930                 this.el.dom.qtip = msg;
4931                 this.el.dom.qclass = 'x-form-invalid-tip';
4932                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4933                     Roo.QuickTips.enable();
4934                 }
4935                 break;
4936             case 'title':
4937                 this.el.dom.title = msg;
4938                 break;
4939             case 'under':
4940                 if(!this.errorEl){
4941                     var elp = this.el.findParent('.x-form-element', 5, true);
4942                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4943                     this.errorEl.setWidth(elp.getWidth(true)-20);
4944                 }
4945                 this.errorEl.update(msg);
4946                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4947                 break;
4948             case 'side':
4949                 if(!this.errorIcon){
4950                     var elp = this.el.findParent('.x-form-element', 5, true);
4951                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4952                 }
4953                 this.alignErrorIcon();
4954                 this.errorIcon.dom.qtip = msg;
4955                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
4956                 this.errorIcon.show();
4957                 this.on('resize', this.alignErrorIcon, this);
4958                 break;
4959             default:
4960                 var t = Roo.getDom(this.msgTarget);
4961                 t.innerHTML = msg;
4962                 t.style.display = this.msgDisplay;
4963                 break;
4964         }
4965         */
4966         this.fireEvent('invalid', this, msg);
4967     },
4968     // private
4969     SafariOnKeyDown : function(event)
4970     {
4971         // this is a workaround for a password hang bug on chrome/ webkit.
4972         
4973         var isSelectAll = false;
4974         
4975         if(this.inputEl().dom.selectionEnd > 0){
4976             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
4977         }
4978         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
4979             event.preventDefault();
4980             this.setValue('');
4981             return;
4982         }
4983         
4984         if(isSelectAll){ // backspace and delete key
4985             
4986             event.preventDefault();
4987             // this is very hacky as keydown always get's upper case.
4988             //
4989             var cc = String.fromCharCode(event.getCharCode());
4990             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
4991             
4992         }
4993     },
4994     adjustWidth : function(tag, w){
4995         tag = tag.toLowerCase();
4996         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
4997             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
4998                 if(tag == 'input'){
4999                     return w + 2;
5000                 }
5001                 if(tag == 'textarea'){
5002                     return w-2;
5003                 }
5004             }else if(Roo.isOpera){
5005                 if(tag == 'input'){
5006                     return w + 2;
5007                 }
5008                 if(tag == 'textarea'){
5009                     return w-2;
5010                 }
5011             }
5012         }
5013         return w;
5014     }
5015     
5016 });
5017
5018  
5019 /*
5020  * - LGPL
5021  *
5022  * Input
5023  * 
5024  */
5025
5026 /**
5027  * @class Roo.bootstrap.TextArea
5028  * @extends Roo.bootstrap.Input
5029  * Bootstrap TextArea class
5030  * @cfg {Number} cols Specifies the visible width of a text area
5031  * @cfg {Number} rows Specifies the visible number of lines in a text area
5032  * @cfg {Number} readOnly Specifies that a text area should be read-only
5033  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5034  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5035  * @cfg {string} html text
5036  * 
5037  * @constructor
5038  * Create a new TextArea
5039  * @param {Object} config The config object
5040  */
5041
5042 Roo.bootstrap.TextArea = function(config){
5043     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5044    
5045 };
5046
5047 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
5048      
5049     cols : false,
5050     rows : 5,
5051     readOnly : false,
5052     warp : 'soft',
5053     resize : false,
5054     value: false,
5055     html: false,
5056     
5057     getAutoCreate : function(){
5058         
5059         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5060         
5061         var id = Roo.id();
5062         
5063         var cfg = {};
5064         
5065         var input =  {
5066             tag: 'textarea',
5067             id : id,
5068             warp : this.warp,
5069             rows : this.rows,
5070             value : this.value || '',
5071             html: this.html || '',
5072             cls : 'form-control',
5073             placeholder : this.placeholder || '' 
5074             
5075         };
5076         
5077         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5078             input.maxLength = this.maxLength;
5079         }
5080         
5081         if(this.resize){
5082             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5083         }
5084         
5085         if(this.cols){
5086             input.cols = this.cols;
5087         }
5088         
5089         if (this.readOnly) {
5090             input.readonly = true;
5091         }
5092         
5093         if (this.name) {
5094             input.name = this.name;
5095         }
5096         
5097         if (this.size) {
5098             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5099         }
5100         
5101         var settings=this;
5102         ['xs','sm','md','lg'].map(function(size){
5103             if (settings[size]) {
5104                 cfg.cls += ' col-' + size + '-' + settings[size];
5105             }
5106         });
5107         
5108         var inputblock = input;
5109         
5110         if (this.before || this.after) {
5111             
5112             inputblock = {
5113                 cls : 'input-group',
5114                 cn :  [] 
5115             };
5116             if (this.before) {
5117                 inputblock.cn.push({
5118                     tag :'span',
5119                     cls : 'input-group-addon',
5120                     html : this.before
5121                 });
5122             }
5123             inputblock.cn.push(input);
5124             if (this.after) {
5125                 inputblock.cn.push({
5126                     tag :'span',
5127                     cls : 'input-group-addon',
5128                     html : this.after
5129                 });
5130             }
5131             
5132         }
5133         
5134         if (align ==='left' && this.fieldLabel.length) {
5135                 Roo.log("left and has label");
5136                 cfg.cn = [
5137                     
5138                     {
5139                         tag: 'label',
5140                         'for' :  id,
5141                         cls : 'control-label col-sm-' + this.labelWidth,
5142                         html : this.fieldLabel
5143                         
5144                     },
5145                     {
5146                         cls : "col-sm-" + (12 - this.labelWidth), 
5147                         cn: [
5148                             inputblock
5149                         ]
5150                     }
5151                     
5152                 ];
5153         } else if ( this.fieldLabel.length) {
5154                 Roo.log(" label");
5155                  cfg.cn = [
5156                    
5157                     {
5158                         tag: 'label',
5159                         //cls : 'input-group-addon',
5160                         html : this.fieldLabel
5161                         
5162                     },
5163                     
5164                     inputblock
5165                     
5166                 ];
5167
5168         } else {
5169             
5170                    Roo.log(" no label && no align");
5171                 cfg.cn = [
5172                     
5173                         inputblock
5174                     
5175                 ];
5176                 
5177                 
5178         }
5179         
5180         if (this.disabled) {
5181             input.disabled=true;
5182         }
5183         
5184         return cfg;
5185         
5186     },
5187     /**
5188      * return the real textarea element.
5189      */
5190     inputEl: function ()
5191     {
5192         return this.el.select('textarea.form-control',true).first();
5193     }
5194 });
5195
5196  
5197 /*
5198  * - LGPL
5199  *
5200  * trigger field - base class for combo..
5201  * 
5202  */
5203  
5204 /**
5205  * @class Roo.bootstrap.TriggerField
5206  * @extends Roo.bootstrap.Input
5207  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5208  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5209  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5210  * for which you can provide a custom implementation.  For example:
5211  * <pre><code>
5212 var trigger = new Roo.bootstrap.TriggerField();
5213 trigger.onTriggerClick = myTriggerFn;
5214 trigger.applyTo('my-field');
5215 </code></pre>
5216  *
5217  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5218  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5219  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
5220  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5221  * @constructor
5222  * Create a new TriggerField.
5223  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5224  * to the base TextField)
5225  */
5226 Roo.bootstrap.TriggerField = function(config){
5227     this.mimicing = false;
5228     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5229 };
5230
5231 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
5232     /**
5233      * @cfg {String} triggerClass A CSS class to apply to the trigger
5234      */
5235      /**
5236      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5237      */
5238     hideTrigger:false,
5239
5240     /** @cfg {Boolean} grow @hide */
5241     /** @cfg {Number} growMin @hide */
5242     /** @cfg {Number} growMax @hide */
5243
5244     /**
5245      * @hide 
5246      * @method
5247      */
5248     autoSize: Roo.emptyFn,
5249     // private
5250     monitorTab : true,
5251     // private
5252     deferHeight : true,
5253
5254     
5255     actionMode : 'wrap',
5256     
5257     
5258     
5259     getAutoCreate : function(){
5260        
5261         var parent = this.parent();
5262         
5263         var align = this.parentLabelAlign();
5264         
5265         var id = Roo.id();
5266         
5267         var cfg = {
5268             cls: 'form-group' //input-group
5269         };
5270         
5271         
5272         var input =  {
5273             tag: 'input',
5274             id : id,
5275             type : this.inputType,
5276             cls : 'form-control',
5277             autocomplete: 'off',
5278             placeholder : this.placeholder || '' 
5279             
5280         };
5281         if (this.name) {
5282             input.name = this.name;
5283         }
5284         if (this.size) {
5285             input.cls += ' input-' + this.size;
5286         }
5287         
5288         if (this.disabled) {
5289             input.disabled=true;
5290         }
5291         
5292         var inputblock = input;
5293         
5294         if (this.before || this.after) {
5295             
5296             inputblock = {
5297                 cls : 'input-group',
5298                 cn :  [] 
5299             };
5300             if (this.before) {
5301                 inputblock.cn.push({
5302                     tag :'span',
5303                     cls : 'input-group-addon',
5304                     html : this.before
5305                 });
5306             }
5307             inputblock.cn.push(input);
5308             if (this.after) {
5309                 inputblock.cn.push({
5310                     tag :'span',
5311                     cls : 'input-group-addon',
5312                     html : this.after
5313                 });
5314             }
5315             
5316         };
5317         
5318         var box = {
5319             tag: 'div',
5320             cn: [
5321                 {
5322                     tag: 'input',
5323                     type : 'hidden',
5324                     cls: 'form-hidden-field'
5325                 },
5326                 inputblock
5327             ]
5328             
5329         };
5330         
5331         if(this.multiple){
5332             Roo.log('multiple');
5333             
5334             box = {
5335                 tag: 'div',
5336                 cn: [
5337                     {
5338                         tag: 'input',
5339                         type : 'hidden',
5340                         cls: 'form-hidden-field'
5341                     },
5342                     {
5343                         tag: 'ul',
5344                         cls: 'select2-choices',
5345                         cn:[
5346                             {
5347                                 tag: 'li',
5348                                 cls: 'select2-search-field',
5349                                 cn: [
5350
5351                                     inputblock
5352                                 ]
5353                             }
5354                         ]
5355                     }
5356                 ]
5357             }
5358         };
5359         
5360         var combobox = {
5361             cls: 'select2-container input-group',
5362             cn: [
5363                 box,
5364                 {
5365                     tag: 'ul',
5366                     cls: 'typeahead typeahead-long dropdown-menu',
5367                     style: 'display:none'
5368                 }
5369             ]
5370         };
5371         
5372         if(!this.multiple){
5373             combobox.cn.push({
5374                 tag :'span',
5375                 cls : 'input-group-addon btn dropdown-toggle',
5376                 cn : [
5377                     {
5378                         tag: 'span',
5379                         cls: 'caret'
5380                     },
5381                     {
5382                         tag: 'span',
5383                         cls: 'combobox-clear',
5384                         cn  : [
5385                             {
5386                                 tag : 'i',
5387                                 cls: 'icon-remove'
5388                             }
5389                         ]
5390                     }
5391                 ]
5392
5393             })
5394         }
5395         
5396         if(this.multiple){
5397             combobox.cls += ' select2-container-multi';
5398         }
5399         
5400         if (align ==='left' && this.fieldLabel.length) {
5401             
5402                 Roo.log("left and has label");
5403                 cfg.cn = [
5404                     
5405                     {
5406                         tag: 'label',
5407                         'for' :  id,
5408                         cls : 'control-label col-sm-' + this.labelWidth,
5409                         html : this.fieldLabel
5410                         
5411                     },
5412                     {
5413                         cls : "col-sm-" + (12 - this.labelWidth), 
5414                         cn: [
5415                             combobox
5416                         ]
5417                     }
5418                     
5419                 ];
5420         } else if ( this.fieldLabel.length) {
5421                 Roo.log(" label");
5422                  cfg.cn = [
5423                    
5424                     {
5425                         tag: 'label',
5426                         //cls : 'input-group-addon',
5427                         html : this.fieldLabel
5428                         
5429                     },
5430                     
5431                     combobox
5432                     
5433                 ];
5434
5435         } else {
5436             
5437                 Roo.log(" no label && no align");
5438                 cfg = combobox
5439                      
5440                 
5441         }
5442          
5443         var settings=this;
5444         ['xs','sm','md','lg'].map(function(size){
5445             if (settings[size]) {
5446                 cfg.cls += ' col-' + size + '-' + settings[size];
5447             }
5448         });
5449         
5450         return cfg;
5451         
5452     },
5453     
5454     
5455     
5456     // private
5457     onResize : function(w, h){
5458 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5459 //        if(typeof w == 'number'){
5460 //            var x = w - this.trigger.getWidth();
5461 //            this.inputEl().setWidth(this.adjustWidth('input', x));
5462 //            this.trigger.setStyle('left', x+'px');
5463 //        }
5464     },
5465
5466     // private
5467     adjustSize : Roo.BoxComponent.prototype.adjustSize,
5468
5469     // private
5470     getResizeEl : function(){
5471         return this.inputEl();
5472     },
5473
5474     // private
5475     getPositionEl : function(){
5476         return this.inputEl();
5477     },
5478
5479     // private
5480     alignErrorIcon : function(){
5481         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5482     },
5483
5484     // private
5485     initEvents : function(){
5486         
5487         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5488         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5489         if(!this.multiple){
5490             this.trigger = this.el.select('span.dropdown-toggle',true).first();
5491             if(this.hideTrigger){
5492                 this.trigger.setDisplayed(false);
5493             }
5494             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5495         }
5496         
5497         if(this.multiple){
5498             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5499         }
5500         
5501         //this.trigger.addClassOnOver('x-form-trigger-over');
5502         //this.trigger.addClassOnClick('x-form-trigger-click');
5503         
5504         //if(!this.width){
5505         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5506         //}
5507     },
5508
5509     // private
5510     initTrigger : function(){
5511        
5512     },
5513
5514     // private
5515     onDestroy : function(){
5516         if(this.trigger){
5517             this.trigger.removeAllListeners();
5518           //  this.trigger.remove();
5519         }
5520         //if(this.wrap){
5521         //    this.wrap.remove();
5522         //}
5523         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5524     },
5525
5526     // private
5527     onFocus : function(){
5528         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5529         /*
5530         if(!this.mimicing){
5531             this.wrap.addClass('x-trigger-wrap-focus');
5532             this.mimicing = true;
5533             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5534             if(this.monitorTab){
5535                 this.el.on("keydown", this.checkTab, this);
5536             }
5537         }
5538         */
5539     },
5540
5541     // private
5542     checkTab : function(e){
5543         if(e.getKey() == e.TAB){
5544             this.triggerBlur();
5545         }
5546     },
5547
5548     // private
5549     onBlur : function(){
5550         // do nothing
5551     },
5552
5553     // private
5554     mimicBlur : function(e, t){
5555         /*
5556         if(!this.wrap.contains(t) && this.validateBlur()){
5557             this.triggerBlur();
5558         }
5559         */
5560     },
5561
5562     // private
5563     triggerBlur : function(){
5564         this.mimicing = false;
5565         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5566         if(this.monitorTab){
5567             this.el.un("keydown", this.checkTab, this);
5568         }
5569         //this.wrap.removeClass('x-trigger-wrap-focus');
5570         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5571     },
5572
5573     // private
5574     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5575     validateBlur : function(e, t){
5576         return true;
5577     },
5578
5579     // private
5580     onDisable : function(){
5581         Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5582         //if(this.wrap){
5583         //    this.wrap.addClass('x-item-disabled');
5584         //}
5585     },
5586
5587     // private
5588     onEnable : function(){
5589         Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5590         //if(this.wrap){
5591         //    this.el.removeClass('x-item-disabled');
5592         //}
5593     },
5594
5595     // private
5596     onShow : function(){
5597         var ae = this.getActionEl();
5598         
5599         if(ae){
5600             ae.dom.style.display = '';
5601             ae.dom.style.visibility = 'visible';
5602         }
5603     },
5604
5605     // private
5606     
5607     onHide : function(){
5608         var ae = this.getActionEl();
5609         ae.dom.style.display = 'none';
5610     },
5611
5612     /**
5613      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
5614      * by an implementing function.
5615      * @method
5616      * @param {EventObject} e
5617      */
5618     onTriggerClick : Roo.emptyFn
5619 });
5620  /*
5621  * Based on:
5622  * Ext JS Library 1.1.1
5623  * Copyright(c) 2006-2007, Ext JS, LLC.
5624  *
5625  * Originally Released Under LGPL - original licence link has changed is not relivant.
5626  *
5627  * Fork - LGPL
5628  * <script type="text/javascript">
5629  */
5630
5631
5632 /**
5633  * @class Roo.data.SortTypes
5634  * @singleton
5635  * Defines the default sorting (casting?) comparison functions used when sorting data.
5636  */
5637 Roo.data.SortTypes = {
5638     /**
5639      * Default sort that does nothing
5640      * @param {Mixed} s The value being converted
5641      * @return {Mixed} The comparison value
5642      */
5643     none : function(s){
5644         return s;
5645     },
5646     
5647     /**
5648      * The regular expression used to strip tags
5649      * @type {RegExp}
5650      * @property
5651      */
5652     stripTagsRE : /<\/?[^>]+>/gi,
5653     
5654     /**
5655      * Strips all HTML tags to sort on text only
5656      * @param {Mixed} s The value being converted
5657      * @return {String} The comparison value
5658      */
5659     asText : function(s){
5660         return String(s).replace(this.stripTagsRE, "");
5661     },
5662     
5663     /**
5664      * Strips all HTML tags to sort on text only - Case insensitive
5665      * @param {Mixed} s The value being converted
5666      * @return {String} The comparison value
5667      */
5668     asUCText : function(s){
5669         return String(s).toUpperCase().replace(this.stripTagsRE, "");
5670     },
5671     
5672     /**
5673      * Case insensitive string
5674      * @param {Mixed} s The value being converted
5675      * @return {String} The comparison value
5676      */
5677     asUCString : function(s) {
5678         return String(s).toUpperCase();
5679     },
5680     
5681     /**
5682      * Date sorting
5683      * @param {Mixed} s The value being converted
5684      * @return {Number} The comparison value
5685      */
5686     asDate : function(s) {
5687         if(!s){
5688             return 0;
5689         }
5690         if(s instanceof Date){
5691             return s.getTime();
5692         }
5693         return Date.parse(String(s));
5694     },
5695     
5696     /**
5697      * Float sorting
5698      * @param {Mixed} s The value being converted
5699      * @return {Float} The comparison value
5700      */
5701     asFloat : function(s) {
5702         var val = parseFloat(String(s).replace(/,/g, ""));
5703         if(isNaN(val)) val = 0;
5704         return val;
5705     },
5706     
5707     /**
5708      * Integer sorting
5709      * @param {Mixed} s The value being converted
5710      * @return {Number} The comparison value
5711      */
5712     asInt : function(s) {
5713         var val = parseInt(String(s).replace(/,/g, ""));
5714         if(isNaN(val)) val = 0;
5715         return val;
5716     }
5717 };/*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727
5728 /**
5729 * @class Roo.data.Record
5730  * Instances of this class encapsulate both record <em>definition</em> information, and record
5731  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5732  * to access Records cached in an {@link Roo.data.Store} object.<br>
5733  * <p>
5734  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5735  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5736  * objects.<br>
5737  * <p>
5738  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5739  * @constructor
5740  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5741  * {@link #create}. The parameters are the same.
5742  * @param {Array} data An associative Array of data values keyed by the field name.
5743  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5744  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5745  * not specified an integer id is generated.
5746  */
5747 Roo.data.Record = function(data, id){
5748     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5749     this.data = data;
5750 };
5751
5752 /**
5753  * Generate a constructor for a specific record layout.
5754  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5755  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5756  * Each field definition object may contain the following properties: <ul>
5757  * <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,
5758  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5759  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5760  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5761  * is being used, then this is a string containing the javascript expression to reference the data relative to 
5762  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5763  * to the data item relative to the record element. If the mapping expression is the same as the field name,
5764  * this may be omitted.</p></li>
5765  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5766  * <ul><li>auto (Default, implies no conversion)</li>
5767  * <li>string</li>
5768  * <li>int</li>
5769  * <li>float</li>
5770  * <li>boolean</li>
5771  * <li>date</li></ul></p></li>
5772  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5773  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5774  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5775  * by the Reader into an object that will be stored in the Record. It is passed the
5776  * following parameters:<ul>
5777  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5778  * </ul></p></li>
5779  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5780  * </ul>
5781  * <br>usage:<br><pre><code>
5782 var TopicRecord = Roo.data.Record.create(
5783     {name: 'title', mapping: 'topic_title'},
5784     {name: 'author', mapping: 'username'},
5785     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5786     {name: 'lastPost', mapping: 'post_time', type: 'date'},
5787     {name: 'lastPoster', mapping: 'user2'},
5788     {name: 'excerpt', mapping: 'post_text'}
5789 );
5790
5791 var myNewRecord = new TopicRecord({
5792     title: 'Do my job please',
5793     author: 'noobie',
5794     totalPosts: 1,
5795     lastPost: new Date(),
5796     lastPoster: 'Animal',
5797     excerpt: 'No way dude!'
5798 });
5799 myStore.add(myNewRecord);
5800 </code></pre>
5801  * @method create
5802  * @static
5803  */
5804 Roo.data.Record.create = function(o){
5805     var f = function(){
5806         f.superclass.constructor.apply(this, arguments);
5807     };
5808     Roo.extend(f, Roo.data.Record);
5809     var p = f.prototype;
5810     p.fields = new Roo.util.MixedCollection(false, function(field){
5811         return field.name;
5812     });
5813     for(var i = 0, len = o.length; i < len; i++){
5814         p.fields.add(new Roo.data.Field(o[i]));
5815     }
5816     f.getField = function(name){
5817         return p.fields.get(name);  
5818     };
5819     return f;
5820 };
5821
5822 Roo.data.Record.AUTO_ID = 1000;
5823 Roo.data.Record.EDIT = 'edit';
5824 Roo.data.Record.REJECT = 'reject';
5825 Roo.data.Record.COMMIT = 'commit';
5826
5827 Roo.data.Record.prototype = {
5828     /**
5829      * Readonly flag - true if this record has been modified.
5830      * @type Boolean
5831      */
5832     dirty : false,
5833     editing : false,
5834     error: null,
5835     modified: null,
5836
5837     // private
5838     join : function(store){
5839         this.store = store;
5840     },
5841
5842     /**
5843      * Set the named field to the specified value.
5844      * @param {String} name The name of the field to set.
5845      * @param {Object} value The value to set the field to.
5846      */
5847     set : function(name, value){
5848         if(this.data[name] == value){
5849             return;
5850         }
5851         this.dirty = true;
5852         if(!this.modified){
5853             this.modified = {};
5854         }
5855         if(typeof this.modified[name] == 'undefined'){
5856             this.modified[name] = this.data[name];
5857         }
5858         this.data[name] = value;
5859         if(!this.editing && this.store){
5860             this.store.afterEdit(this);
5861         }       
5862     },
5863
5864     /**
5865      * Get the value of the named field.
5866      * @param {String} name The name of the field to get the value of.
5867      * @return {Object} The value of the field.
5868      */
5869     get : function(name){
5870         return this.data[name]; 
5871     },
5872
5873     // private
5874     beginEdit : function(){
5875         this.editing = true;
5876         this.modified = {}; 
5877     },
5878
5879     // private
5880     cancelEdit : function(){
5881         this.editing = false;
5882         delete this.modified;
5883     },
5884
5885     // private
5886     endEdit : function(){
5887         this.editing = false;
5888         if(this.dirty && this.store){
5889             this.store.afterEdit(this);
5890         }
5891     },
5892
5893     /**
5894      * Usually called by the {@link Roo.data.Store} which owns the Record.
5895      * Rejects all changes made to the Record since either creation, or the last commit operation.
5896      * Modified fields are reverted to their original values.
5897      * <p>
5898      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5899      * of reject operations.
5900      */
5901     reject : function(){
5902         var m = this.modified;
5903         for(var n in m){
5904             if(typeof m[n] != "function"){
5905                 this.data[n] = m[n];
5906             }
5907         }
5908         this.dirty = false;
5909         delete this.modified;
5910         this.editing = false;
5911         if(this.store){
5912             this.store.afterReject(this);
5913         }
5914     },
5915
5916     /**
5917      * Usually called by the {@link Roo.data.Store} which owns the Record.
5918      * Commits all changes made to the Record since either creation, or the last commit operation.
5919      * <p>
5920      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5921      * of commit operations.
5922      */
5923     commit : function(){
5924         this.dirty = false;
5925         delete this.modified;
5926         this.editing = false;
5927         if(this.store){
5928             this.store.afterCommit(this);
5929         }
5930     },
5931
5932     // private
5933     hasError : function(){
5934         return this.error != null;
5935     },
5936
5937     // private
5938     clearError : function(){
5939         this.error = null;
5940     },
5941
5942     /**
5943      * Creates a copy of this record.
5944      * @param {String} id (optional) A new record id if you don't want to use this record's id
5945      * @return {Record}
5946      */
5947     copy : function(newId) {
5948         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5949     }
5950 };/*
5951  * Based on:
5952  * Ext JS Library 1.1.1
5953  * Copyright(c) 2006-2007, Ext JS, LLC.
5954  *
5955  * Originally Released Under LGPL - original licence link has changed is not relivant.
5956  *
5957  * Fork - LGPL
5958  * <script type="text/javascript">
5959  */
5960
5961
5962
5963 /**
5964  * @class Roo.data.Store
5965  * @extends Roo.util.Observable
5966  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5967  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5968  * <p>
5969  * 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
5970  * has no knowledge of the format of the data returned by the Proxy.<br>
5971  * <p>
5972  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5973  * instances from the data object. These records are cached and made available through accessor functions.
5974  * @constructor
5975  * Creates a new Store.
5976  * @param {Object} config A config object containing the objects needed for the Store to access data,
5977  * and read the data into Records.
5978  */
5979 Roo.data.Store = function(config){
5980     this.data = new Roo.util.MixedCollection(false);
5981     this.data.getKey = function(o){
5982         return o.id;
5983     };
5984     this.baseParams = {};
5985     // private
5986     this.paramNames = {
5987         "start" : "start",
5988         "limit" : "limit",
5989         "sort" : "sort",
5990         "dir" : "dir",
5991         "multisort" : "_multisort"
5992     };
5993
5994     if(config && config.data){
5995         this.inlineData = config.data;
5996         delete config.data;
5997     }
5998
5999     Roo.apply(this, config);
6000     
6001     if(this.reader){ // reader passed
6002         this.reader = Roo.factory(this.reader, Roo.data);
6003         this.reader.xmodule = this.xmodule || false;
6004         if(!this.recordType){
6005             this.recordType = this.reader.recordType;
6006         }
6007         if(this.reader.onMetaChange){
6008             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6009         }
6010     }
6011
6012     if(this.recordType){
6013         this.fields = this.recordType.prototype.fields;
6014     }
6015     this.modified = [];
6016
6017     this.addEvents({
6018         /**
6019          * @event datachanged
6020          * Fires when the data cache has changed, and a widget which is using this Store
6021          * as a Record cache should refresh its view.
6022          * @param {Store} this
6023          */
6024         datachanged : true,
6025         /**
6026          * @event metachange
6027          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6028          * @param {Store} this
6029          * @param {Object} meta The JSON metadata
6030          */
6031         metachange : true,
6032         /**
6033          * @event add
6034          * Fires when Records have been added to the Store
6035          * @param {Store} this
6036          * @param {Roo.data.Record[]} records The array of Records added
6037          * @param {Number} index The index at which the record(s) were added
6038          */
6039         add : true,
6040         /**
6041          * @event remove
6042          * Fires when a Record has been removed from the Store
6043          * @param {Store} this
6044          * @param {Roo.data.Record} record The Record that was removed
6045          * @param {Number} index The index at which the record was removed
6046          */
6047         remove : true,
6048         /**
6049          * @event update
6050          * Fires when a Record has been updated
6051          * @param {Store} this
6052          * @param {Roo.data.Record} record The Record that was updated
6053          * @param {String} operation The update operation being performed.  Value may be one of:
6054          * <pre><code>
6055  Roo.data.Record.EDIT
6056  Roo.data.Record.REJECT
6057  Roo.data.Record.COMMIT
6058          * </code></pre>
6059          */
6060         update : true,
6061         /**
6062          * @event clear
6063          * Fires when the data cache has been cleared.
6064          * @param {Store} this
6065          */
6066         clear : true,
6067         /**
6068          * @event beforeload
6069          * Fires before a request is made for a new data object.  If the beforeload handler returns false
6070          * the load action will be canceled.
6071          * @param {Store} this
6072          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6073          */
6074         beforeload : true,
6075         /**
6076          * @event beforeloadadd
6077          * Fires after a new set of Records has been loaded.
6078          * @param {Store} this
6079          * @param {Roo.data.Record[]} records The Records that were loaded
6080          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6081          */
6082         beforeloadadd : true,
6083         /**
6084          * @event load
6085          * Fires after a new set of Records has been loaded, before they are added to the store.
6086          * @param {Store} this
6087          * @param {Roo.data.Record[]} records The Records that were loaded
6088          * @param {Object} options The loading options that were specified (see {@link #load} for details)
6089          * @params {Object} return from reader
6090          */
6091         load : true,
6092         /**
6093          * @event loadexception
6094          * Fires if an exception occurs in the Proxy during loading.
6095          * Called with the signature of the Proxy's "loadexception" event.
6096          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6097          * 
6098          * @param {Proxy} 
6099          * @param {Object} return from JsonData.reader() - success, totalRecords, records
6100          * @param {Object} load options 
6101          * @param {Object} jsonData from your request (normally this contains the Exception)
6102          */
6103         loadexception : true
6104     });
6105     
6106     if(this.proxy){
6107         this.proxy = Roo.factory(this.proxy, Roo.data);
6108         this.proxy.xmodule = this.xmodule || false;
6109         this.relayEvents(this.proxy,  ["loadexception"]);
6110     }
6111     this.sortToggle = {};
6112     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6113
6114     Roo.data.Store.superclass.constructor.call(this);
6115
6116     if(this.inlineData){
6117         this.loadData(this.inlineData);
6118         delete this.inlineData;
6119     }
6120 };
6121
6122 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6123      /**
6124     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
6125     * without a remote query - used by combo/forms at present.
6126     */
6127     
6128     /**
6129     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6130     */
6131     /**
6132     * @cfg {Array} data Inline data to be loaded when the store is initialized.
6133     */
6134     /**
6135     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6136     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6137     */
6138     /**
6139     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6140     * on any HTTP request
6141     */
6142     /**
6143     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6144     */
6145     /**
6146     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6147     */
6148     multiSort: false,
6149     /**
6150     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6151     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6152     */
6153     remoteSort : false,
6154
6155     /**
6156     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6157      * loaded or when a record is removed. (defaults to false).
6158     */
6159     pruneModifiedRecords : false,
6160
6161     // private
6162     lastOptions : null,
6163
6164     /**
6165      * Add Records to the Store and fires the add event.
6166      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6167      */
6168     add : function(records){
6169         records = [].concat(records);
6170         for(var i = 0, len = records.length; i < len; i++){
6171             records[i].join(this);
6172         }
6173         var index = this.data.length;
6174         this.data.addAll(records);
6175         this.fireEvent("add", this, records, index);
6176     },
6177
6178     /**
6179      * Remove a Record from the Store and fires the remove event.
6180      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6181      */
6182     remove : function(record){
6183         var index = this.data.indexOf(record);
6184         this.data.removeAt(index);
6185         if(this.pruneModifiedRecords){
6186             this.modified.remove(record);
6187         }
6188         this.fireEvent("remove", this, record, index);
6189     },
6190
6191     /**
6192      * Remove all Records from the Store and fires the clear event.
6193      */
6194     removeAll : function(){
6195         this.data.clear();
6196         if(this.pruneModifiedRecords){
6197             this.modified = [];
6198         }
6199         this.fireEvent("clear", this);
6200     },
6201
6202     /**
6203      * Inserts Records to the Store at the given index and fires the add event.
6204      * @param {Number} index The start index at which to insert the passed Records.
6205      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6206      */
6207     insert : function(index, records){
6208         records = [].concat(records);
6209         for(var i = 0, len = records.length; i < len; i++){
6210             this.data.insert(index, records[i]);
6211             records[i].join(this);
6212         }
6213         this.fireEvent("add", this, records, index);
6214     },
6215
6216     /**
6217      * Get the index within the cache of the passed Record.
6218      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6219      * @return {Number} The index of the passed Record. Returns -1 if not found.
6220      */
6221     indexOf : function(record){
6222         return this.data.indexOf(record);
6223     },
6224
6225     /**
6226      * Get the index within the cache of the Record with the passed id.
6227      * @param {String} id The id of the Record to find.
6228      * @return {Number} The index of the Record. Returns -1 if not found.
6229      */
6230     indexOfId : function(id){
6231         return this.data.indexOfKey(id);
6232     },
6233
6234     /**
6235      * Get the Record with the specified id.
6236      * @param {String} id The id of the Record to find.
6237      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6238      */
6239     getById : function(id){
6240         return this.data.key(id);
6241     },
6242
6243     /**
6244      * Get the Record at the specified index.
6245      * @param {Number} index The index of the Record to find.
6246      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6247      */
6248     getAt : function(index){
6249         return this.data.itemAt(index);
6250     },
6251
6252     /**
6253      * Returns a range of Records between specified indices.
6254      * @param {Number} startIndex (optional) The starting index (defaults to 0)
6255      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6256      * @return {Roo.data.Record[]} An array of Records
6257      */
6258     getRange : function(start, end){
6259         return this.data.getRange(start, end);
6260     },
6261
6262     // private
6263     storeOptions : function(o){
6264         o = Roo.apply({}, o);
6265         delete o.callback;
6266         delete o.scope;
6267         this.lastOptions = o;
6268     },
6269
6270     /**
6271      * Loads the Record cache from the configured Proxy using the configured Reader.
6272      * <p>
6273      * If using remote paging, then the first load call must specify the <em>start</em>
6274      * and <em>limit</em> properties in the options.params property to establish the initial
6275      * position within the dataset, and the number of Records to cache on each read from the Proxy.
6276      * <p>
6277      * <strong>It is important to note that for remote data sources, loading is asynchronous,
6278      * and this call will return before the new data has been loaded. Perform any post-processing
6279      * in a callback function, or in a "load" event handler.</strong>
6280      * <p>
6281      * @param {Object} options An object containing properties which control loading options:<ul>
6282      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6283      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6284      * passed the following arguments:<ul>
6285      * <li>r : Roo.data.Record[]</li>
6286      * <li>options: Options object from the load call</li>
6287      * <li>success: Boolean success indicator</li></ul></li>
6288      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6289      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6290      * </ul>
6291      */
6292     load : function(options){
6293         options = options || {};
6294         if(this.fireEvent("beforeload", this, options) !== false){
6295             this.storeOptions(options);
6296             var p = Roo.apply(options.params || {}, this.baseParams);
6297             // if meta was not loaded from remote source.. try requesting it.
6298             if (!this.reader.metaFromRemote) {
6299                 p._requestMeta = 1;
6300             }
6301             if(this.sortInfo && this.remoteSort){
6302                 var pn = this.paramNames;
6303                 p[pn["sort"]] = this.sortInfo.field;
6304                 p[pn["dir"]] = this.sortInfo.direction;
6305             }
6306             if (this.multiSort) {
6307                 var pn = this.paramNames;
6308                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6309             }
6310             
6311             this.proxy.load(p, this.reader, this.loadRecords, this, options);
6312         }
6313     },
6314
6315     /**
6316      * Reloads the Record cache from the configured Proxy using the configured Reader and
6317      * the options from the last load operation performed.
6318      * @param {Object} options (optional) An object containing properties which may override the options
6319      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6320      * the most recently used options are reused).
6321      */
6322     reload : function(options){
6323         this.load(Roo.applyIf(options||{}, this.lastOptions));
6324     },
6325
6326     // private
6327     // Called as a callback by the Reader during a load operation.
6328     loadRecords : function(o, options, success){
6329         if(!o || success === false){
6330             if(success !== false){
6331                 this.fireEvent("load", this, [], options, o);
6332             }
6333             if(options.callback){
6334                 options.callback.call(options.scope || this, [], options, false);
6335             }
6336             return;
6337         }
6338         // if data returned failure - throw an exception.
6339         if (o.success === false) {
6340             // show a message if no listener is registered.
6341             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6342                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6343             }
6344             // loadmask wil be hooked into this..
6345             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6346             return;
6347         }
6348         var r = o.records, t = o.totalRecords || r.length;
6349         
6350         this.fireEvent("beforeloadadd", this, r, options, o);
6351         
6352         if(!options || options.add !== true){
6353             if(this.pruneModifiedRecords){
6354                 this.modified = [];
6355             }
6356             for(var i = 0, len = r.length; i < len; i++){
6357                 r[i].join(this);
6358             }
6359             if(this.snapshot){
6360                 this.data = this.snapshot;
6361                 delete this.snapshot;
6362             }
6363             this.data.clear();
6364             this.data.addAll(r);
6365             this.totalLength = t;
6366             this.applySort();
6367             this.fireEvent("datachanged", this);
6368         }else{
6369             this.totalLength = Math.max(t, this.data.length+r.length);
6370             this.add(r);
6371         }
6372         this.fireEvent("load", this, r, options, o);
6373         if(options.callback){
6374             options.callback.call(options.scope || this, r, options, true);
6375         }
6376     },
6377
6378
6379     /**
6380      * Loads data from a passed data block. A Reader which understands the format of the data
6381      * must have been configured in the constructor.
6382      * @param {Object} data The data block from which to read the Records.  The format of the data expected
6383      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6384      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6385      */
6386     loadData : function(o, append){
6387         var r = this.reader.readRecords(o);
6388         this.loadRecords(r, {add: append}, true);
6389     },
6390
6391     /**
6392      * Gets the number of cached records.
6393      * <p>
6394      * <em>If using paging, this may not be the total size of the dataset. If the data object
6395      * used by the Reader contains the dataset size, then the getTotalCount() function returns
6396      * the data set size</em>
6397      */
6398     getCount : function(){
6399         return this.data.length || 0;
6400     },
6401
6402     /**
6403      * Gets the total number of records in the dataset as returned by the server.
6404      * <p>
6405      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6406      * the dataset size</em>
6407      */
6408     getTotalCount : function(){
6409         return this.totalLength || 0;
6410     },
6411
6412     /**
6413      * Returns the sort state of the Store as an object with two properties:
6414      * <pre><code>
6415  field {String} The name of the field by which the Records are sorted
6416  direction {String} The sort order, "ASC" or "DESC"
6417      * </code></pre>
6418      */
6419     getSortState : function(){
6420         return this.sortInfo;
6421     },
6422
6423     // private
6424     applySort : function(){
6425         if(this.sortInfo && !this.remoteSort){
6426             var s = this.sortInfo, f = s.field;
6427             var st = this.fields.get(f).sortType;
6428             var fn = function(r1, r2){
6429                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6430                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6431             };
6432             this.data.sort(s.direction, fn);
6433             if(this.snapshot && this.snapshot != this.data){
6434                 this.snapshot.sort(s.direction, fn);
6435             }
6436         }
6437     },
6438
6439     /**
6440      * Sets the default sort column and order to be used by the next load operation.
6441      * @param {String} fieldName The name of the field to sort by.
6442      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6443      */
6444     setDefaultSort : function(field, dir){
6445         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6446     },
6447
6448     /**
6449      * Sort the Records.
6450      * If remote sorting is used, the sort is performed on the server, and the cache is
6451      * reloaded. If local sorting is used, the cache is sorted internally.
6452      * @param {String} fieldName The name of the field to sort by.
6453      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6454      */
6455     sort : function(fieldName, dir){
6456         var f = this.fields.get(fieldName);
6457         if(!dir){
6458             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6459             
6460             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6461                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6462             }else{
6463                 dir = f.sortDir;
6464             }
6465         }
6466         this.sortToggle[f.name] = dir;
6467         this.sortInfo = {field: f.name, direction: dir};
6468         if(!this.remoteSort){
6469             this.applySort();
6470             this.fireEvent("datachanged", this);
6471         }else{
6472             this.load(this.lastOptions);
6473         }
6474     },
6475
6476     /**
6477      * Calls the specified function for each of the Records in the cache.
6478      * @param {Function} fn The function to call. The Record is passed as the first parameter.
6479      * Returning <em>false</em> aborts and exits the iteration.
6480      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6481      */
6482     each : function(fn, scope){
6483         this.data.each(fn, scope);
6484     },
6485
6486     /**
6487      * Gets all records modified since the last commit.  Modified records are persisted across load operations
6488      * (e.g., during paging).
6489      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6490      */
6491     getModifiedRecords : function(){
6492         return this.modified;
6493     },
6494
6495     // private
6496     createFilterFn : function(property, value, anyMatch){
6497         if(!value.exec){ // not a regex
6498             value = String(value);
6499             if(value.length == 0){
6500                 return false;
6501             }
6502             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6503         }
6504         return function(r){
6505             return value.test(r.data[property]);
6506         };
6507     },
6508
6509     /**
6510      * Sums the value of <i>property</i> for each record between start and end and returns the result.
6511      * @param {String} property A field on your records
6512      * @param {Number} start The record index to start at (defaults to 0)
6513      * @param {Number} end The last record index to include (defaults to length - 1)
6514      * @return {Number} The sum
6515      */
6516     sum : function(property, start, end){
6517         var rs = this.data.items, v = 0;
6518         start = start || 0;
6519         end = (end || end === 0) ? end : rs.length-1;
6520
6521         for(var i = start; i <= end; i++){
6522             v += (rs[i].data[property] || 0);
6523         }
6524         return v;
6525     },
6526
6527     /**
6528      * Filter the records by a specified property.
6529      * @param {String} field A field on your records
6530      * @param {String/RegExp} value Either a string that the field
6531      * should start with or a RegExp to test against the field
6532      * @param {Boolean} anyMatch True to match any part not just the beginning
6533      */
6534     filter : function(property, value, anyMatch){
6535         var fn = this.createFilterFn(property, value, anyMatch);
6536         return fn ? this.filterBy(fn) : this.clearFilter();
6537     },
6538
6539     /**
6540      * Filter by a function. The specified function will be called with each
6541      * record in this data source. If the function returns true the record is included,
6542      * otherwise it is filtered.
6543      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6544      * @param {Object} scope (optional) The scope of the function (defaults to this)
6545      */
6546     filterBy : function(fn, scope){
6547         this.snapshot = this.snapshot || this.data;
6548         this.data = this.queryBy(fn, scope||this);
6549         this.fireEvent("datachanged", this);
6550     },
6551
6552     /**
6553      * Query the records by a specified property.
6554      * @param {String} field A field on your records
6555      * @param {String/RegExp} value Either a string that the field
6556      * should start with or a RegExp to test against the field
6557      * @param {Boolean} anyMatch True to match any part not just the beginning
6558      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6559      */
6560     query : function(property, value, anyMatch){
6561         var fn = this.createFilterFn(property, value, anyMatch);
6562         return fn ? this.queryBy(fn) : this.data.clone();
6563     },
6564
6565     /**
6566      * Query by a function. The specified function will be called with each
6567      * record in this data source. If the function returns true the record is included
6568      * in the results.
6569      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6570      * @param {Object} scope (optional) The scope of the function (defaults to this)
6571       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6572      **/
6573     queryBy : function(fn, scope){
6574         var data = this.snapshot || this.data;
6575         return data.filterBy(fn, scope||this);
6576     },
6577
6578     /**
6579      * Collects unique values for a particular dataIndex from this store.
6580      * @param {String} dataIndex The property to collect
6581      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6582      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6583      * @return {Array} An array of the unique values
6584      **/
6585     collect : function(dataIndex, allowNull, bypassFilter){
6586         var d = (bypassFilter === true && this.snapshot) ?
6587                 this.snapshot.items : this.data.items;
6588         var v, sv, r = [], l = {};
6589         for(var i = 0, len = d.length; i < len; i++){
6590             v = d[i].data[dataIndex];
6591             sv = String(v);
6592             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6593                 l[sv] = true;
6594                 r[r.length] = v;
6595             }
6596         }
6597         return r;
6598     },
6599
6600     /**
6601      * Revert to a view of the Record cache with no filtering applied.
6602      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6603      */
6604     clearFilter : function(suppressEvent){
6605         if(this.snapshot && this.snapshot != this.data){
6606             this.data = this.snapshot;
6607             delete this.snapshot;
6608             if(suppressEvent !== true){
6609                 this.fireEvent("datachanged", this);
6610             }
6611         }
6612     },
6613
6614     // private
6615     afterEdit : function(record){
6616         if(this.modified.indexOf(record) == -1){
6617             this.modified.push(record);
6618         }
6619         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6620     },
6621     
6622     // private
6623     afterReject : function(record){
6624         this.modified.remove(record);
6625         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6626     },
6627
6628     // private
6629     afterCommit : function(record){
6630         this.modified.remove(record);
6631         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6632     },
6633
6634     /**
6635      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6636      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6637      */
6638     commitChanges : function(){
6639         var m = this.modified.slice(0);
6640         this.modified = [];
6641         for(var i = 0, len = m.length; i < len; i++){
6642             m[i].commit();
6643         }
6644     },
6645
6646     /**
6647      * Cancel outstanding changes on all changed records.
6648      */
6649     rejectChanges : function(){
6650         var m = this.modified.slice(0);
6651         this.modified = [];
6652         for(var i = 0, len = m.length; i < len; i++){
6653             m[i].reject();
6654         }
6655     },
6656
6657     onMetaChange : function(meta, rtype, o){
6658         this.recordType = rtype;
6659         this.fields = rtype.prototype.fields;
6660         delete this.snapshot;
6661         this.sortInfo = meta.sortInfo || this.sortInfo;
6662         this.modified = [];
6663         this.fireEvent('metachange', this, this.reader.meta);
6664     }
6665 });/*
6666  * Based on:
6667  * Ext JS Library 1.1.1
6668  * Copyright(c) 2006-2007, Ext JS, LLC.
6669  *
6670  * Originally Released Under LGPL - original licence link has changed is not relivant.
6671  *
6672  * Fork - LGPL
6673  * <script type="text/javascript">
6674  */
6675
6676 /**
6677  * @class Roo.data.SimpleStore
6678  * @extends Roo.data.Store
6679  * Small helper class to make creating Stores from Array data easier.
6680  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6681  * @cfg {Array} fields An array of field definition objects, or field name strings.
6682  * @cfg {Array} data The multi-dimensional array of data
6683  * @constructor
6684  * @param {Object} config
6685  */
6686 Roo.data.SimpleStore = function(config){
6687     Roo.data.SimpleStore.superclass.constructor.call(this, {
6688         isLocal : true,
6689         reader: new Roo.data.ArrayReader({
6690                 id: config.id
6691             },
6692             Roo.data.Record.create(config.fields)
6693         ),
6694         proxy : new Roo.data.MemoryProxy(config.data)
6695     });
6696     this.load();
6697 };
6698 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6699  * Based on:
6700  * Ext JS Library 1.1.1
6701  * Copyright(c) 2006-2007, Ext JS, LLC.
6702  *
6703  * Originally Released Under LGPL - original licence link has changed is not relivant.
6704  *
6705  * Fork - LGPL
6706  * <script type="text/javascript">
6707  */
6708
6709 /**
6710 /**
6711  * @extends Roo.data.Store
6712  * @class Roo.data.JsonStore
6713  * Small helper class to make creating Stores for JSON data easier. <br/>
6714 <pre><code>
6715 var store = new Roo.data.JsonStore({
6716     url: 'get-images.php',
6717     root: 'images',
6718     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6719 });
6720 </code></pre>
6721  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6722  * JsonReader and HttpProxy (unless inline data is provided).</b>
6723  * @cfg {Array} fields An array of field definition objects, or field name strings.
6724  * @constructor
6725  * @param {Object} config
6726  */
6727 Roo.data.JsonStore = function(c){
6728     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6729         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6730         reader: new Roo.data.JsonReader(c, c.fields)
6731     }));
6732 };
6733 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6734  * Based on:
6735  * Ext JS Library 1.1.1
6736  * Copyright(c) 2006-2007, Ext JS, LLC.
6737  *
6738  * Originally Released Under LGPL - original licence link has changed is not relivant.
6739  *
6740  * Fork - LGPL
6741  * <script type="text/javascript">
6742  */
6743
6744  
6745 Roo.data.Field = function(config){
6746     if(typeof config == "string"){
6747         config = {name: config};
6748     }
6749     Roo.apply(this, config);
6750     
6751     if(!this.type){
6752         this.type = "auto";
6753     }
6754     
6755     var st = Roo.data.SortTypes;
6756     // named sortTypes are supported, here we look them up
6757     if(typeof this.sortType == "string"){
6758         this.sortType = st[this.sortType];
6759     }
6760     
6761     // set default sortType for strings and dates
6762     if(!this.sortType){
6763         switch(this.type){
6764             case "string":
6765                 this.sortType = st.asUCString;
6766                 break;
6767             case "date":
6768                 this.sortType = st.asDate;
6769                 break;
6770             default:
6771                 this.sortType = st.none;
6772         }
6773     }
6774
6775     // define once
6776     var stripRe = /[\$,%]/g;
6777
6778     // prebuilt conversion function for this field, instead of
6779     // switching every time we're reading a value
6780     if(!this.convert){
6781         var cv, dateFormat = this.dateFormat;
6782         switch(this.type){
6783             case "":
6784             case "auto":
6785             case undefined:
6786                 cv = function(v){ return v; };
6787                 break;
6788             case "string":
6789                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6790                 break;
6791             case "int":
6792                 cv = function(v){
6793                     return v !== undefined && v !== null && v !== '' ?
6794                            parseInt(String(v).replace(stripRe, ""), 10) : '';
6795                     };
6796                 break;
6797             case "float":
6798                 cv = function(v){
6799                     return v !== undefined && v !== null && v !== '' ?
6800                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
6801                     };
6802                 break;
6803             case "bool":
6804             case "boolean":
6805                 cv = function(v){ return v === true || v === "true" || v == 1; };
6806                 break;
6807             case "date":
6808                 cv = function(v){
6809                     if(!v){
6810                         return '';
6811                     }
6812                     if(v instanceof Date){
6813                         return v;
6814                     }
6815                     if(dateFormat){
6816                         if(dateFormat == "timestamp"){
6817                             return new Date(v*1000);
6818                         }
6819                         return Date.parseDate(v, dateFormat);
6820                     }
6821                     var parsed = Date.parse(v);
6822                     return parsed ? new Date(parsed) : null;
6823                 };
6824              break;
6825             
6826         }
6827         this.convert = cv;
6828     }
6829 };
6830
6831 Roo.data.Field.prototype = {
6832     dateFormat: null,
6833     defaultValue: "",
6834     mapping: null,
6835     sortType : null,
6836     sortDir : "ASC"
6837 };/*
6838  * Based on:
6839  * Ext JS Library 1.1.1
6840  * Copyright(c) 2006-2007, Ext JS, LLC.
6841  *
6842  * Originally Released Under LGPL - original licence link has changed is not relivant.
6843  *
6844  * Fork - LGPL
6845  * <script type="text/javascript">
6846  */
6847  
6848 // Base class for reading structured data from a data source.  This class is intended to be
6849 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6850
6851 /**
6852  * @class Roo.data.DataReader
6853  * Base class for reading structured data from a data source.  This class is intended to be
6854  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6855  */
6856
6857 Roo.data.DataReader = function(meta, recordType){
6858     
6859     this.meta = meta;
6860     
6861     this.recordType = recordType instanceof Array ? 
6862         Roo.data.Record.create(recordType) : recordType;
6863 };
6864
6865 Roo.data.DataReader.prototype = {
6866      /**
6867      * Create an empty record
6868      * @param {Object} data (optional) - overlay some values
6869      * @return {Roo.data.Record} record created.
6870      */
6871     newRow :  function(d) {
6872         var da =  {};
6873         this.recordType.prototype.fields.each(function(c) {
6874             switch( c.type) {
6875                 case 'int' : da[c.name] = 0; break;
6876                 case 'date' : da[c.name] = new Date(); break;
6877                 case 'float' : da[c.name] = 0.0; break;
6878                 case 'boolean' : da[c.name] = false; break;
6879                 default : da[c.name] = ""; break;
6880             }
6881             
6882         });
6883         return new this.recordType(Roo.apply(da, d));
6884     }
6885     
6886 };/*
6887  * Based on:
6888  * Ext JS Library 1.1.1
6889  * Copyright(c) 2006-2007, Ext JS, LLC.
6890  *
6891  * Originally Released Under LGPL - original licence link has changed is not relivant.
6892  *
6893  * Fork - LGPL
6894  * <script type="text/javascript">
6895  */
6896
6897 /**
6898  * @class Roo.data.DataProxy
6899  * @extends Roo.data.Observable
6900  * This class is an abstract base class for implementations which provide retrieval of
6901  * unformatted data objects.<br>
6902  * <p>
6903  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6904  * (of the appropriate type which knows how to parse the data object) to provide a block of
6905  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6906  * <p>
6907  * Custom implementations must implement the load method as described in
6908  * {@link Roo.data.HttpProxy#load}.
6909  */
6910 Roo.data.DataProxy = function(){
6911     this.addEvents({
6912         /**
6913          * @event beforeload
6914          * Fires before a network request is made to retrieve a data object.
6915          * @param {Object} This DataProxy object.
6916          * @param {Object} params The params parameter to the load function.
6917          */
6918         beforeload : true,
6919         /**
6920          * @event load
6921          * Fires before the load method's callback is called.
6922          * @param {Object} This DataProxy object.
6923          * @param {Object} o The data object.
6924          * @param {Object} arg The callback argument object passed to the load function.
6925          */
6926         load : true,
6927         /**
6928          * @event loadexception
6929          * Fires if an Exception occurs during data retrieval.
6930          * @param {Object} This DataProxy object.
6931          * @param {Object} o The data object.
6932          * @param {Object} arg The callback argument object passed to the load function.
6933          * @param {Object} e The Exception.
6934          */
6935         loadexception : true
6936     });
6937     Roo.data.DataProxy.superclass.constructor.call(this);
6938 };
6939
6940 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6941
6942     /**
6943      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6944      */
6945 /*
6946  * Based on:
6947  * Ext JS Library 1.1.1
6948  * Copyright(c) 2006-2007, Ext JS, LLC.
6949  *
6950  * Originally Released Under LGPL - original licence link has changed is not relivant.
6951  *
6952  * Fork - LGPL
6953  * <script type="text/javascript">
6954  */
6955 /**
6956  * @class Roo.data.MemoryProxy
6957  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6958  * to the Reader when its load method is called.
6959  * @constructor
6960  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6961  */
6962 Roo.data.MemoryProxy = function(data){
6963     if (data.data) {
6964         data = data.data;
6965     }
6966     Roo.data.MemoryProxy.superclass.constructor.call(this);
6967     this.data = data;
6968 };
6969
6970 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6971     /**
6972      * Load data from the requested source (in this case an in-memory
6973      * data object passed to the constructor), read the data object into
6974      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6975      * process that block using the passed callback.
6976      * @param {Object} params This parameter is not used by the MemoryProxy class.
6977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6978      * object into a block of Roo.data.Records.
6979      * @param {Function} callback The function into which to pass the block of Roo.data.records.
6980      * The function must be passed <ul>
6981      * <li>The Record block object</li>
6982      * <li>The "arg" argument from the load function</li>
6983      * <li>A boolean success indicator</li>
6984      * </ul>
6985      * @param {Object} scope The scope in which to call the callback
6986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6987      */
6988     load : function(params, reader, callback, scope, arg){
6989         params = params || {};
6990         var result;
6991         try {
6992             result = reader.readRecords(this.data);
6993         }catch(e){
6994             this.fireEvent("loadexception", this, arg, null, e);
6995             callback.call(scope, null, arg, false);
6996             return;
6997         }
6998         callback.call(scope, result, arg, true);
6999     },
7000     
7001     // private
7002     update : function(params, records){
7003         
7004     }
7005 });/*
7006  * Based on:
7007  * Ext JS Library 1.1.1
7008  * Copyright(c) 2006-2007, Ext JS, LLC.
7009  *
7010  * Originally Released Under LGPL - original licence link has changed is not relivant.
7011  *
7012  * Fork - LGPL
7013  * <script type="text/javascript">
7014  */
7015 /**
7016  * @class Roo.data.HttpProxy
7017  * @extends Roo.data.DataProxy
7018  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7019  * configured to reference a certain URL.<br><br>
7020  * <p>
7021  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7022  * from which the running page was served.<br><br>
7023  * <p>
7024  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7025  * <p>
7026  * Be aware that to enable the browser to parse an XML document, the server must set
7027  * the Content-Type header in the HTTP response to "text/xml".
7028  * @constructor
7029  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7030  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
7031  * will be used to make the request.
7032  */
7033 Roo.data.HttpProxy = function(conn){
7034     Roo.data.HttpProxy.superclass.constructor.call(this);
7035     // is conn a conn config or a real conn?
7036     this.conn = conn;
7037     this.useAjax = !conn || !conn.events;
7038   
7039 };
7040
7041 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7042     // thse are take from connection...
7043     
7044     /**
7045      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7046      */
7047     /**
7048      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7049      * extra parameters to each request made by this object. (defaults to undefined)
7050      */
7051     /**
7052      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7053      *  to each request made by this object. (defaults to undefined)
7054      */
7055     /**
7056      * @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)
7057      */
7058     /**
7059      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7060      */
7061      /**
7062      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7063      * @type Boolean
7064      */
7065   
7066
7067     /**
7068      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7069      * @type Boolean
7070      */
7071     /**
7072      * Return the {@link Roo.data.Connection} object being used by this Proxy.
7073      * @return {Connection} The Connection object. This object may be used to subscribe to events on
7074      * a finer-grained basis than the DataProxy events.
7075      */
7076     getConnection : function(){
7077         return this.useAjax ? Roo.Ajax : this.conn;
7078     },
7079
7080     /**
7081      * Load data from the configured {@link Roo.data.Connection}, read the data object into
7082      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7083      * process that block using the passed callback.
7084      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7085      * for the request to the remote server.
7086      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7087      * object into a block of Roo.data.Records.
7088      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7089      * The function must be passed <ul>
7090      * <li>The Record block object</li>
7091      * <li>The "arg" argument from the load function</li>
7092      * <li>A boolean success indicator</li>
7093      * </ul>
7094      * @param {Object} scope The scope in which to call the callback
7095      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7096      */
7097     load : function(params, reader, callback, scope, arg){
7098         if(this.fireEvent("beforeload", this, params) !== false){
7099             var  o = {
7100                 params : params || {},
7101                 request: {
7102                     callback : callback,
7103                     scope : scope,
7104                     arg : arg
7105                 },
7106                 reader: reader,
7107                 callback : this.loadResponse,
7108                 scope: this
7109             };
7110             if(this.useAjax){
7111                 Roo.applyIf(o, this.conn);
7112                 if(this.activeRequest){
7113                     Roo.Ajax.abort(this.activeRequest);
7114                 }
7115                 this.activeRequest = Roo.Ajax.request(o);
7116             }else{
7117                 this.conn.request(o);
7118             }
7119         }else{
7120             callback.call(scope||this, null, arg, false);
7121         }
7122     },
7123
7124     // private
7125     loadResponse : function(o, success, response){
7126         delete this.activeRequest;
7127         if(!success){
7128             this.fireEvent("loadexception", this, o, response);
7129             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7130             return;
7131         }
7132         var result;
7133         try {
7134             result = o.reader.read(response);
7135         }catch(e){
7136             this.fireEvent("loadexception", this, o, response, e);
7137             o.request.callback.call(o.request.scope, null, o.request.arg, false);
7138             return;
7139         }
7140         
7141         this.fireEvent("load", this, o, o.request.arg);
7142         o.request.callback.call(o.request.scope, result, o.request.arg, true);
7143     },
7144
7145     // private
7146     update : function(dataSet){
7147
7148     },
7149
7150     // private
7151     updateResponse : function(dataSet){
7152
7153     }
7154 });/*
7155  * Based on:
7156  * Ext JS Library 1.1.1
7157  * Copyright(c) 2006-2007, Ext JS, LLC.
7158  *
7159  * Originally Released Under LGPL - original licence link has changed is not relivant.
7160  *
7161  * Fork - LGPL
7162  * <script type="text/javascript">
7163  */
7164
7165 /**
7166  * @class Roo.data.ScriptTagProxy
7167  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7168  * other than the originating domain of the running page.<br><br>
7169  * <p>
7170  * <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
7171  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7172  * <p>
7173  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7174  * source code that is used as the source inside a &lt;script> tag.<br><br>
7175  * <p>
7176  * In order for the browser to process the returned data, the server must wrap the data object
7177  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7178  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7179  * depending on whether the callback name was passed:
7180  * <p>
7181  * <pre><code>
7182 boolean scriptTag = false;
7183 String cb = request.getParameter("callback");
7184 if (cb != null) {
7185     scriptTag = true;
7186     response.setContentType("text/javascript");
7187 } else {
7188     response.setContentType("application/x-json");
7189 }
7190 Writer out = response.getWriter();
7191 if (scriptTag) {
7192     out.write(cb + "(");
7193 }
7194 out.print(dataBlock.toJsonString());
7195 if (scriptTag) {
7196     out.write(");");
7197 }
7198 </pre></code>
7199  *
7200  * @constructor
7201  * @param {Object} config A configuration object.
7202  */
7203 Roo.data.ScriptTagProxy = function(config){
7204     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7205     Roo.apply(this, config);
7206     this.head = document.getElementsByTagName("head")[0];
7207 };
7208
7209 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7210
7211 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7212     /**
7213      * @cfg {String} url The URL from which to request the data object.
7214      */
7215     /**
7216      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7217      */
7218     timeout : 30000,
7219     /**
7220      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7221      * the server the name of the callback function set up by the load call to process the returned data object.
7222      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7223      * javascript output which calls this named function passing the data object as its only parameter.
7224      */
7225     callbackParam : "callback",
7226     /**
7227      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7228      * name to the request.
7229      */
7230     nocache : true,
7231
7232     /**
7233      * Load data from the configured URL, read the data object into
7234      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7235      * process that block using the passed callback.
7236      * @param {Object} params An object containing properties which are to be used as HTTP parameters
7237      * for the request to the remote server.
7238      * @param {Roo.data.DataReader} reader The Reader object which converts the data
7239      * object into a block of Roo.data.Records.
7240      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7241      * The function must be passed <ul>
7242      * <li>The Record block object</li>
7243      * <li>The "arg" argument from the load function</li>
7244      * <li>A boolean success indicator</li>
7245      * </ul>
7246      * @param {Object} scope The scope in which to call the callback
7247      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7248      */
7249     load : function(params, reader, callback, scope, arg){
7250         if(this.fireEvent("beforeload", this, params) !== false){
7251
7252             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7253
7254             var url = this.url;
7255             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7256             if(this.nocache){
7257                 url += "&_dc=" + (new Date().getTime());
7258             }
7259             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7260             var trans = {
7261                 id : transId,
7262                 cb : "stcCallback"+transId,
7263                 scriptId : "stcScript"+transId,
7264                 params : params,
7265                 arg : arg,
7266                 url : url,
7267                 callback : callback,
7268                 scope : scope,
7269                 reader : reader
7270             };
7271             var conn = this;
7272
7273             window[trans.cb] = function(o){
7274                 conn.handleResponse(o, trans);
7275             };
7276
7277             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7278
7279             if(this.autoAbort !== false){
7280                 this.abort();
7281             }
7282
7283             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7284
7285             var script = document.createElement("script");
7286             script.setAttribute("src", url);
7287             script.setAttribute("type", "text/javascript");
7288             script.setAttribute("id", trans.scriptId);
7289             this.head.appendChild(script);
7290
7291             this.trans = trans;
7292         }else{
7293             callback.call(scope||this, null, arg, false);
7294         }
7295     },
7296
7297     // private
7298     isLoading : function(){
7299         return this.trans ? true : false;
7300     },
7301
7302     /**
7303      * Abort the current server request.
7304      */
7305     abort : function(){
7306         if(this.isLoading()){
7307             this.destroyTrans(this.trans);
7308         }
7309     },
7310
7311     // private
7312     destroyTrans : function(trans, isLoaded){
7313         this.head.removeChild(document.getElementById(trans.scriptId));
7314         clearTimeout(trans.timeoutId);
7315         if(isLoaded){
7316             window[trans.cb] = undefined;
7317             try{
7318                 delete window[trans.cb];
7319             }catch(e){}
7320         }else{
7321             // if hasn't been loaded, wait for load to remove it to prevent script error
7322             window[trans.cb] = function(){
7323                 window[trans.cb] = undefined;
7324                 try{
7325                     delete window[trans.cb];
7326                 }catch(e){}
7327             };
7328         }
7329     },
7330
7331     // private
7332     handleResponse : function(o, trans){
7333         this.trans = false;
7334         this.destroyTrans(trans, true);
7335         var result;
7336         try {
7337             result = trans.reader.readRecords(o);
7338         }catch(e){
7339             this.fireEvent("loadexception", this, o, trans.arg, e);
7340             trans.callback.call(trans.scope||window, null, trans.arg, false);
7341             return;
7342         }
7343         this.fireEvent("load", this, o, trans.arg);
7344         trans.callback.call(trans.scope||window, result, trans.arg, true);
7345     },
7346
7347     // private
7348     handleFailure : function(trans){
7349         this.trans = false;
7350         this.destroyTrans(trans, false);
7351         this.fireEvent("loadexception", this, null, trans.arg);
7352         trans.callback.call(trans.scope||window, null, trans.arg, false);
7353     }
7354 });/*
7355  * Based on:
7356  * Ext JS Library 1.1.1
7357  * Copyright(c) 2006-2007, Ext JS, LLC.
7358  *
7359  * Originally Released Under LGPL - original licence link has changed is not relivant.
7360  *
7361  * Fork - LGPL
7362  * <script type="text/javascript">
7363  */
7364
7365 /**
7366  * @class Roo.data.JsonReader
7367  * @extends Roo.data.DataReader
7368  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7369  * based on mappings in a provided Roo.data.Record constructor.
7370  * 
7371  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7372  * in the reply previously. 
7373  * 
7374  * <p>
7375  * Example code:
7376  * <pre><code>
7377 var RecordDef = Roo.data.Record.create([
7378     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
7379     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
7380 ]);
7381 var myReader = new Roo.data.JsonReader({
7382     totalProperty: "results",    // The property which contains the total dataset size (optional)
7383     root: "rows",                // The property which contains an Array of row objects
7384     id: "id"                     // The property within each row object that provides an ID for the record (optional)
7385 }, RecordDef);
7386 </code></pre>
7387  * <p>
7388  * This would consume a JSON file like this:
7389  * <pre><code>
7390 { 'results': 2, 'rows': [
7391     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7392     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7393 }
7394 </code></pre>
7395  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7396  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7397  * paged from the remote server.
7398  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7399  * @cfg {String} root name of the property which contains the Array of row objects.
7400  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7401  * @constructor
7402  * Create a new JsonReader
7403  * @param {Object} meta Metadata configuration options
7404  * @param {Object} recordType Either an Array of field definition objects,
7405  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7406  */
7407 Roo.data.JsonReader = function(meta, recordType){
7408     
7409     meta = meta || {};
7410     // set some defaults:
7411     Roo.applyIf(meta, {
7412         totalProperty: 'total',
7413         successProperty : 'success',
7414         root : 'data',
7415         id : 'id'
7416     });
7417     
7418     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7419 };
7420 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7421     
7422     /**
7423      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
7424      * Used by Store query builder to append _requestMeta to params.
7425      * 
7426      */
7427     metaFromRemote : false,
7428     /**
7429      * This method is only used by a DataProxy which has retrieved data from a remote server.
7430      * @param {Object} response The XHR object which contains the JSON data in its responseText.
7431      * @return {Object} data A data block which is used by an Roo.data.Store object as
7432      * a cache of Roo.data.Records.
7433      */
7434     read : function(response){
7435         var json = response.responseText;
7436        
7437         var o = /* eval:var:o */ eval("("+json+")");
7438         if(!o) {
7439             throw {message: "JsonReader.read: Json object not found"};
7440         }
7441         
7442         if(o.metaData){
7443             
7444             delete this.ef;
7445             this.metaFromRemote = true;
7446             this.meta = o.metaData;
7447             this.recordType = Roo.data.Record.create(o.metaData.fields);
7448             this.onMetaChange(this.meta, this.recordType, o);
7449         }
7450         return this.readRecords(o);
7451     },
7452
7453     // private function a store will implement
7454     onMetaChange : function(meta, recordType, o){
7455
7456     },
7457
7458     /**
7459          * @ignore
7460          */
7461     simpleAccess: function(obj, subsc) {
7462         return obj[subsc];
7463     },
7464
7465         /**
7466          * @ignore
7467          */
7468     getJsonAccessor: function(){
7469         var re = /[\[\.]/;
7470         return function(expr) {
7471             try {
7472                 return(re.test(expr))
7473                     ? new Function("obj", "return obj." + expr)
7474                     : function(obj){
7475                         return obj[expr];
7476                     };
7477             } catch(e){}
7478             return Roo.emptyFn;
7479         };
7480     }(),
7481
7482     /**
7483      * Create a data block containing Roo.data.Records from an XML document.
7484      * @param {Object} o An object which contains an Array of row objects in the property specified
7485      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7486      * which contains the total size of the dataset.
7487      * @return {Object} data A data block which is used by an Roo.data.Store object as
7488      * a cache of Roo.data.Records.
7489      */
7490     readRecords : function(o){
7491         /**
7492          * After any data loads, the raw JSON data is available for further custom processing.
7493          * @type Object
7494          */
7495         this.o = o;
7496         var s = this.meta, Record = this.recordType,
7497             f = Record.prototype.fields, fi = f.items, fl = f.length;
7498
7499 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
7500         if (!this.ef) {
7501             if(s.totalProperty) {
7502                     this.getTotal = this.getJsonAccessor(s.totalProperty);
7503                 }
7504                 if(s.successProperty) {
7505                     this.getSuccess = this.getJsonAccessor(s.successProperty);
7506                 }
7507                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7508                 if (s.id) {
7509                         var g = this.getJsonAccessor(s.id);
7510                         this.getId = function(rec) {
7511                                 var r = g(rec);
7512                                 return (r === undefined || r === "") ? null : r;
7513                         };
7514                 } else {
7515                         this.getId = function(){return null;};
7516                 }
7517             this.ef = [];
7518             for(var jj = 0; jj < fl; jj++){
7519                 f = fi[jj];
7520                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7521                 this.ef[jj] = this.getJsonAccessor(map);
7522             }
7523         }
7524
7525         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7526         if(s.totalProperty){
7527             var vt = parseInt(this.getTotal(o), 10);
7528             if(!isNaN(vt)){
7529                 totalRecords = vt;
7530             }
7531         }
7532         if(s.successProperty){
7533             var vs = this.getSuccess(o);
7534             if(vs === false || vs === 'false'){
7535                 success = false;
7536             }
7537         }
7538         var records = [];
7539             for(var i = 0; i < c; i++){
7540                     var n = root[i];
7541                 var values = {};
7542                 var id = this.getId(n);
7543                 for(var j = 0; j < fl; j++){
7544                     f = fi[j];
7545                 var v = this.ef[j](n);
7546                 if (!f.convert) {
7547                     Roo.log('missing convert for ' + f.name);
7548                     Roo.log(f);
7549                     continue;
7550                 }
7551                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7552                 }
7553                 var record = new Record(values, id);
7554                 record.json = n;
7555                 records[i] = record;
7556             }
7557             return {
7558             raw : o,
7559                 success : success,
7560                 records : records,
7561                 totalRecords : totalRecords
7562             };
7563     }
7564 });/*
7565  * Based on:
7566  * Ext JS Library 1.1.1
7567  * Copyright(c) 2006-2007, Ext JS, LLC.
7568  *
7569  * Originally Released Under LGPL - original licence link has changed is not relivant.
7570  *
7571  * Fork - LGPL
7572  * <script type="text/javascript">
7573  */
7574
7575 /**
7576  * @class Roo.data.ArrayReader
7577  * @extends Roo.data.DataReader
7578  * Data reader class to create an Array of Roo.data.Record objects from an Array.
7579  * Each element of that Array represents a row of data fields. The
7580  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7581  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7582  * <p>
7583  * Example code:.
7584  * <pre><code>
7585 var RecordDef = Roo.data.Record.create([
7586     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
7587     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
7588 ]);
7589 var myReader = new Roo.data.ArrayReader({
7590     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
7591 }, RecordDef);
7592 </code></pre>
7593  * <p>
7594  * This would consume an Array like this:
7595  * <pre><code>
7596 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7597   </code></pre>
7598  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7599  * @constructor
7600  * Create a new JsonReader
7601  * @param {Object} meta Metadata configuration options.
7602  * @param {Object} recordType Either an Array of field definition objects
7603  * as specified to {@link Roo.data.Record#create},
7604  * or an {@link Roo.data.Record} object
7605  * created using {@link Roo.data.Record#create}.
7606  */
7607 Roo.data.ArrayReader = function(meta, recordType){
7608     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7609 };
7610
7611 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7612     /**
7613      * Create a data block containing Roo.data.Records from an XML document.
7614      * @param {Object} o An Array of row objects which represents the dataset.
7615      * @return {Object} data A data block which is used by an Roo.data.Store object as
7616      * a cache of Roo.data.Records.
7617      */
7618     readRecords : function(o){
7619         var sid = this.meta ? this.meta.id : null;
7620         var recordType = this.recordType, fields = recordType.prototype.fields;
7621         var records = [];
7622         var root = o;
7623             for(var i = 0; i < root.length; i++){
7624                     var n = root[i];
7625                 var values = {};
7626                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7627                 for(var j = 0, jlen = fields.length; j < jlen; j++){
7628                 var f = fields.items[j];
7629                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7630                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7631                 v = f.convert(v);
7632                 values[f.name] = v;
7633             }
7634                 var record = new recordType(values, id);
7635                 record.json = n;
7636                 records[records.length] = record;
7637             }
7638             return {
7639                 records : records,
7640                 totalRecords : records.length
7641             };
7642     }
7643 });/*
7644  * - LGPL
7645  * * 
7646  */
7647
7648 /**
7649  * @class Roo.bootstrap.ComboBox
7650  * @extends Roo.bootstrap.TriggerField
7651  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7652  * @cfg {Boolean} append (true|false) default false
7653  * @constructor
7654  * Create a new ComboBox.
7655  * @param {Object} config Configuration options
7656  */
7657 Roo.bootstrap.ComboBox = function(config){
7658     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7659     this.addEvents({
7660         /**
7661          * @event expand
7662          * Fires when the dropdown list is expanded
7663              * @param {Roo.bootstrap.ComboBox} combo This combo box
7664              */
7665         'expand' : true,
7666         /**
7667          * @event collapse
7668          * Fires when the dropdown list is collapsed
7669              * @param {Roo.bootstrap.ComboBox} combo This combo box
7670              */
7671         'collapse' : true,
7672         /**
7673          * @event beforeselect
7674          * Fires before a list item is selected. Return false to cancel the selection.
7675              * @param {Roo.bootstrap.ComboBox} combo This combo box
7676              * @param {Roo.data.Record} record The data record returned from the underlying store
7677              * @param {Number} index The index of the selected item in the dropdown list
7678              */
7679         'beforeselect' : true,
7680         /**
7681          * @event select
7682          * Fires when a list item is selected
7683              * @param {Roo.bootstrap.ComboBox} combo This combo box
7684              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7685              * @param {Number} index The index of the selected item in the dropdown list
7686              */
7687         'select' : true,
7688         /**
7689          * @event beforequery
7690          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7691          * The event object passed has these properties:
7692              * @param {Roo.bootstrap.ComboBox} combo This combo box
7693              * @param {String} query The query
7694              * @param {Boolean} forceAll true to force "all" query
7695              * @param {Boolean} cancel true to cancel the query
7696              * @param {Object} e The query event object
7697              */
7698         'beforequery': true,
7699          /**
7700          * @event add
7701          * Fires when the 'add' icon is pressed (add a listener to enable add button)
7702              * @param {Roo.bootstrap.ComboBox} combo This combo box
7703              */
7704         'add' : true,
7705         /**
7706          * @event edit
7707          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7708              * @param {Roo.bootstrap.ComboBox} combo This combo box
7709              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7710              */
7711         'edit' : true,
7712         /**
7713          * @event remove
7714          * Fires when the remove value from the combobox array
7715              * @param {Roo.bootstrap.ComboBox} combo This combo box
7716              */
7717         'remove' : true
7718         
7719     });
7720     
7721     
7722     this.selectedIndex = -1;
7723     if(this.mode == 'local'){
7724         if(config.queryDelay === undefined){
7725             this.queryDelay = 10;
7726         }
7727         if(config.minChars === undefined){
7728             this.minChars = 0;
7729         }
7730     }
7731 };
7732
7733 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7734      
7735     /**
7736      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7737      * rendering into an Roo.Editor, defaults to false)
7738      */
7739     /**
7740      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7741      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7742      */
7743     /**
7744      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7745      */
7746     /**
7747      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7748      * the dropdown list (defaults to undefined, with no header element)
7749      */
7750
7751      /**
7752      * @cfg {String/Roo.Template} tpl The template to use to render the output
7753      */
7754      
7755      /**
7756      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7757      */
7758     listWidth: undefined,
7759     /**
7760      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7761      * mode = 'remote' or 'text' if mode = 'local')
7762      */
7763     displayField: undefined,
7764     /**
7765      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7766      * mode = 'remote' or 'value' if mode = 'local'). 
7767      * Note: use of a valueField requires the user make a selection
7768      * in order for a value to be mapped.
7769      */
7770     valueField: undefined,
7771     
7772     
7773     /**
7774      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7775      * field's data value (defaults to the underlying DOM element's name)
7776      */
7777     hiddenName: undefined,
7778     /**
7779      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7780      */
7781     listClass: '',
7782     /**
7783      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7784      */
7785     selectedClass: 'active',
7786     
7787     /**
7788      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7789      */
7790     shadow:'sides',
7791     /**
7792      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7793      * anchor positions (defaults to 'tl-bl')
7794      */
7795     listAlign: 'tl-bl?',
7796     /**
7797      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7798      */
7799     maxHeight: 300,
7800     /**
7801      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
7802      * query specified by the allQuery config option (defaults to 'query')
7803      */
7804     triggerAction: 'query',
7805     /**
7806      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7807      * (defaults to 4, does not apply if editable = false)
7808      */
7809     minChars : 4,
7810     /**
7811      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7812      * delay (typeAheadDelay) if it matches a known value (defaults to false)
7813      */
7814     typeAhead: false,
7815     /**
7816      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7817      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7818      */
7819     queryDelay: 500,
7820     /**
7821      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7822      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
7823      */
7824     pageSize: 0,
7825     /**
7826      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
7827      * when editable = true (defaults to false)
7828      */
7829     selectOnFocus:false,
7830     /**
7831      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7832      */
7833     queryParam: 'query',
7834     /**
7835      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
7836      * when mode = 'remote' (defaults to 'Loading...')
7837      */
7838     loadingText: 'Loading...',
7839     /**
7840      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7841      */
7842     resizable: false,
7843     /**
7844      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7845      */
7846     handleHeight : 8,
7847     /**
7848      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7849      * traditional select (defaults to true)
7850      */
7851     editable: true,
7852     /**
7853      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7854      */
7855     allQuery: '',
7856     /**
7857      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7858      */
7859     mode: 'remote',
7860     /**
7861      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7862      * listWidth has a higher value)
7863      */
7864     minListWidth : 70,
7865     /**
7866      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7867      * allow the user to set arbitrary text into the field (defaults to false)
7868      */
7869     forceSelection:false,
7870     /**
7871      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7872      * if typeAhead = true (defaults to 250)
7873      */
7874     typeAheadDelay : 250,
7875     /**
7876      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7877      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7878      */
7879     valueNotFoundText : undefined,
7880     /**
7881      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7882      */
7883     blockFocus : false,
7884     
7885     /**
7886      * @cfg {Boolean} disableClear Disable showing of clear button.
7887      */
7888     disableClear : false,
7889     /**
7890      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
7891      */
7892     alwaysQuery : false,
7893     
7894     /**
7895      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
7896      */
7897     multiple : false,
7898     
7899     //private
7900     addicon : false,
7901     editicon: false,
7902     
7903     page: 0,
7904     hasQuery: false,
7905     append: false,
7906     loadNext: false,
7907     item: [],
7908     
7909     // element that contains real text value.. (when hidden is used..)
7910      
7911     // private
7912     initEvents: function(){
7913         
7914         if (!this.store) {
7915             throw "can not find store for combo";
7916         }
7917         this.store = Roo.factory(this.store, Roo.data);
7918         
7919         
7920         
7921         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7922         
7923         
7924         if(this.hiddenName){
7925             
7926             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7927             
7928             this.hiddenField.dom.value =
7929                 this.hiddenValue !== undefined ? this.hiddenValue :
7930                 this.value !== undefined ? this.value : '';
7931
7932             // prevent input submission
7933             this.el.dom.removeAttribute('name');
7934             this.hiddenField.dom.setAttribute('name', this.hiddenName);
7935              
7936              
7937         }
7938         //if(Roo.isGecko){
7939         //    this.el.dom.setAttribute('autocomplete', 'off');
7940         //}
7941
7942         var cls = 'x-combo-list';
7943         this.list = this.el.select('ul.dropdown-menu',true).first();
7944
7945         //this.list = new Roo.Layer({
7946         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7947         //});
7948         
7949         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7950         this.list.setWidth(lw);
7951         
7952         this.list.on('mouseover', this.onViewOver, this);
7953         this.list.on('mousemove', this.onViewMove, this);
7954         
7955         this.list.on('scroll', this.onViewScroll, this);
7956         
7957         /*
7958         this.list.swallowEvent('mousewheel');
7959         this.assetHeight = 0;
7960
7961         if(this.title){
7962             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7963             this.assetHeight += this.header.getHeight();
7964         }
7965
7966         this.innerList = this.list.createChild({cls:cls+'-inner'});
7967         this.innerList.on('mouseover', this.onViewOver, this);
7968         this.innerList.on('mousemove', this.onViewMove, this);
7969         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7970         
7971         if(this.allowBlank && !this.pageSize && !this.disableClear){
7972             this.footer = this.list.createChild({cls:cls+'-ft'});
7973             this.pageTb = new Roo.Toolbar(this.footer);
7974            
7975         }
7976         if(this.pageSize){
7977             this.footer = this.list.createChild({cls:cls+'-ft'});
7978             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7979                     {pageSize: this.pageSize});
7980             
7981         }
7982         
7983         if (this.pageTb && this.allowBlank && !this.disableClear) {
7984             var _this = this;
7985             this.pageTb.add(new Roo.Toolbar.Fill(), {
7986                 cls: 'x-btn-icon x-btn-clear',
7987                 text: '&#160;',
7988                 handler: function()
7989                 {
7990                     _this.collapse();
7991                     _this.clearValue();
7992                     _this.onSelect(false, -1);
7993                 }
7994             });
7995         }
7996         if (this.footer) {
7997             this.assetHeight += this.footer.getHeight();
7998         }
7999         */
8000             
8001         if(!this.tpl){
8002             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8003         }
8004
8005         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8006             singleSelect:true, store: this.store, selectedClass: this.selectedClass
8007         });
8008         //this.view.wrapEl.setDisplayed(false);
8009         this.view.on('click', this.onViewClick, this);
8010         
8011         
8012         
8013         this.store.on('beforeload', this.onBeforeLoad, this);
8014         this.store.on('load', this.onLoad, this);
8015         this.store.on('loadexception', this.onLoadException, this);
8016         /*
8017         if(this.resizable){
8018             this.resizer = new Roo.Resizable(this.list,  {
8019                pinned:true, handles:'se'
8020             });
8021             this.resizer.on('resize', function(r, w, h){
8022                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8023                 this.listWidth = w;
8024                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8025                 this.restrictHeight();
8026             }, this);
8027             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8028         }
8029         */
8030         if(!this.editable){
8031             this.editable = true;
8032             this.setEditable(false);
8033         }
8034         
8035         /*
8036         
8037         if (typeof(this.events.add.listeners) != 'undefined') {
8038             
8039             this.addicon = this.wrap.createChild(
8040                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
8041        
8042             this.addicon.on('click', function(e) {
8043                 this.fireEvent('add', this);
8044             }, this);
8045         }
8046         if (typeof(this.events.edit.listeners) != 'undefined') {
8047             
8048             this.editicon = this.wrap.createChild(
8049                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
8050             if (this.addicon) {
8051                 this.editicon.setStyle('margin-left', '40px');
8052             }
8053             this.editicon.on('click', function(e) {
8054                 
8055                 // we fire even  if inothing is selected..
8056                 this.fireEvent('edit', this, this.lastData );
8057                 
8058             }, this);
8059         }
8060         */
8061         
8062         this.keyNav = new Roo.KeyNav(this.inputEl(), {
8063             "up" : function(e){
8064                 this.inKeyMode = true;
8065                 this.selectPrev();
8066             },
8067
8068             "down" : function(e){
8069                 if(!this.isExpanded()){
8070                     this.onTriggerClick();
8071                 }else{
8072                     this.inKeyMode = true;
8073                     this.selectNext();
8074                 }
8075             },
8076
8077             "enter" : function(e){
8078                 this.onViewClick();
8079                 //return true;
8080             },
8081
8082             "esc" : function(e){
8083                 this.collapse();
8084             },
8085
8086             "tab" : function(e){
8087                 this.collapse();
8088                 
8089                 if(this.fireEvent("specialkey", this, e)){
8090                     this.onViewClick(false);
8091                 }
8092                 
8093                 return true;
8094             },
8095
8096             scope : this,
8097
8098             doRelay : function(foo, bar, hname){
8099                 if(hname == 'down' || this.scope.isExpanded()){
8100                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8101                 }
8102                 return true;
8103             },
8104
8105             forceKeyDown: true
8106         });
8107         
8108         
8109         this.queryDelay = Math.max(this.queryDelay || 10,
8110                 this.mode == 'local' ? 10 : 250);
8111         
8112         
8113         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8114         
8115         if(this.typeAhead){
8116             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8117         }
8118         if(this.editable !== false){
8119             this.inputEl().on("keyup", this.onKeyUp, this);
8120         }
8121         if(this.forceSelection){
8122             this.on('blur', this.doForce, this);
8123         }
8124         
8125         if(this.multiple){
8126             this.choices = this.el.select('ul.select2-choices', true).first();
8127             this.searchField = this.el.select('ul li.select2-search-field', true).first();
8128         }
8129     },
8130
8131     onDestroy : function(){
8132         if(this.view){
8133             this.view.setStore(null);
8134             this.view.el.removeAllListeners();
8135             this.view.el.remove();
8136             this.view.purgeListeners();
8137         }
8138         if(this.list){
8139             this.list.dom.innerHTML  = '';
8140         }
8141         if(this.store){
8142             this.store.un('beforeload', this.onBeforeLoad, this);
8143             this.store.un('load', this.onLoad, this);
8144             this.store.un('loadexception', this.onLoadException, this);
8145         }
8146         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8147     },
8148
8149     // private
8150     fireKey : function(e){
8151         if(e.isNavKeyPress() && !this.list.isVisible()){
8152             this.fireEvent("specialkey", this, e);
8153         }
8154     },
8155
8156     // private
8157     onResize: function(w, h){
8158 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8159 //        
8160 //        if(typeof w != 'number'){
8161 //            // we do not handle it!?!?
8162 //            return;
8163 //        }
8164 //        var tw = this.trigger.getWidth();
8165 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
8166 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
8167 //        var x = w - tw;
8168 //        this.inputEl().setWidth( this.adjustWidth('input', x));
8169 //            
8170 //        //this.trigger.setStyle('left', x+'px');
8171 //        
8172 //        if(this.list && this.listWidth === undefined){
8173 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8174 //            this.list.setWidth(lw);
8175 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8176 //        }
8177         
8178     
8179         
8180     },
8181
8182     /**
8183      * Allow or prevent the user from directly editing the field text.  If false is passed,
8184      * the user will only be able to select from the items defined in the dropdown list.  This method
8185      * is the runtime equivalent of setting the 'editable' config option at config time.
8186      * @param {Boolean} value True to allow the user to directly edit the field text
8187      */
8188     setEditable : function(value){
8189         if(value == this.editable){
8190             return;
8191         }
8192         this.editable = value;
8193         if(!value){
8194             this.inputEl().dom.setAttribute('readOnly', true);
8195             this.inputEl().on('mousedown', this.onTriggerClick,  this);
8196             this.inputEl().addClass('x-combo-noedit');
8197         }else{
8198             this.inputEl().dom.setAttribute('readOnly', false);
8199             this.inputEl().un('mousedown', this.onTriggerClick,  this);
8200             this.inputEl().removeClass('x-combo-noedit');
8201         }
8202     },
8203
8204     // private
8205     
8206     onBeforeLoad : function(combo,opts){
8207         if(!this.hasFocus){
8208             return;
8209         }
8210          if (!opts.add) {
8211             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8212          }
8213         this.restrictHeight();
8214         this.selectedIndex = -1;
8215     },
8216
8217     // private
8218     onLoad : function(){
8219         
8220         this.hasQuery = false;
8221         
8222         if(!this.hasFocus){
8223             return;
8224         }
8225         
8226         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8227             this.loading.hide();
8228         }
8229         
8230         if(this.store.getCount() > 0){
8231             this.expand();
8232             this.restrictHeight();
8233             if(this.lastQuery == this.allQuery){
8234                 if(this.editable){
8235                     this.inputEl().dom.select();
8236                 }
8237                 if(!this.selectByValue(this.value, true)){
8238                     this.select(0, true);
8239                 }
8240             }else{
8241                 this.selectNext();
8242                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8243                     this.taTask.delay(this.typeAheadDelay);
8244                 }
8245             }
8246         }else{
8247             this.onEmptyResults();
8248         }
8249         
8250         //this.el.focus();
8251     },
8252     // private
8253     onLoadException : function()
8254     {
8255         this.hasQuery = false;
8256         
8257         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8258             this.loading.hide();
8259         }
8260         
8261         this.collapse();
8262         Roo.log(this.store.reader.jsonData);
8263         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8264             // fixme
8265             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8266         }
8267         
8268         
8269     },
8270     // private
8271     onTypeAhead : function(){
8272         if(this.store.getCount() > 0){
8273             var r = this.store.getAt(0);
8274             var newValue = r.data[this.displayField];
8275             var len = newValue.length;
8276             var selStart = this.getRawValue().length;
8277             
8278             if(selStart != len){
8279                 this.setRawValue(newValue);
8280                 this.selectText(selStart, newValue.length);
8281             }
8282         }
8283     },
8284
8285     // private
8286     onSelect : function(record, index){
8287         
8288         if(this.fireEvent('beforeselect', this, record, index) !== false){
8289         
8290             this.setFromData(index > -1 ? record.data : false);
8291             
8292             this.collapse();
8293             this.fireEvent('select', this, record, index);
8294         }
8295     },
8296
8297     /**
8298      * Returns the currently selected field value or empty string if no value is set.
8299      * @return {String} value The selected value
8300      */
8301     getValue : function(){
8302         
8303         if(this.multiple){
8304             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8305         }
8306         
8307         if(this.valueField){
8308             return typeof this.value != 'undefined' ? this.value : '';
8309         }else{
8310             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8311         }
8312     },
8313
8314     /**
8315      * Clears any text/value currently set in the field
8316      */
8317     clearValue : function(){
8318         if(this.hiddenField){
8319             this.hiddenField.dom.value = '';
8320         }
8321         this.value = '';
8322         this.setRawValue('');
8323         this.lastSelectionText = '';
8324         
8325     },
8326
8327     /**
8328      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
8329      * will be displayed in the field.  If the value does not match the data value of an existing item,
8330      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8331      * Otherwise the field will be blank (although the value will still be set).
8332      * @param {String} value The value to match
8333      */
8334     setValue : function(v){
8335         if(this.multiple){
8336             this.syncValue();
8337             return;
8338         }
8339         
8340         var text = v;
8341         if(this.valueField){
8342             var r = this.findRecord(this.valueField, v);
8343             if(r){
8344                 text = r.data[this.displayField];
8345             }else if(this.valueNotFoundText !== undefined){
8346                 text = this.valueNotFoundText;
8347             }
8348         }
8349         this.lastSelectionText = text;
8350         if(this.hiddenField){
8351             this.hiddenField.dom.value = v;
8352         }
8353         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8354         this.value = v;
8355     },
8356     /**
8357      * @property {Object} the last set data for the element
8358      */
8359     
8360     lastData : false,
8361     /**
8362      * Sets the value of the field based on a object which is related to the record format for the store.
8363      * @param {Object} value the value to set as. or false on reset?
8364      */
8365     setFromData : function(o){
8366         
8367         if(this.multiple){
8368             this.addItem(o);
8369             return;
8370         }
8371             
8372         var dv = ''; // display value
8373         var vv = ''; // value value..
8374         this.lastData = o;
8375         if (this.displayField) {
8376             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8377         } else {
8378             // this is an error condition!!!
8379             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8380         }
8381         
8382         if(this.valueField){
8383             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8384         }
8385         
8386         if(this.hiddenField){
8387             this.hiddenField.dom.value = vv;
8388             
8389             this.lastSelectionText = dv;
8390             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8391             this.value = vv;
8392             return;
8393         }
8394         // no hidden field.. - we store the value in 'value', but still display
8395         // display field!!!!
8396         this.lastSelectionText = dv;
8397         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8398         this.value = vv;
8399         
8400         
8401     },
8402     // private
8403     reset : function(){
8404         // overridden so that last data is reset..
8405         this.setValue(this.originalValue);
8406         this.clearInvalid();
8407         this.lastData = false;
8408         if (this.view) {
8409             this.view.clearSelections();
8410         }
8411     },
8412     // private
8413     findRecord : function(prop, value){
8414         var record;
8415         if(this.store.getCount() > 0){
8416             this.store.each(function(r){
8417                 if(r.data[prop] == value){
8418                     record = r;
8419                     return false;
8420                 }
8421                 return true;
8422             });
8423         }
8424         return record;
8425     },
8426     
8427     getName: function()
8428     {
8429         // returns hidden if it's set..
8430         if (!this.rendered) {return ''};
8431         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
8432         
8433     },
8434     // private
8435     onViewMove : function(e, t){
8436         this.inKeyMode = false;
8437     },
8438
8439     // private
8440     onViewOver : function(e, t){
8441         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8442             return;
8443         }
8444         var item = this.view.findItemFromChild(t);
8445         if(item){
8446             var index = this.view.indexOf(item);
8447             this.select(index, false);
8448         }
8449     },
8450
8451     // private
8452     onViewClick : function(doFocus)
8453     {
8454         var index = this.view.getSelectedIndexes()[0];
8455         var r = this.store.getAt(index);
8456         if(r){
8457             this.onSelect(r, index);
8458         }
8459         if(doFocus !== false && !this.blockFocus){
8460             this.inputEl().focus();
8461         }
8462     },
8463
8464     // private
8465     restrictHeight : function(){
8466         //this.innerList.dom.style.height = '';
8467         //var inner = this.innerList.dom;
8468         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8469         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8470         //this.list.beginUpdate();
8471         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8472         this.list.alignTo(this.inputEl(), this.listAlign);
8473         //this.list.endUpdate();
8474     },
8475
8476     // private
8477     onEmptyResults : function(){
8478         this.collapse();
8479     },
8480
8481     /**
8482      * Returns true if the dropdown list is expanded, else false.
8483      */
8484     isExpanded : function(){
8485         return this.list.isVisible();
8486     },
8487
8488     /**
8489      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8490      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8491      * @param {String} value The data value of the item to select
8492      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8493      * selected item if it is not currently in view (defaults to true)
8494      * @return {Boolean} True if the value matched an item in the list, else false
8495      */
8496     selectByValue : function(v, scrollIntoView){
8497         if(v !== undefined && v !== null){
8498             var r = this.findRecord(this.valueField || this.displayField, v);
8499             if(r){
8500                 this.select(this.store.indexOf(r), scrollIntoView);
8501                 return true;
8502             }
8503         }
8504         return false;
8505     },
8506
8507     /**
8508      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8509      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8510      * @param {Number} index The zero-based index of the list item to select
8511      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8512      * selected item if it is not currently in view (defaults to true)
8513      */
8514     select : function(index, scrollIntoView){
8515         this.selectedIndex = index;
8516         this.view.select(index);
8517         if(scrollIntoView !== false){
8518             var el = this.view.getNode(index);
8519             if(el){
8520                 //this.innerList.scrollChildIntoView(el, false);
8521                 
8522             }
8523         }
8524     },
8525
8526     // private
8527     selectNext : function(){
8528         var ct = this.store.getCount();
8529         if(ct > 0){
8530             if(this.selectedIndex == -1){
8531                 this.select(0);
8532             }else if(this.selectedIndex < ct-1){
8533                 this.select(this.selectedIndex+1);
8534             }
8535         }
8536     },
8537
8538     // private
8539     selectPrev : function(){
8540         var ct = this.store.getCount();
8541         if(ct > 0){
8542             if(this.selectedIndex == -1){
8543                 this.select(0);
8544             }else if(this.selectedIndex != 0){
8545                 this.select(this.selectedIndex-1);
8546             }
8547         }
8548     },
8549
8550     // private
8551     onKeyUp : function(e){
8552         if(this.editable !== false && !e.isSpecialKey()){
8553             this.lastKey = e.getKey();
8554             this.dqTask.delay(this.queryDelay);
8555         }
8556     },
8557
8558     // private
8559     validateBlur : function(){
8560         return !this.list || !this.list.isVisible();   
8561     },
8562
8563     // private
8564     initQuery : function(){
8565         this.doQuery(this.getRawValue());
8566     },
8567
8568     // private
8569     doForce : function(){
8570         if(this.el.dom.value.length > 0){
8571             this.el.dom.value =
8572                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8573              
8574         }
8575     },
8576
8577     /**
8578      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
8579      * query allowing the query action to be canceled if needed.
8580      * @param {String} query The SQL query to execute
8581      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8582      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
8583      * saved in the current store (defaults to false)
8584      */
8585     doQuery : function(q, forceAll){
8586         
8587         if(q === undefined || q === null){
8588             q = '';
8589         }
8590         var qe = {
8591             query: q,
8592             forceAll: forceAll,
8593             combo: this,
8594             cancel:false
8595         };
8596         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8597             return false;
8598         }
8599         q = qe.query;
8600         
8601         forceAll = qe.forceAll;
8602         if(forceAll === true || (q.length >= this.minChars)){
8603             
8604             this.hasQuery = true;
8605             
8606             if(this.lastQuery != q || this.alwaysQuery){
8607                 this.lastQuery = q;
8608                 if(this.mode == 'local'){
8609                     this.selectedIndex = -1;
8610                     if(forceAll){
8611                         this.store.clearFilter();
8612                     }else{
8613                         this.store.filter(this.displayField, q);
8614                     }
8615                     this.onLoad();
8616                 }else{
8617                     this.store.baseParams[this.queryParam] = q;
8618                     
8619                     var options = {params : this.getParams(q)};
8620                     
8621                     if(this.loadNext){
8622                         options.add = true;
8623                         options.params.start = this.page * this.pageSize;
8624                     }
8625                     
8626                     this.store.load(options);
8627                     this.expand();
8628                 }
8629             }else{
8630                 this.selectedIndex = -1;
8631                 this.onLoad();   
8632             }
8633         }
8634         
8635         this.loadNext = false;
8636     },
8637
8638     // private
8639     getParams : function(q){
8640         var p = {};
8641         //p[this.queryParam] = q;
8642         
8643         if(this.pageSize){
8644             p.start = 0;
8645             p.limit = this.pageSize;
8646         }
8647         return p;
8648     },
8649
8650     /**
8651      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8652      */
8653     collapse : function(){
8654         if(!this.isExpanded()){
8655             return;
8656         }
8657         
8658         this.list.hide();
8659         Roo.get(document).un('mousedown', this.collapseIf, this);
8660         Roo.get(document).un('mousewheel', this.collapseIf, this);
8661         if (!this.editable) {
8662             Roo.get(document).un('keydown', this.listKeyPress, this);
8663         }
8664         this.fireEvent('collapse', this);
8665     },
8666
8667     // private
8668     collapseIf : function(e){
8669         var in_combo  = e.within(this.el);
8670         var in_list =  e.within(this.list);
8671         
8672         if (in_combo || in_list) {
8673             //e.stopPropagation();
8674             return;
8675         }
8676
8677         this.collapse();
8678         
8679     },
8680
8681     /**
8682      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8683      */
8684     expand : function(){
8685        
8686         if(this.isExpanded() || !this.hasFocus){
8687             return;
8688         }
8689          Roo.log('expand');
8690         this.list.alignTo(this.inputEl(), this.listAlign);
8691         this.list.show();
8692         Roo.get(document).on('mousedown', this.collapseIf, this);
8693         Roo.get(document).on('mousewheel', this.collapseIf, this);
8694         if (!this.editable) {
8695             Roo.get(document).on('keydown', this.listKeyPress, this);
8696         }
8697         
8698         this.fireEvent('expand', this);
8699     },
8700
8701     // private
8702     // Implements the default empty TriggerField.onTriggerClick function
8703     onTriggerClick : function()
8704     {
8705         Roo.log('trigger click');
8706         
8707         if(this.disabled){
8708             return;
8709         }
8710         
8711         this.page = 0;
8712         this.loadNext = false;
8713         
8714         if(this.isExpanded()){
8715             this.collapse();
8716             if (!this.blockFocus) {
8717                 this.inputEl().focus();
8718             }
8719             
8720         }else {
8721             this.hasFocus = true;
8722             if(this.triggerAction == 'all') {
8723                 this.doQuery(this.allQuery, true);
8724             } else {
8725                 this.doQuery(this.getRawValue());
8726             }
8727             if (!this.blockFocus) {
8728                 this.inputEl().focus();
8729             }
8730         }
8731     },
8732     listKeyPress : function(e)
8733     {
8734         //Roo.log('listkeypress');
8735         // scroll to first matching element based on key pres..
8736         if (e.isSpecialKey()) {
8737             return false;
8738         }
8739         var k = String.fromCharCode(e.getKey()).toUpperCase();
8740         //Roo.log(k);
8741         var match  = false;
8742         var csel = this.view.getSelectedNodes();
8743         var cselitem = false;
8744         if (csel.length) {
8745             var ix = this.view.indexOf(csel[0]);
8746             cselitem  = this.store.getAt(ix);
8747             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8748                 cselitem = false;
8749             }
8750             
8751         }
8752         
8753         this.store.each(function(v) { 
8754             if (cselitem) {
8755                 // start at existing selection.
8756                 if (cselitem.id == v.id) {
8757                     cselitem = false;
8758                 }
8759                 return true;
8760             }
8761                 
8762             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8763                 match = this.store.indexOf(v);
8764                 return false;
8765             }
8766             return true;
8767         }, this);
8768         
8769         if (match === false) {
8770             return true; // no more action?
8771         }
8772         // scroll to?
8773         this.view.select(match);
8774         var sn = Roo.get(this.view.getSelectedNodes()[0])
8775         //sn.scrollIntoView(sn.dom.parentNode, false);
8776     },
8777     
8778     onViewScroll : function(e, t){
8779         
8780         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8781             return;
8782         }
8783         
8784         this.hasQuery = true;
8785         
8786         this.loading = this.list.select('.loading', true).first();
8787         
8788         if(this.loading === null){
8789             this.list.createChild({
8790                 tag: 'div',
8791                 cls: 'loading select2-more-results select2-active',
8792                 html: 'Loading more results...'
8793             })
8794             
8795             this.loading = this.list.select('.loading', true).first();
8796             
8797             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8798             
8799             this.loading.hide();
8800         }
8801         
8802         this.loading.show();
8803         
8804         var _combo = this;
8805         
8806         this.page++;
8807         this.loadNext = true;
8808         
8809         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8810         
8811         return;
8812     },
8813     
8814     addItem : function(o)
8815     {   
8816         var dv = ''; // display value
8817         
8818         if (this.displayField) {
8819             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8820         } else {
8821             // this is an error condition!!!
8822             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
8823         }
8824         
8825         if(!dv.length){
8826             return;
8827         }
8828         
8829         var choice = this.choices.createChild({
8830             tag: 'li',
8831             cls: 'select2-search-choice',
8832             cn: [
8833                 {
8834                     tag: 'div',
8835                     html: dv
8836                 },
8837                 {
8838                     tag: 'a',
8839                     href: '#',
8840                     cls: 'select2-search-choice-close',
8841                     tabindex: '-1'
8842                 }
8843             ]
8844             
8845         }, this.searchField);
8846         
8847         var close = choice.select('a.select2-search-choice-close', true).first()
8848         
8849         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8850         
8851         this.item.push(o);
8852         this.lastData = o;
8853         
8854         this.syncValue();
8855         
8856         this.inputEl().dom.value = '';
8857         
8858     },
8859     
8860     onRemoveItem : function(e, _self, o)
8861     {
8862         Roo.log('remove item');
8863         var index = this.item.indexOf(o.data) * 1;
8864         
8865         if( index < 0){
8866             Roo.log('not this item?!');
8867             return;
8868         }
8869         
8870         this.item.splice(index, 1);
8871         o.item.remove();
8872         
8873         this.syncValue();
8874         
8875         this.fireEvent('remove', this);
8876         
8877     },
8878     
8879     syncValue : function()
8880     {
8881         if(!this.item.length){
8882             this.clearValue();
8883             return;
8884         }
8885             
8886         var value = [];
8887         var _this = this;
8888         Roo.each(this.item, function(i){
8889             if(_this.valueField){
8890                 value.push(i[_this.valueField]);
8891                 return;
8892             }
8893
8894             value.push(i);
8895         });
8896
8897         this.value = value.join(',');
8898
8899         if(this.hiddenField){
8900             this.hiddenField.dom.value = this.value;
8901         }
8902     }
8903
8904     /** 
8905     * @cfg {Boolean} grow 
8906     * @hide 
8907     */
8908     /** 
8909     * @cfg {Number} growMin 
8910     * @hide 
8911     */
8912     /** 
8913     * @cfg {Number} growMax 
8914     * @hide 
8915     */
8916     /**
8917      * @hide
8918      * @method autoSize
8919      */
8920 });
8921 /*
8922  * Based on:
8923  * Ext JS Library 1.1.1
8924  * Copyright(c) 2006-2007, Ext JS, LLC.
8925  *
8926  * Originally Released Under LGPL - original licence link has changed is not relivant.
8927  *
8928  * Fork - LGPL
8929  * <script type="text/javascript">
8930  */
8931
8932 /**
8933  * @class Roo.View
8934  * @extends Roo.util.Observable
8935  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8936  * This class also supports single and multi selection modes. <br>
8937  * Create a data model bound view:
8938  <pre><code>
8939  var store = new Roo.data.Store(...);
8940
8941  var view = new Roo.View({
8942     el : "my-element",
8943     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8944  
8945     singleSelect: true,
8946     selectedClass: "ydataview-selected",
8947     store: store
8948  });
8949
8950  // listen for node click?
8951  view.on("click", function(vw, index, node, e){
8952  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8953  });
8954
8955  // load XML data
8956  dataModel.load("foobar.xml");
8957  </code></pre>
8958  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8959  * <br><br>
8960  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8961  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8962  * 
8963  * Note: old style constructor is still suported (container, template, config)
8964  * 
8965  * @constructor
8966  * Create a new View
8967  * @param {Object} config The config object
8968  * 
8969  */
8970 Roo.View = function(config, depreciated_tpl, depreciated_config){
8971     
8972     if (typeof(depreciated_tpl) == 'undefined') {
8973         // new way.. - universal constructor.
8974         Roo.apply(this, config);
8975         this.el  = Roo.get(this.el);
8976     } else {
8977         // old format..
8978         this.el  = Roo.get(config);
8979         this.tpl = depreciated_tpl;
8980         Roo.apply(this, depreciated_config);
8981     }
8982     this.wrapEl  = this.el.wrap().wrap();
8983     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8984     
8985     
8986     if(typeof(this.tpl) == "string"){
8987         this.tpl = new Roo.Template(this.tpl);
8988     } else {
8989         // support xtype ctors..
8990         this.tpl = new Roo.factory(this.tpl, Roo);
8991     }
8992     
8993     
8994     this.tpl.compile();
8995    
8996   
8997     
8998      
8999     /** @private */
9000     this.addEvents({
9001         /**
9002          * @event beforeclick
9003          * Fires before a click is processed. Returns false to cancel the default action.
9004          * @param {Roo.View} this
9005          * @param {Number} index The index of the target node
9006          * @param {HTMLElement} node The target node
9007          * @param {Roo.EventObject} e The raw event object
9008          */
9009             "beforeclick" : true,
9010         /**
9011          * @event click
9012          * Fires when a template node is clicked.
9013          * @param {Roo.View} this
9014          * @param {Number} index The index of the target node
9015          * @param {HTMLElement} node The target node
9016          * @param {Roo.EventObject} e The raw event object
9017          */
9018             "click" : true,
9019         /**
9020          * @event dblclick
9021          * Fires when a template node is double clicked.
9022          * @param {Roo.View} this
9023          * @param {Number} index The index of the target node
9024          * @param {HTMLElement} node The target node
9025          * @param {Roo.EventObject} e The raw event object
9026          */
9027             "dblclick" : true,
9028         /**
9029          * @event contextmenu
9030          * Fires when a template node is right clicked.
9031          * @param {Roo.View} this
9032          * @param {Number} index The index of the target node
9033          * @param {HTMLElement} node The target node
9034          * @param {Roo.EventObject} e The raw event object
9035          */
9036             "contextmenu" : true,
9037         /**
9038          * @event selectionchange
9039          * Fires when the selected nodes change.
9040          * @param {Roo.View} this
9041          * @param {Array} selections Array of the selected nodes
9042          */
9043             "selectionchange" : true,
9044     
9045         /**
9046          * @event beforeselect
9047          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9048          * @param {Roo.View} this
9049          * @param {HTMLElement} node The node to be selected
9050          * @param {Array} selections Array of currently selected nodes
9051          */
9052             "beforeselect" : true,
9053         /**
9054          * @event preparedata
9055          * Fires on every row to render, to allow you to change the data.
9056          * @param {Roo.View} this
9057          * @param {Object} data to be rendered (change this)
9058          */
9059           "preparedata" : true
9060           
9061           
9062         });
9063
9064
9065
9066     this.el.on({
9067         "click": this.onClick,
9068         "dblclick": this.onDblClick,
9069         "contextmenu": this.onContextMenu,
9070         scope:this
9071     });
9072
9073     this.selections = [];
9074     this.nodes = [];
9075     this.cmp = new Roo.CompositeElementLite([]);
9076     if(this.store){
9077         this.store = Roo.factory(this.store, Roo.data);
9078         this.setStore(this.store, true);
9079     }
9080     
9081     if ( this.footer && this.footer.xtype) {
9082            
9083          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9084         
9085         this.footer.dataSource = this.store
9086         this.footer.container = fctr;
9087         this.footer = Roo.factory(this.footer, Roo);
9088         fctr.insertFirst(this.el);
9089         
9090         // this is a bit insane - as the paging toolbar seems to detach the el..
9091 //        dom.parentNode.parentNode.parentNode
9092          // they get detached?
9093     }
9094     
9095     
9096     Roo.View.superclass.constructor.call(this);
9097     
9098     
9099 };
9100
9101 Roo.extend(Roo.View, Roo.util.Observable, {
9102     
9103      /**
9104      * @cfg {Roo.data.Store} store Data store to load data from.
9105      */
9106     store : false,
9107     
9108     /**
9109      * @cfg {String|Roo.Element} el The container element.
9110      */
9111     el : '',
9112     
9113     /**
9114      * @cfg {String|Roo.Template} tpl The template used by this View 
9115      */
9116     tpl : false,
9117     /**
9118      * @cfg {String} dataName the named area of the template to use as the data area
9119      *                          Works with domtemplates roo-name="name"
9120      */
9121     dataName: false,
9122     /**
9123      * @cfg {String} selectedClass The css class to add to selected nodes
9124      */
9125     selectedClass : "x-view-selected",
9126      /**
9127      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9128      */
9129     emptyText : "",
9130     
9131     /**
9132      * @cfg {String} text to display on mask (default Loading)
9133      */
9134     mask : false,
9135     /**
9136      * @cfg {Boolean} multiSelect Allow multiple selection
9137      */
9138     multiSelect : false,
9139     /**
9140      * @cfg {Boolean} singleSelect Allow single selection
9141      */
9142     singleSelect:  false,
9143     
9144     /**
9145      * @cfg {Boolean} toggleSelect - selecting 
9146      */
9147     toggleSelect : false,
9148     
9149     /**
9150      * Returns the element this view is bound to.
9151      * @return {Roo.Element}
9152      */
9153     getEl : function(){
9154         return this.wrapEl;
9155     },
9156     
9157     
9158
9159     /**
9160      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9161      */
9162     refresh : function(){
9163         Roo.log('refresh');
9164         var t = this.tpl;
9165         
9166         // if we are using something like 'domtemplate', then
9167         // the what gets used is:
9168         // t.applySubtemplate(NAME, data, wrapping data..)
9169         // the outer template then get' applied with
9170         //     the store 'extra data'
9171         // and the body get's added to the
9172         //      roo-name="data" node?
9173         //      <span class='roo-tpl-{name}'></span> ?????
9174         
9175         
9176         
9177         this.clearSelections();
9178         this.el.update("");
9179         var html = [];
9180         var records = this.store.getRange();
9181         if(records.length < 1) {
9182             
9183             // is this valid??  = should it render a template??
9184             
9185             this.el.update(this.emptyText);
9186             return;
9187         }
9188         var el = this.el;
9189         if (this.dataName) {
9190             this.el.update(t.apply(this.store.meta)); //????
9191             el = this.el.child('.roo-tpl-' + this.dataName);
9192         }
9193         
9194         for(var i = 0, len = records.length; i < len; i++){
9195             var data = this.prepareData(records[i].data, i, records[i]);
9196             this.fireEvent("preparedata", this, data, i, records[i]);
9197             html[html.length] = Roo.util.Format.trim(
9198                 this.dataName ?
9199                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9200                     t.apply(data)
9201             );
9202         }
9203         
9204         
9205         
9206         el.update(html.join(""));
9207         this.nodes = el.dom.childNodes;
9208         this.updateIndexes(0);
9209     },
9210     
9211
9212     /**
9213      * Function to override to reformat the data that is sent to
9214      * the template for each node.
9215      * DEPRICATED - use the preparedata event handler.
9216      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9217      * a JSON object for an UpdateManager bound view).
9218      */
9219     prepareData : function(data, index, record)
9220     {
9221         this.fireEvent("preparedata", this, data, index, record);
9222         return data;
9223     },
9224
9225     onUpdate : function(ds, record){
9226          Roo.log('on update');   
9227         this.clearSelections();
9228         var index = this.store.indexOf(record);
9229         var n = this.nodes[index];
9230         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9231         n.parentNode.removeChild(n);
9232         this.updateIndexes(index, index);
9233     },
9234
9235     
9236     
9237 // --------- FIXME     
9238     onAdd : function(ds, records, index)
9239     {
9240         Roo.log(['on Add', ds, records, index] );        
9241         this.clearSelections();
9242         if(this.nodes.length == 0){
9243             this.refresh();
9244             return;
9245         }
9246         var n = this.nodes[index];
9247         for(var i = 0, len = records.length; i < len; i++){
9248             var d = this.prepareData(records[i].data, i, records[i]);
9249             if(n){
9250                 this.tpl.insertBefore(n, d);
9251             }else{
9252                 
9253                 this.tpl.append(this.el, d);
9254             }
9255         }
9256         this.updateIndexes(index);
9257     },
9258
9259     onRemove : function(ds, record, index){
9260         Roo.log('onRemove');
9261         this.clearSelections();
9262         var el = this.dataName  ?
9263             this.el.child('.roo-tpl-' + this.dataName) :
9264             this.el; 
9265         
9266         el.dom.removeChild(this.nodes[index]);
9267         this.updateIndexes(index);
9268     },
9269
9270     /**
9271      * Refresh an individual node.
9272      * @param {Number} index
9273      */
9274     refreshNode : function(index){
9275         this.onUpdate(this.store, this.store.getAt(index));
9276     },
9277
9278     updateIndexes : function(startIndex, endIndex){
9279         var ns = this.nodes;
9280         startIndex = startIndex || 0;
9281         endIndex = endIndex || ns.length - 1;
9282         for(var i = startIndex; i <= endIndex; i++){
9283             ns[i].nodeIndex = i;
9284         }
9285     },
9286
9287     /**
9288      * Changes the data store this view uses and refresh the view.
9289      * @param {Store} store
9290      */
9291     setStore : function(store, initial){
9292         if(!initial && this.store){
9293             this.store.un("datachanged", this.refresh);
9294             this.store.un("add", this.onAdd);
9295             this.store.un("remove", this.onRemove);
9296             this.store.un("update", this.onUpdate);
9297             this.store.un("clear", this.refresh);
9298             this.store.un("beforeload", this.onBeforeLoad);
9299             this.store.un("load", this.onLoad);
9300             this.store.un("loadexception", this.onLoad);
9301         }
9302         if(store){
9303           
9304             store.on("datachanged", this.refresh, this);
9305             store.on("add", this.onAdd, this);
9306             store.on("remove", this.onRemove, this);
9307             store.on("update", this.onUpdate, this);
9308             store.on("clear", this.refresh, this);
9309             store.on("beforeload", this.onBeforeLoad, this);
9310             store.on("load", this.onLoad, this);
9311             store.on("loadexception", this.onLoad, this);
9312         }
9313         
9314         if(store){
9315             this.refresh();
9316         }
9317     },
9318     /**
9319      * onbeforeLoad - masks the loading area.
9320      *
9321      */
9322     onBeforeLoad : function(store,opts)
9323     {
9324          Roo.log('onBeforeLoad');   
9325         if (!opts.add) {
9326             this.el.update("");
9327         }
9328         this.el.mask(this.mask ? this.mask : "Loading" ); 
9329     },
9330     onLoad : function ()
9331     {
9332         this.el.unmask();
9333     },
9334     
9335
9336     /**
9337      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9338      * @param {HTMLElement} node
9339      * @return {HTMLElement} The template node
9340      */
9341     findItemFromChild : function(node){
9342         var el = this.dataName  ?
9343             this.el.child('.roo-tpl-' + this.dataName,true) :
9344             this.el.dom; 
9345         
9346         if(!node || node.parentNode == el){
9347                     return node;
9348             }
9349             var p = node.parentNode;
9350             while(p && p != el){
9351             if(p.parentNode == el){
9352                 return p;
9353             }
9354             p = p.parentNode;
9355         }
9356             return null;
9357     },
9358
9359     /** @ignore */
9360     onClick : function(e){
9361         var item = this.findItemFromChild(e.getTarget());
9362         if(item){
9363             var index = this.indexOf(item);
9364             if(this.onItemClick(item, index, e) !== false){
9365                 this.fireEvent("click", this, index, item, e);
9366             }
9367         }else{
9368             this.clearSelections();
9369         }
9370     },
9371
9372     /** @ignore */
9373     onContextMenu : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9377         }
9378     },
9379
9380     /** @ignore */
9381     onDblClick : function(e){
9382         var item = this.findItemFromChild(e.getTarget());
9383         if(item){
9384             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9385         }
9386     },
9387
9388     onItemClick : function(item, index, e)
9389     {
9390         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9391             return false;
9392         }
9393         if (this.toggleSelect) {
9394             var m = this.isSelected(item) ? 'unselect' : 'select';
9395             Roo.log(m);
9396             var _t = this;
9397             _t[m](item, true, false);
9398             return true;
9399         }
9400         if(this.multiSelect || this.singleSelect){
9401             if(this.multiSelect && e.shiftKey && this.lastSelection){
9402                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9403             }else{
9404                 this.select(item, this.multiSelect && e.ctrlKey);
9405                 this.lastSelection = item;
9406             }
9407             e.preventDefault();
9408         }
9409         return true;
9410     },
9411
9412     /**
9413      * Get the number of selected nodes.
9414      * @return {Number}
9415      */
9416     getSelectionCount : function(){
9417         return this.selections.length;
9418     },
9419
9420     /**
9421      * Get the currently selected nodes.
9422      * @return {Array} An array of HTMLElements
9423      */
9424     getSelectedNodes : function(){
9425         return this.selections;
9426     },
9427
9428     /**
9429      * Get the indexes of the selected nodes.
9430      * @return {Array}
9431      */
9432     getSelectedIndexes : function(){
9433         var indexes = [], s = this.selections;
9434         for(var i = 0, len = s.length; i < len; i++){
9435             indexes.push(s[i].nodeIndex);
9436         }
9437         return indexes;
9438     },
9439
9440     /**
9441      * Clear all selections
9442      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9443      */
9444     clearSelections : function(suppressEvent){
9445         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9446             this.cmp.elements = this.selections;
9447             this.cmp.removeClass(this.selectedClass);
9448             this.selections = [];
9449             if(!suppressEvent){
9450                 this.fireEvent("selectionchange", this, this.selections);
9451             }
9452         }
9453     },
9454
9455     /**
9456      * Returns true if the passed node is selected
9457      * @param {HTMLElement/Number} node The node or node index
9458      * @return {Boolean}
9459      */
9460     isSelected : function(node){
9461         var s = this.selections;
9462         if(s.length < 1){
9463             return false;
9464         }
9465         node = this.getNode(node);
9466         return s.indexOf(node) !== -1;
9467     },
9468
9469     /**
9470      * Selects nodes.
9471      * @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
9472      * @param {Boolean} keepExisting (optional) true to keep existing selections
9473      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9474      */
9475     select : function(nodeInfo, keepExisting, suppressEvent){
9476         if(nodeInfo instanceof Array){
9477             if(!keepExisting){
9478                 this.clearSelections(true);
9479             }
9480             for(var i = 0, len = nodeInfo.length; i < len; i++){
9481                 this.select(nodeInfo[i], true, true);
9482             }
9483             return;
9484         } 
9485         var node = this.getNode(nodeInfo);
9486         if(!node || this.isSelected(node)){
9487             return; // already selected.
9488         }
9489         if(!keepExisting){
9490             this.clearSelections(true);
9491         }
9492         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9493             Roo.fly(node).addClass(this.selectedClass);
9494             this.selections.push(node);
9495             if(!suppressEvent){
9496                 this.fireEvent("selectionchange", this, this.selections);
9497             }
9498         }
9499         
9500         
9501     },
9502       /**
9503      * Unselects nodes.
9504      * @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
9505      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9506      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9507      */
9508     unselect : function(nodeInfo, keepExisting, suppressEvent)
9509     {
9510         if(nodeInfo instanceof Array){
9511             Roo.each(this.selections, function(s) {
9512                 this.unselect(s, nodeInfo);
9513             }, this);
9514             return;
9515         }
9516         var node = this.getNode(nodeInfo);
9517         if(!node || !this.isSelected(node)){
9518             Roo.log("not selected");
9519             return; // not selected.
9520         }
9521         // fireevent???
9522         var ns = [];
9523         Roo.each(this.selections, function(s) {
9524             if (s == node ) {
9525                 Roo.fly(node).removeClass(this.selectedClass);
9526
9527                 return;
9528             }
9529             ns.push(s);
9530         },this);
9531         
9532         this.selections= ns;
9533         this.fireEvent("selectionchange", this, this.selections);
9534     },
9535
9536     /**
9537      * Gets a template node.
9538      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9539      * @return {HTMLElement} The node or null if it wasn't found
9540      */
9541     getNode : function(nodeInfo){
9542         if(typeof nodeInfo == "string"){
9543             return document.getElementById(nodeInfo);
9544         }else if(typeof nodeInfo == "number"){
9545             return this.nodes[nodeInfo];
9546         }
9547         return nodeInfo;
9548     },
9549
9550     /**
9551      * Gets a range template nodes.
9552      * @param {Number} startIndex
9553      * @param {Number} endIndex
9554      * @return {Array} An array of nodes
9555      */
9556     getNodes : function(start, end){
9557         var ns = this.nodes;
9558         start = start || 0;
9559         end = typeof end == "undefined" ? ns.length - 1 : end;
9560         var nodes = [];
9561         if(start <= end){
9562             for(var i = start; i <= end; i++){
9563                 nodes.push(ns[i]);
9564             }
9565         } else{
9566             for(var i = start; i >= end; i--){
9567                 nodes.push(ns[i]);
9568             }
9569         }
9570         return nodes;
9571     },
9572
9573     /**
9574      * Finds the index of the passed node
9575      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9576      * @return {Number} The index of the node or -1
9577      */
9578     indexOf : function(node){
9579         node = this.getNode(node);
9580         if(typeof node.nodeIndex == "number"){
9581             return node.nodeIndex;
9582         }
9583         var ns = this.nodes;
9584         for(var i = 0, len = ns.length; i < len; i++){
9585             if(ns[i] == node){
9586                 return i;
9587             }
9588         }
9589         return -1;
9590     }
9591 });
9592 /*
9593  * - LGPL
9594  *
9595  * based on jquery fullcalendar
9596  * 
9597  */
9598
9599 Roo.bootstrap = Roo.bootstrap || {};
9600 /**
9601  * @class Roo.bootstrap.Calendar
9602  * @extends Roo.bootstrap.Component
9603  * Bootstrap Calendar class
9604  * @cfg {Boolean} loadMask (true|false) default false
9605     
9606  * @constructor
9607  * Create a new Container
9608  * @param {Object} config The config object
9609  */
9610
9611
9612
9613 Roo.bootstrap.Calendar = function(config){
9614     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9615      this.addEvents({
9616         /**
9617              * @event select
9618              * Fires when a date is selected
9619              * @param {DatePicker} this
9620              * @param {Date} date The selected date
9621              */
9622         'select': true,
9623         /**
9624              * @event monthchange
9625              * Fires when the displayed month changes 
9626              * @param {DatePicker} this
9627              * @param {Date} date The selected month
9628              */
9629         'monthchange': true,
9630         /**
9631              * @event evententer
9632              * Fires when mouse over an event
9633              * @param {Calendar} this
9634              * @param {event} Event
9635              */
9636         'evententer': true,
9637         /**
9638              * @event eventleave
9639              * Fires when the mouse leaves an
9640              * @param {Calendar} this
9641              * @param {event}
9642              */
9643         'eventleave': true,
9644         /**
9645              * @event eventclick
9646              * Fires when the mouse click an
9647              * @param {Calendar} this
9648              * @param {event}
9649              */
9650         'eventclick': true
9651         
9652     });
9653
9654 };
9655
9656 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
9657     
9658      /**
9659      * @cfg {Number} startDay
9660      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9661      */
9662     startDay : 0,
9663     
9664     loadMask : false,
9665       
9666     getAutoCreate : function(){
9667         
9668         
9669         var fc_button = function(name, corner, style, content ) {
9670             return Roo.apply({},{
9671                 tag : 'span',
9672                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
9673                          (corner.length ?
9674                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9675                             ''
9676                         ),
9677                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9678                 unselectable: 'on'
9679             });
9680         };
9681         
9682         var header = {
9683             tag : 'table',
9684             cls : 'fc-header',
9685             style : 'width:100%',
9686             cn : [
9687                 {
9688                     tag: 'tr',
9689                     cn : [
9690                         {
9691                             tag : 'td',
9692                             cls : 'fc-header-left',
9693                             cn : [
9694                                 fc_button('prev', 'left', 'arrow', '&#8249;' ),
9695                                 fc_button('next', 'right', 'arrow', '&#8250;' ),
9696                                 { tag: 'span', cls: 'fc-header-space' },
9697                                 fc_button('today', 'left right', '', 'today' )  // neds state disabled..
9698                                 
9699                                 
9700                             ]
9701                         },
9702                         
9703                         {
9704                             tag : 'td',
9705                             cls : 'fc-header-center',
9706                             cn : [
9707                                 {
9708                                     tag: 'span',
9709                                     cls: 'fc-header-title',
9710                                     cn : {
9711                                         tag: 'H2',
9712                                         html : 'month / year'
9713                                     }
9714                                 }
9715                                 
9716                             ]
9717                         },
9718                         {
9719                             tag : 'td',
9720                             cls : 'fc-header-right',
9721                             cn : [
9722                           /*      fc_button('month', 'left', '', 'month' ),
9723                                 fc_button('week', '', '', 'week' ),
9724                                 fc_button('day', 'right', '', 'day' )
9725                             */    
9726                                 
9727                             ]
9728                         }
9729                         
9730                     ]
9731                 }
9732             ]
9733         };
9734         
9735        
9736         var cal_heads = function() {
9737             var ret = [];
9738             // fixme - handle this.
9739             
9740             for (var i =0; i < Date.dayNames.length; i++) {
9741                 var d = Date.dayNames[i];
9742                 ret.push({
9743                     tag: 'th',
9744                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9745                     html : d.substring(0,3)
9746                 });
9747                 
9748             }
9749             ret[0].cls += ' fc-first';
9750             ret[6].cls += ' fc-last';
9751             return ret;
9752         };
9753         var cal_cell = function(n) {
9754             return  {
9755                 tag: 'td',
9756                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9757                 cn : [
9758                     {
9759                         cn : [
9760                             {
9761                                 cls: 'fc-day-number',
9762                                 html: 'D'
9763                             },
9764                             {
9765                                 cls: 'fc-day-content',
9766                              
9767                                 cn : [
9768                                      {
9769                                         style: 'position: relative;' // height: 17px;
9770                                     }
9771                                 ]
9772                             }
9773                             
9774                             
9775                         ]
9776                     }
9777                 ]
9778                 
9779             }
9780         };
9781         var cal_rows = function() {
9782             
9783             var ret = []
9784             for (var r = 0; r < 6; r++) {
9785                 var row= {
9786                     tag : 'tr',
9787                     cls : 'fc-week',
9788                     cn : []
9789                 };
9790                 
9791                 for (var i =0; i < Date.dayNames.length; i++) {
9792                     var d = Date.dayNames[i];
9793                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9794
9795                 }
9796                 row.cn[0].cls+=' fc-first';
9797                 row.cn[0].cn[0].style = 'min-height:90px';
9798                 row.cn[6].cls+=' fc-last';
9799                 ret.push(row);
9800                 
9801             }
9802             ret[0].cls += ' fc-first';
9803             ret[4].cls += ' fc-prev-last';
9804             ret[5].cls += ' fc-last';
9805             return ret;
9806             
9807         };
9808         
9809         var cal_table = {
9810             tag: 'table',
9811             cls: 'fc-border-separate',
9812             style : 'width:100%',
9813             cellspacing  : 0,
9814             cn : [
9815                 { 
9816                     tag: 'thead',
9817                     cn : [
9818                         { 
9819                             tag: 'tr',
9820                             cls : 'fc-first fc-last',
9821                             cn : cal_heads()
9822                         }
9823                     ]
9824                 },
9825                 { 
9826                     tag: 'tbody',
9827                     cn : cal_rows()
9828                 }
9829                   
9830             ]
9831         };
9832          
9833          var cfg = {
9834             cls : 'fc fc-ltr',
9835             cn : [
9836                 header,
9837                 {
9838                     cls : 'fc-content',
9839                     style : "position: relative;",
9840                     cn : [
9841                         {
9842                             cls : 'fc-view fc-view-month fc-grid',
9843                             style : 'position: relative',
9844                             unselectable : 'on',
9845                             cn : [
9846                                 {
9847                                     cls : 'fc-event-container',
9848                                     style : 'position:absolute;z-index:8;top:0;left:0;'
9849                                 },
9850                                 cal_table
9851                             ]
9852                         }
9853                     ]
9854     
9855                 }
9856            ] 
9857             
9858         };
9859         
9860          
9861         
9862         return cfg;
9863     },
9864     
9865     
9866     initEvents : function()
9867     {
9868         if(!this.store){
9869             throw "can not find store for calendar";
9870         }
9871         
9872         var mark = {
9873             tag: "div",
9874             cls:"x-dlg-mask",
9875             style: "text-align:center",
9876             cn: [
9877                 {
9878                     tag: "div",
9879                     style: "background-color:white;width:50%;margin:250 auto",
9880                     cn: [
9881                         {
9882                             tag: "img",
9883                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9884                         },
9885                         {
9886                             tag: "span",
9887                             html: "Loading"
9888                         }
9889                         
9890                     ]
9891                 }
9892             ]
9893         }
9894         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9895         
9896         var size = this.el.select('.fc-content', true).first().getSize();
9897         this.maskEl.setSize(size.width, size.height);
9898         this.maskEl.enableDisplayMode("block");
9899         if(!this.loadMask){
9900             this.maskEl.hide();
9901         }
9902         
9903         this.store = Roo.factory(this.store, Roo.data);
9904         this.store.on('load', this.onLoad, this);
9905         this.store.on('beforeload', this.onBeforeLoad, this);
9906         
9907         this.resize();
9908         
9909         this.cells = this.el.select('.fc-day',true);
9910         //Roo.log(this.cells);
9911         this.textNodes = this.el.query('.fc-day-number');
9912         this.cells.addClassOnOver('fc-state-hover');
9913         
9914         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9915         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9916         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9917         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9918         
9919         this.on('monthchange', this.onMonthChange, this);
9920         
9921         this.update(new Date().clearTime());
9922     },
9923     
9924     resize : function() {
9925         var sz  = this.el.getSize();
9926         
9927         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9928         this.el.select('.fc-day-content div',true).setHeight(34);
9929     },
9930     
9931     
9932     // private
9933     showPrevMonth : function(e){
9934         this.update(this.activeDate.add("mo", -1));
9935     },
9936     showToday : function(e){
9937         this.update(new Date().clearTime());
9938     },
9939     // private
9940     showNextMonth : function(e){
9941         this.update(this.activeDate.add("mo", 1));
9942     },
9943
9944     // private
9945     showPrevYear : function(){
9946         this.update(this.activeDate.add("y", -1));
9947     },
9948
9949     // private
9950     showNextYear : function(){
9951         this.update(this.activeDate.add("y", 1));
9952     },
9953
9954     
9955    // private
9956     update : function(date)
9957     {
9958         var vd = this.activeDate;
9959         this.activeDate = date;
9960 //        if(vd && this.el){
9961 //            var t = date.getTime();
9962 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9963 //                Roo.log('using add remove');
9964 //                
9965 //                this.fireEvent('monthchange', this, date);
9966 //                
9967 //                this.cells.removeClass("fc-state-highlight");
9968 //                this.cells.each(function(c){
9969 //                   if(c.dateValue == t){
9970 //                       c.addClass("fc-state-highlight");
9971 //                       setTimeout(function(){
9972 //                            try{c.dom.firstChild.focus();}catch(e){}
9973 //                       }, 50);
9974 //                       return false;
9975 //                   }
9976 //                   return true;
9977 //                });
9978 //                return;
9979 //            }
9980 //        }
9981         
9982         var days = date.getDaysInMonth();
9983         
9984         var firstOfMonth = date.getFirstDateOfMonth();
9985         var startingPos = firstOfMonth.getDay()-this.startDay;
9986         
9987         if(startingPos < this.startDay){
9988             startingPos += 7;
9989         }
9990         
9991         var pm = date.add(Date.MONTH, -1);
9992         var prevStart = pm.getDaysInMonth()-startingPos;
9993 //        
9994         this.cells = this.el.select('.fc-day',true);
9995         this.textNodes = this.el.query('.fc-day-number');
9996         this.cells.addClassOnOver('fc-state-hover');
9997         
9998         var cells = this.cells.elements;
9999         var textEls = this.textNodes;
10000         
10001         Roo.each(cells, function(cell){
10002             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10003         });
10004         
10005         days += startingPos;
10006
10007         // convert everything to numbers so it's fast
10008         var day = 86400000;
10009         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10010         //Roo.log(d);
10011         //Roo.log(pm);
10012         //Roo.log(prevStart);
10013         
10014         var today = new Date().clearTime().getTime();
10015         var sel = date.clearTime().getTime();
10016         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10017         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10018         var ddMatch = this.disabledDatesRE;
10019         var ddText = this.disabledDatesText;
10020         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10021         var ddaysText = this.disabledDaysText;
10022         var format = this.format;
10023         
10024         var setCellClass = function(cal, cell){
10025             
10026             //Roo.log('set Cell Class');
10027             cell.title = "";
10028             var t = d.getTime();
10029             
10030             //Roo.log(d);
10031             
10032             cell.dateValue = t;
10033             if(t == today){
10034                 cell.className += " fc-today";
10035                 cell.className += " fc-state-highlight";
10036                 cell.title = cal.todayText;
10037             }
10038             if(t == sel){
10039                 // disable highlight in other month..
10040                 //cell.className += " fc-state-highlight";
10041                 
10042             }
10043             // disabling
10044             if(t < min) {
10045                 cell.className = " fc-state-disabled";
10046                 cell.title = cal.minText;
10047                 return;
10048             }
10049             if(t > max) {
10050                 cell.className = " fc-state-disabled";
10051                 cell.title = cal.maxText;
10052                 return;
10053             }
10054             if(ddays){
10055                 if(ddays.indexOf(d.getDay()) != -1){
10056                     cell.title = ddaysText;
10057                     cell.className = " fc-state-disabled";
10058                 }
10059             }
10060             if(ddMatch && format){
10061                 var fvalue = d.dateFormat(format);
10062                 if(ddMatch.test(fvalue)){
10063                     cell.title = ddText.replace("%0", fvalue);
10064                     cell.className = " fc-state-disabled";
10065                 }
10066             }
10067             
10068             if (!cell.initialClassName) {
10069                 cell.initialClassName = cell.dom.className;
10070             }
10071             
10072             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
10073         };
10074
10075         var i = 0;
10076         
10077         for(; i < startingPos; i++) {
10078             textEls[i].innerHTML = (++prevStart);
10079             d.setDate(d.getDate()+1);
10080             
10081             cells[i].className = "fc-past fc-other-month";
10082             setCellClass(this, cells[i]);
10083         }
10084         
10085         var intDay = 0;
10086         
10087         for(; i < days; i++){
10088             intDay = i - startingPos + 1;
10089             textEls[i].innerHTML = (intDay);
10090             d.setDate(d.getDate()+1);
10091             
10092             cells[i].className = ''; // "x-date-active";
10093             setCellClass(this, cells[i]);
10094         }
10095         var extraDays = 0;
10096         
10097         for(; i < 42; i++) {
10098             textEls[i].innerHTML = (++extraDays);
10099             d.setDate(d.getDate()+1);
10100             
10101             cells[i].className = "fc-future fc-other-month";
10102             setCellClass(this, cells[i]);
10103         }
10104         
10105         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10106         
10107         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10108         
10109         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10110         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10111         
10112         if(totalRows != 6){
10113             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10114             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10115         }
10116         
10117         this.fireEvent('monthchange', this, date);
10118         
10119         
10120         /*
10121         if(!this.internalRender){
10122             var main = this.el.dom.firstChild;
10123             var w = main.offsetWidth;
10124             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10125             Roo.fly(main).setWidth(w);
10126             this.internalRender = true;
10127             // opera does not respect the auto grow header center column
10128             // then, after it gets a width opera refuses to recalculate
10129             // without a second pass
10130             if(Roo.isOpera && !this.secondPass){
10131                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10132                 this.secondPass = true;
10133                 this.update.defer(10, this, [date]);
10134             }
10135         }
10136         */
10137         
10138     },
10139     
10140     findCell : function(dt) {
10141         dt = dt.clearTime().getTime();
10142         var ret = false;
10143         this.cells.each(function(c){
10144             //Roo.log("check " +c.dateValue + '?=' + dt);
10145             if(c.dateValue == dt){
10146                 ret = c;
10147                 return false;
10148             }
10149             return true;
10150         });
10151         
10152         return ret;
10153     },
10154     
10155     findCells : function(ev) {
10156         var s = ev.start.clone().clearTime().getTime();
10157        // Roo.log(s);
10158         var e= ev.end.clone().clearTime().getTime();
10159        // Roo.log(e);
10160         var ret = [];
10161         this.cells.each(function(c){
10162              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10163             
10164             if(c.dateValue > e){
10165                 return ;
10166             }
10167             if(c.dateValue < s){
10168                 return ;
10169             }
10170             ret.push(c);
10171         });
10172         
10173         return ret;    
10174     },
10175     
10176     findBestRow: function(cells)
10177     {
10178         var ret = 0;
10179         
10180         for (var i =0 ; i < cells.length;i++) {
10181             ret  = Math.max(cells[i].rows || 0,ret);
10182         }
10183         return ret;
10184         
10185     },
10186     
10187     
10188     addItem : function(ev)
10189     {
10190         // look for vertical location slot in
10191         var cells = this.findCells(ev);
10192         
10193         ev.row = this.findBestRow(cells);
10194         
10195         // work out the location.
10196         
10197         var crow = false;
10198         var rows = [];
10199         for(var i =0; i < cells.length; i++) {
10200             if (!crow) {
10201                 crow = {
10202                     start : cells[i],
10203                     end :  cells[i]
10204                 };
10205                 continue;
10206             }
10207             if (crow.start.getY() == cells[i].getY()) {
10208                 // on same row.
10209                 crow.end = cells[i];
10210                 continue;
10211             }
10212             // different row.
10213             rows.push(crow);
10214             crow = {
10215                 start: cells[i],
10216                 end : cells[i]
10217             };
10218             
10219         }
10220         
10221         rows.push(crow);
10222         ev.els = [];
10223         ev.rows = rows;
10224         ev.cells = cells;
10225         for (var i = 0; i < cells.length;i++) {
10226             cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10227             
10228         }
10229         
10230         this.calevents.push(ev);
10231     },
10232     
10233     clearEvents: function() {
10234         
10235         if(!this.calevents){
10236             return;
10237         }
10238         
10239         Roo.each(this.cells.elements, function(c){
10240             c.rows = 0;
10241         });
10242         
10243         Roo.each(this.calevents, function(e) {
10244             Roo.each(e.els, function(el) {
10245                 el.un('mouseenter' ,this.onEventEnter, this);
10246                 el.un('mouseleave' ,this.onEventLeave, this);
10247                 el.remove();
10248             },this);
10249         },this);
10250         
10251     },
10252     
10253     renderEvents: function()
10254     {   
10255         // first make sure there is enough space..
10256         
10257         this.cells.each(function(c) {
10258         
10259             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10260         });
10261         
10262         for (var e = 0; e < this.calevents.length; e++) {
10263             var ev = this.calevents[e];
10264             var cells = ev.cells;
10265             var rows = ev.rows;
10266             
10267             for(var i =0; i < rows.length; i++) {
10268                 
10269                  
10270                 // how many rows should it span..
10271                 
10272                 var  cfg = {
10273                     cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10274                     style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10275                     
10276                     unselectable : "on",
10277                     cn : [
10278                         {
10279                             cls: 'fc-event-inner',
10280                             cn : [
10281 //                                {
10282 //                                  tag:'span',
10283 //                                  cls: 'fc-event-time',
10284 //                                  html : cells.length > 1 ? '' : ev.time
10285 //                                },
10286                                 {
10287                                   tag:'span',
10288                                   cls: 'fc-event-title',
10289                                   html : String.format('{0}', ev.title)
10290                                 }
10291                                 
10292                                 
10293                             ]
10294                         },
10295                         {
10296                             cls: 'ui-resizable-handle ui-resizable-e',
10297                             html : '&nbsp;&nbsp;&nbsp'
10298                         }
10299                         
10300                     ]
10301                 };
10302                 if (i == 0) {
10303                     cfg.cls += ' fc-event-start';
10304                 }
10305                 if ((i+1) == rows.length) {
10306                     cfg.cls += ' fc-event-end';
10307                 }
10308                 
10309                 var ctr = this.el.select('.fc-event-container',true).first();
10310                 var cg = ctr.createChild(cfg);
10311                 
10312                 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10313                 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10314                 cg.on('click', this.onEventClick, this, ev);
10315                 
10316                 ev.els.push(cg);
10317                 
10318                 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10319                 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10320                 //Roo.log(cg);
10321                 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
10322                 cg.setWidth(ebox.right - sbox.x -2);
10323             }
10324             
10325             
10326         }
10327         
10328     },
10329     
10330     onEventEnter: function (e, el,event,d) {
10331         this.fireEvent('evententer', this, el, event);
10332     },
10333     
10334     onEventLeave: function (e, el,event,d) {
10335         this.fireEvent('eventleave', this, el, event);
10336     },
10337     
10338     onEventClick: function (e, el,event,d) {
10339         this.fireEvent('eventclick', this, el, event);
10340     },
10341     
10342     onMonthChange: function () {
10343         this.store.load();
10344     },
10345     
10346     onLoad: function () 
10347     {   
10348         this.calevents = [];
10349         var cal = this;
10350         
10351         if(this.store.getCount() > 0){
10352             this.store.data.each(function(d){
10353                cal.addItem({
10354                     id : d.data.id,
10355                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10356                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10357                     time : d.data.start_time,
10358                     title : d.data.title,
10359                     description : d.data.description,
10360                     venue : d.data.venue
10361                 });
10362             });
10363         }
10364         
10365         this.renderEvents();
10366         
10367         if(this.loadMask){
10368             this.maskEl.hide();
10369         }
10370     },
10371     
10372     onBeforeLoad: function()
10373     {
10374         this.clearEvents();
10375         
10376         if(this.loadMask){
10377             this.maskEl.show();
10378         }
10379     }
10380 });
10381
10382  
10383  /*
10384  * - LGPL
10385  *
10386  * element
10387  * 
10388  */
10389
10390 /**
10391  * @class Roo.bootstrap.Popover
10392  * @extends Roo.bootstrap.Component
10393  * Bootstrap Popover class
10394  * @cfg {String} html contents of the popover   (or false to use children..)
10395  * @cfg {String} title of popover (or false to hide)
10396  * @cfg {String} placement how it is placed
10397  * @cfg {String} trigger click || hover (or false to trigger manually)
10398  * @cfg {String} over what (parent or false to trigger manually.)
10399  * 
10400  * @constructor
10401  * Create a new Popover
10402  * @param {Object} config The config object
10403  */
10404
10405 Roo.bootstrap.Popover = function(config){
10406     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10407 };
10408
10409 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
10410     
10411     title: 'Fill in a title',
10412     html: false,
10413     
10414     placement : 'right',
10415     trigger : 'hover', // hover
10416     
10417     over: 'parent',
10418     
10419     can_build_overlaid : false,
10420     
10421     getChildContainer : function()
10422     {
10423         return this.el.select('.popover-content',true).first();
10424     },
10425     
10426     getAutoCreate : function(){
10427          Roo.log('make popover?');
10428         var cfg = {
10429            cls : 'popover roo-dynamic',
10430            style: 'display:block',
10431            cn : [
10432                 {
10433                     cls : 'arrow'
10434                 },
10435                 {
10436                     cls : 'popover-inner',
10437                     cn : [
10438                         {
10439                             tag: 'h3',
10440                             cls: 'popover-title',
10441                             html : this.title
10442                         },
10443                         {
10444                             cls : 'popover-content',
10445                             html : this.html
10446                         }
10447                     ]
10448                     
10449                 }
10450            ]
10451         };
10452         
10453         return cfg;
10454     },
10455     setTitle: function(str)
10456     {
10457         this.el.select('.popover-title',true).first().dom.innerHTML = str;
10458     },
10459     setContent: function(str)
10460     {
10461         this.el.select('.popover-content',true).first().dom.innerHTML = str;
10462     },
10463     // as it get's added to the bottom of the page.
10464     onRender : function(ct, position)
10465     {
10466         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10467         if(!this.el){
10468             var cfg = Roo.apply({},  this.getAutoCreate());
10469             cfg.id = Roo.id();
10470             
10471             if (this.cls) {
10472                 cfg.cls += ' ' + this.cls;
10473             }
10474             if (this.style) {
10475                 cfg.style = this.style;
10476             }
10477             Roo.log("adding to ")
10478             this.el = Roo.get(document.body).createChild(cfg, position);
10479             Roo.log(this.el);
10480         }
10481         this.initEvents();
10482     },
10483     
10484     initEvents : function()
10485     {
10486         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10487         this.el.enableDisplayMode('block');
10488         this.el.hide();
10489         if (this.over === false) {
10490             return; 
10491         }
10492         if (this.triggers === false) {
10493             return;
10494         }
10495         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10496         var triggers = this.trigger ? this.trigger.split(' ') : [];
10497         Roo.each(triggers, function(trigger) {
10498         
10499             if (trigger == 'click') {
10500                 on_el.on('click', this.toggle, this);
10501             } else if (trigger != 'manual') {
10502                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
10503                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10504       
10505                 on_el.on(eventIn  ,this.enter, this);
10506                 on_el.on(eventOut, this.leave, this);
10507             }
10508         }, this);
10509         
10510     },
10511     
10512     
10513     // private
10514     timeout : null,
10515     hoverState : null,
10516     
10517     toggle : function () {
10518         this.hoverState == 'in' ? this.leave() : this.enter();
10519     },
10520     
10521     enter : function () {
10522        
10523     
10524         clearTimeout(this.timeout);
10525     
10526         this.hoverState = 'in'
10527     
10528         if (!this.delay || !this.delay.show) {
10529             this.show();
10530             return 
10531         }
10532         var _t = this;
10533         this.timeout = setTimeout(function () {
10534             if (_t.hoverState == 'in') {
10535                 _t.show();
10536             }
10537         }, this.delay.show)
10538     },
10539     leave : function() {
10540         clearTimeout(this.timeout);
10541     
10542         this.hoverState = 'out'
10543     
10544         if (!this.delay || !this.delay.hide) {
10545             this.hide();
10546             return 
10547         }
10548         var _t = this;
10549         this.timeout = setTimeout(function () {
10550             if (_t.hoverState == 'out') {
10551                 _t.hide();
10552             }
10553         }, this.delay.hide)
10554     },
10555     
10556     show : function (on_el)
10557     {
10558         if (!on_el) {
10559             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10560         }
10561         // set content.
10562         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10563         if (this.html !== false) {
10564             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10565         }
10566         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10567         if (!this.title.length) {
10568             this.el.select('.popover-title',true).hide();
10569         }
10570         
10571         var placement = typeof this.placement == 'function' ?
10572             this.placement.call(this, this.el, on_el) :
10573             this.placement;
10574             
10575         var autoToken = /\s?auto?\s?/i;
10576         var autoPlace = autoToken.test(placement);
10577         if (autoPlace) {
10578             placement = placement.replace(autoToken, '') || 'top';
10579         }
10580         
10581         //this.el.detach()
10582         //this.el.setXY([0,0]);
10583         this.el.show();
10584         this.el.dom.style.display='block';
10585         this.el.addClass(placement);
10586         
10587         //this.el.appendTo(on_el);
10588         
10589         var p = this.getPosition();
10590         var box = this.el.getBox();
10591         
10592         if (autoPlace) {
10593             // fixme..
10594         }
10595         var align = Roo.bootstrap.Popover.alignment[placement]
10596         this.el.alignTo(on_el, align[0],align[1]);
10597         //var arrow = this.el.select('.arrow',true).first();
10598         //arrow.set(align[2], 
10599         
10600         this.el.addClass('in');
10601         this.hoverState = null;
10602         
10603         if (this.el.hasClass('fade')) {
10604             // fade it?
10605         }
10606         
10607     },
10608     hide : function()
10609     {
10610         this.el.setXY([0,0]);
10611         this.el.removeClass('in');
10612         this.el.hide();
10613         
10614     }
10615     
10616 });
10617
10618 Roo.bootstrap.Popover.alignment = {
10619     'left' : ['r-l', [-10,0], 'right'],
10620     'right' : ['l-r', [10,0], 'left'],
10621     'bottom' : ['t-b', [0,10], 'top'],
10622     'top' : [ 'b-t', [0,-10], 'bottom']
10623 };
10624
10625  /*
10626  * - LGPL
10627  *
10628  * Progress
10629  * 
10630  */
10631
10632 /**
10633  * @class Roo.bootstrap.Progress
10634  * @extends Roo.bootstrap.Component
10635  * Bootstrap Progress class
10636  * @cfg {Boolean} striped striped of the progress bar
10637  * @cfg {Boolean} active animated of the progress bar
10638  * 
10639  * 
10640  * @constructor
10641  * Create a new Progress
10642  * @param {Object} config The config object
10643  */
10644
10645 Roo.bootstrap.Progress = function(config){
10646     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10647 };
10648
10649 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
10650     
10651     striped : false,
10652     active: false,
10653     
10654     getAutoCreate : function(){
10655         var cfg = {
10656             tag: 'div',
10657             cls: 'progress'
10658         };
10659         
10660         
10661         if(this.striped){
10662             cfg.cls += ' progress-striped';
10663         }
10664       
10665         if(this.active){
10666             cfg.cls += ' active';
10667         }
10668         
10669         
10670         return cfg;
10671     }
10672    
10673 });
10674
10675  
10676
10677  /*
10678  * - LGPL
10679  *
10680  * ProgressBar
10681  * 
10682  */
10683
10684 /**
10685  * @class Roo.bootstrap.ProgressBar
10686  * @extends Roo.bootstrap.Component
10687  * Bootstrap ProgressBar class
10688  * @cfg {Number} aria_valuenow aria-value now
10689  * @cfg {Number} aria_valuemin aria-value min
10690  * @cfg {Number} aria_valuemax aria-value max
10691  * @cfg {String} label label for the progress bar
10692  * @cfg {String} panel (success | info | warning | danger )
10693  * @cfg {String} role role of the progress bar
10694  * @cfg {String} sr_only text
10695  * 
10696  * 
10697  * @constructor
10698  * Create a new ProgressBar
10699  * @param {Object} config The config object
10700  */
10701
10702 Roo.bootstrap.ProgressBar = function(config){
10703     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10704 };
10705
10706 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
10707     
10708     aria_valuenow : 0,
10709     aria_valuemin : 0,
10710     aria_valuemax : 100,
10711     label : false,
10712     panel : false,
10713     role : false,
10714     sr_only: false,
10715     
10716     getAutoCreate : function()
10717     {
10718         
10719         var cfg = {
10720             tag: 'div',
10721             cls: 'progress-bar',
10722             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10723         };
10724         
10725         if(this.sr_only){
10726             cfg.cn = {
10727                 tag: 'span',
10728                 cls: 'sr-only',
10729                 html: this.sr_only
10730             }
10731         }
10732         
10733         if(this.role){
10734             cfg.role = this.role;
10735         }
10736         
10737         if(this.aria_valuenow){
10738             cfg['aria-valuenow'] = this.aria_valuenow;
10739         }
10740         
10741         if(this.aria_valuemin){
10742             cfg['aria-valuemin'] = this.aria_valuemin;
10743         }
10744         
10745         if(this.aria_valuemax){
10746             cfg['aria-valuemax'] = this.aria_valuemax;
10747         }
10748         
10749         if(this.label && !this.sr_only){
10750             cfg.html = this.label;
10751         }
10752         
10753         if(this.panel){
10754             cfg.cls += ' progress-bar-' + this.panel;
10755         }
10756         
10757         return cfg;
10758     },
10759     
10760     update : function(aria_valuenow)
10761     {
10762         this.aria_valuenow = aria_valuenow;
10763         
10764         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10765     }
10766    
10767 });
10768
10769  
10770
10771  /*
10772  * - LGPL
10773  *
10774  * TabPanel
10775  * 
10776  */
10777
10778 /**
10779  * @class Roo.bootstrap.TabPanel
10780  * @extends Roo.bootstrap.Component
10781  * Bootstrap TabPanel class
10782  * @cfg {Boolean} active panel active
10783  * @cfg {String} html panel content
10784  * @cfg {String} tabId tab relate id
10785  * 
10786  * 
10787  * @constructor
10788  * Create a new TabPanel
10789  * @param {Object} config The config object
10790  */
10791
10792 Roo.bootstrap.TabPanel = function(config){
10793     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10794 };
10795
10796 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
10797     
10798     active: false,
10799     html: false,
10800     tabId: false,
10801     
10802     getAutoCreate : function(){
10803         var cfg = {
10804             tag: 'div',
10805             cls: 'tab-pane',
10806             html: this.html || ''
10807         };
10808         
10809         if(this.active){
10810             cfg.cls += ' active';
10811         }
10812         
10813         if(this.tabId){
10814             cfg.tabId = this.tabId;
10815         }
10816         
10817         return cfg;
10818     }
10819    
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * DateField
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.DateField
10833  * @extends Roo.bootstrap.Input
10834  * Bootstrap DateField class
10835  * @cfg {Number} weekStart default 0
10836  * @cfg {Number} weekStart default 0
10837  * @cfg {Number} viewMode default empty, (months|years)
10838  * @cfg {Number} minViewMode default empty, (months|years)
10839  * @cfg {Number} startDate default -Infinity
10840  * @cfg {Number} endDate default Infinity
10841  * @cfg {Boolean} todayHighlight default false
10842  * @cfg {Boolean} todayBtn default false
10843  * @cfg {Boolean} calendarWeeks default false
10844  * @cfg {Object} daysOfWeekDisabled default empty
10845  * 
10846  * @cfg {Boolean} keyboardNavigation default true
10847  * @cfg {String} language default en
10848  * 
10849  * @constructor
10850  * Create a new DateField
10851  * @param {Object} config The config object
10852  */
10853
10854 Roo.bootstrap.DateField = function(config){
10855     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10856 };
10857
10858 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
10859     
10860     /**
10861      * @cfg {String} format
10862      * The default date format string which can be overriden for localization support.  The format must be
10863      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10864      */
10865     format : "m/d/y",
10866     /**
10867      * @cfg {String} altFormats
10868      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10869      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10870      */
10871     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10872     
10873     weekStart : 0,
10874     
10875     viewMode : '',
10876     
10877     minViewMode : '',
10878     
10879     todayHighlight : false,
10880     
10881     todayBtn: false,
10882     
10883     language: 'en',
10884     
10885     keyboardNavigation: true,
10886     
10887     calendarWeeks: false,
10888     
10889     startDate: -Infinity,
10890     
10891     endDate: Infinity,
10892     
10893     daysOfWeekDisabled: [],
10894     
10895     _events: [],
10896     
10897     UTCDate: function()
10898     {
10899         return new Date(Date.UTC.apply(Date, arguments));
10900     },
10901     
10902     UTCToday: function()
10903     {
10904         var today = new Date();
10905         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10906     },
10907     
10908     getDate: function() {
10909             var d = this.getUTCDate();
10910             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10911     },
10912     
10913     getUTCDate: function() {
10914             return this.date;
10915     },
10916     
10917     setDate: function(d) {
10918             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10919     },
10920     
10921     setUTCDate: function(d) {
10922             this.date = d;
10923             this.setValue(this.formatDate(this.date));
10924     },
10925         
10926     onRender: function(ct, position)
10927     {
10928         
10929         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10930         
10931         this.language = this.language || 'en';
10932         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10933         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10934         
10935         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10936         this.format = this.format || 'm/d/y';
10937         this.isInline = false;
10938         this.isInput = true;
10939         this.component = this.el.select('.add-on', true).first() || false;
10940         this.component = (this.component && this.component.length === 0) ? false : this.component;
10941         this.hasInput = this.component && this.inputEL().length;
10942         
10943         if (typeof(this.minViewMode === 'string')) {
10944             switch (this.minViewMode) {
10945                 case 'months':
10946                     this.minViewMode = 1;
10947                     break;
10948                 case 'years':
10949                     this.minViewMode = 2;
10950                     break;
10951                 default:
10952                     this.minViewMode = 0;
10953                     break;
10954             }
10955         }
10956         
10957         if (typeof(this.viewMode === 'string')) {
10958             switch (this.viewMode) {
10959                 case 'months':
10960                     this.viewMode = 1;
10961                     break;
10962                 case 'years':
10963                     this.viewMode = 2;
10964                     break;
10965                 default:
10966                     this.viewMode = 0;
10967                     break;
10968             }
10969         }
10970                 
10971         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10972         
10973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10974         
10975         this.picker().on('mousedown', this.onMousedown, this);
10976         this.picker().on('click', this.onClick, this);
10977         
10978         this.picker().addClass('datepicker-dropdown');
10979         
10980         this.startViewMode = this.viewMode;
10981         
10982         
10983         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10984             if(!this.calendarWeeks){
10985                 v.remove();
10986                 return;
10987             };
10988             
10989             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10990             v.attr('colspan', function(i, val){
10991                 return parseInt(val) + 1;
10992             });
10993         })
10994                         
10995         
10996         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10997         
10998         this.setStartDate(this.startDate);
10999         this.setEndDate(this.endDate);
11000         
11001         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11002         
11003         this.fillDow();
11004         this.fillMonths();
11005         this.update();
11006         this.showMode();
11007         
11008         if(this.isInline) {
11009             this.show();
11010         }
11011     },
11012     
11013     picker : function()
11014     {
11015         return this.el.select('.datepicker', true).first();
11016     },
11017     
11018     fillDow: function()
11019     {
11020         var dowCnt = this.weekStart;
11021         
11022         var dow = {
11023             tag: 'tr',
11024             cn: [
11025                 
11026             ]
11027         };
11028         
11029         if(this.calendarWeeks){
11030             dow.cn.push({
11031                 tag: 'th',
11032                 cls: 'cw',
11033                 html: '&nbsp;'
11034             })
11035         }
11036         
11037         while (dowCnt < this.weekStart + 7) {
11038             dow.cn.push({
11039                 tag: 'th',
11040                 cls: 'dow',
11041                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11042             });
11043         }
11044         
11045         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11046     },
11047     
11048     fillMonths: function()
11049     {    
11050         var i = 0
11051         var months = this.picker().select('>.datepicker-months td', true).first();
11052         
11053         months.dom.innerHTML = '';
11054         
11055         while (i < 12) {
11056             var month = {
11057                 tag: 'span',
11058                 cls: 'month',
11059                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11060             }
11061             
11062             months.createChild(month);
11063         }
11064         
11065     },
11066     
11067     update: function(){
11068         
11069         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11070         
11071         if (this.date < this.startDate) {
11072             this.viewDate = new Date(this.startDate);
11073         } else if (this.date > this.endDate) {
11074             this.viewDate = new Date(this.endDate);
11075         } else {
11076             this.viewDate = new Date(this.date);
11077         }
11078         
11079         this.fill();
11080     },
11081     
11082     fill: function() {
11083         var d = new Date(this.viewDate),
11084                 year = d.getUTCFullYear(),
11085                 month = d.getUTCMonth(),
11086                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11087                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11088                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11089                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11090                 currentDate = this.date && this.date.valueOf(),
11091                 today = this.UTCToday();
11092         
11093         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11094         
11095 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11096         
11097 //        this.picker.select('>tfoot th.today').
11098 //                                              .text(dates[this.language].today)
11099 //                                              .toggle(this.todayBtn !== false);
11100     
11101         this.updateNavArrows();
11102         this.fillMonths();
11103                                                 
11104         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11105         
11106         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11107          
11108         prevMonth.setUTCDate(day);
11109         
11110         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11111         
11112         var nextMonth = new Date(prevMonth);
11113         
11114         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11115         
11116         nextMonth = nextMonth.valueOf();
11117         
11118         var fillMonths = false;
11119         
11120         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11121         
11122         while(prevMonth.valueOf() < nextMonth) {
11123             var clsName = '';
11124             
11125             if (prevMonth.getUTCDay() === this.weekStart) {
11126                 if(fillMonths){
11127                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11128                 }
11129                     
11130                 fillMonths = {
11131                     tag: 'tr',
11132                     cn: []
11133                 };
11134                 
11135                 if(this.calendarWeeks){
11136                     // ISO 8601: First week contains first thursday.
11137                     // ISO also states week starts on Monday, but we can be more abstract here.
11138                     var
11139                     // Start of current week: based on weekstart/current date
11140                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11141                     // Thursday of this week
11142                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11143                     // First Thursday of year, year from thursday
11144                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11145                     // Calendar week: ms between thursdays, div ms per day, div 7 days
11146                     calWeek =  (th - yth) / 864e5 / 7 + 1;
11147                     
11148                     fillMonths.cn.push({
11149                         tag: 'td',
11150                         cls: 'cw',
11151                         html: calWeek
11152                     });
11153                 }
11154             }
11155             
11156             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11157                 clsName += ' old';
11158             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11159                 clsName += ' new';
11160             }
11161             if (this.todayHighlight &&
11162                 prevMonth.getUTCFullYear() == today.getFullYear() &&
11163                 prevMonth.getUTCMonth() == today.getMonth() &&
11164                 prevMonth.getUTCDate() == today.getDate()) {
11165                 clsName += ' today';
11166             }
11167             
11168             if (currentDate && prevMonth.valueOf() === currentDate) {
11169                 clsName += ' active';
11170             }
11171             
11172             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11173                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11174                     clsName += ' disabled';
11175             }
11176             
11177             fillMonths.cn.push({
11178                 tag: 'td',
11179                 cls: 'day ' + clsName,
11180                 html: prevMonth.getDate()
11181             })
11182             
11183             prevMonth.setDate(prevMonth.getDate()+1);
11184         }
11185           
11186         var currentYear = this.date && this.date.getUTCFullYear();
11187         var currentMonth = this.date && this.date.getUTCMonth();
11188         
11189         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11190         
11191         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11192             v.removeClass('active');
11193             
11194             if(currentYear === year && k === currentMonth){
11195                 v.addClass('active');
11196             }
11197             
11198             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11199                 v.addClass('disabled');
11200             }
11201             
11202         });
11203         
11204         
11205         year = parseInt(year/10, 10) * 10;
11206         
11207         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11208         
11209         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11210         
11211         year -= 1;
11212         for (var i = -1; i < 11; i++) {
11213             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11214                 tag: 'span',
11215                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11216                 html: year
11217             })
11218             
11219             year += 1;
11220         }
11221     },
11222     
11223     showMode: function(dir) {
11224         if (dir) {
11225             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11226         }
11227         Roo.each(this.picker().select('>div',true).elements, function(v){
11228             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11229             v.hide();
11230         });
11231         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11232     },
11233     
11234     place: function()
11235     {
11236         if(this.isInline) return;
11237         
11238         this.picker().removeClass(['bottom', 'top']);
11239         
11240         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11241             /*
11242              * place to the top of element!
11243              *
11244              */
11245             
11246             this.picker().addClass('top');
11247             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11248             
11249             return;
11250         }
11251         
11252         this.picker().addClass('bottom');
11253         
11254         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11255     },
11256     
11257     parseDate : function(value){
11258         if(!value || value instanceof Date){
11259             return value;
11260         }
11261         var v = Date.parseDate(value, this.format);
11262         if (!v && this.useIso) {
11263             v = Date.parseDate(value, 'Y-m-d');
11264         }
11265         if(!v && this.altFormats){
11266             if(!this.altFormatsArray){
11267                 this.altFormatsArray = this.altFormats.split("|");
11268             }
11269             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11270                 v = Date.parseDate(value, this.altFormatsArray[i]);
11271             }
11272         }
11273         return v;
11274     },
11275     
11276     formatDate : function(date, fmt){
11277         return (!date || !(date instanceof Date)) ?
11278         date : date.dateFormat(fmt || this.format);
11279     },
11280     
11281     onFocus : function()
11282     {
11283         Roo.bootstrap.DateField.superclass.onFocus.call(this);
11284         this.show();
11285     },
11286     
11287     onBlur : function()
11288     {
11289         Roo.bootstrap.DateField.superclass.onBlur.call(this);
11290         this.hide();
11291     },
11292     
11293     show : function()
11294     {
11295         this.picker().show();
11296         this.update();
11297         this.place();
11298     },
11299     
11300     hide : function()
11301     {
11302         if(this.isInline) return;
11303         this.picker().hide();
11304         this.viewMode = this.startViewMode;
11305         this.showMode();
11306         
11307     },
11308     
11309     onMousedown: function(e){
11310         e.stopPropagation();
11311         e.preventDefault();
11312     },
11313     
11314     keyup: function(e){
11315         Roo.bootstrap.DateField.superclass.keyup.call(this);
11316         this.update();
11317         
11318     },
11319     
11320     fireKey: function(e){
11321         if (!this.picker().isVisible()){
11322             if (e.keyCode == 27) // allow escape to hide and re-show picker
11323                 this.show();
11324             return;
11325         }
11326         var dateChanged = false,
11327         dir, day, month,
11328         newDate, newViewDate;
11329         switch(e.keyCode){
11330             case 27: // escape
11331                 this.hide();
11332                 e.preventDefault();
11333                 break;
11334             case 37: // left
11335             case 39: // right
11336                 if (!this.keyboardNavigation) break;
11337                 dir = e.keyCode == 37 ? -1 : 1;
11338                 
11339                 if (e.ctrlKey){
11340                     newDate = this.moveYear(this.date, dir);
11341                     newViewDate = this.moveYear(this.viewDate, dir);
11342                 } else if (e.shiftKey){
11343                     newDate = this.moveMonth(this.date, dir);
11344                     newViewDate = this.moveMonth(this.viewDate, dir);
11345                 } else {
11346                     newDate = new Date(this.date);
11347                     newDate.setUTCDate(this.date.getUTCDate() + dir);
11348                     newViewDate = new Date(this.viewDate);
11349                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11350                 }
11351                 if (this.dateWithinRange(newDate)){
11352                     this.date = newDate;
11353                     this.viewDate = newViewDate;
11354                     this.setValue(this.formatDate(this.date));
11355                     this.update();
11356                     e.preventDefault();
11357                     dateChanged = true;
11358                 }
11359                 break;
11360             case 38: // up
11361             case 40: // down
11362                 if (!this.keyboardNavigation) break;
11363                 dir = e.keyCode == 38 ? -1 : 1;
11364                 if (e.ctrlKey){
11365                     newDate = this.moveYear(this.date, dir);
11366                     newViewDate = this.moveYear(this.viewDate, dir);
11367                 } else if (e.shiftKey){
11368                     newDate = this.moveMonth(this.date, dir);
11369                     newViewDate = this.moveMonth(this.viewDate, dir);
11370                 } else {
11371                     newDate = new Date(this.date);
11372                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11373                     newViewDate = new Date(this.viewDate);
11374                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11375                 }
11376                 if (this.dateWithinRange(newDate)){
11377                     this.date = newDate;
11378                     this.viewDate = newViewDate;
11379                     this.setValue(this.formatDate(this.date));
11380                     this.update();
11381                     e.preventDefault();
11382                     dateChanged = true;
11383                 }
11384                 break;
11385             case 13: // enter
11386                 this.setValue(this.formatDate(this.date));
11387                 this.hide();
11388                 e.preventDefault();
11389                 break;
11390             case 9: // tab
11391                 this.setValue(this.formatDate(this.date));
11392                 this.hide();
11393                 break;
11394         }
11395     },
11396     
11397     
11398     onClick: function(e) {
11399         e.stopPropagation();
11400         e.preventDefault();
11401         
11402         var target = e.getTarget();
11403         
11404         if(target.nodeName.toLowerCase() === 'i'){
11405             target = Roo.get(target).dom.parentNode;
11406         }
11407         
11408         var nodeName = target.nodeName;
11409         var className = target.className;
11410         var html = target.innerHTML;
11411         
11412         switch(nodeName.toLowerCase()) {
11413             case 'th':
11414                 switch(className) {
11415                     case 'switch':
11416                         this.showMode(1);
11417                         break;
11418                     case 'prev':
11419                     case 'next':
11420                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11421                         switch(this.viewMode){
11422                                 case 0:
11423                                         this.viewDate = this.moveMonth(this.viewDate, dir);
11424                                         break;
11425                                 case 1:
11426                                 case 2:
11427                                         this.viewDate = this.moveYear(this.viewDate, dir);
11428                                         break;
11429                         }
11430                         this.fill();
11431                         break;
11432                     case 'today':
11433                         var date = new Date();
11434                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11435                         this.fill()
11436                         this.setValue(this.formatDate(this.date));
11437                         this.hide();
11438                         break;
11439                 }
11440                 break;
11441             case 'span':
11442                 if (className.indexOf('disabled') === -1) {
11443                     this.viewDate.setUTCDate(1);
11444                     if (className.indexOf('month') !== -1) {
11445                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11446                     } else {
11447                         var year = parseInt(html, 10) || 0;
11448                         this.viewDate.setUTCFullYear(year);
11449                         
11450                     }
11451                     this.showMode(-1);
11452                     this.fill();
11453                 }
11454                 break;
11455                 
11456             case 'td':
11457                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11458                     var day = parseInt(html, 10) || 1;
11459                     var year = this.viewDate.getUTCFullYear(),
11460                         month = this.viewDate.getUTCMonth();
11461
11462                     if (className.indexOf('old') !== -1) {
11463                         if(month === 0 ){
11464                             month = 11;
11465                             year -= 1;
11466                         }else{
11467                             month -= 1;
11468                         }
11469                     } else if (className.indexOf('new') !== -1) {
11470                         if (month == 11) {
11471                             month = 0;
11472                             year += 1;
11473                         } else {
11474                             month += 1;
11475                         }
11476                     }
11477                     this.date = this.UTCDate(year, month, day,0,0,0,0);
11478                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11479                     this.fill();
11480                     this.setValue(this.formatDate(this.date));
11481                     this.hide();
11482                 }
11483                 break;
11484         }
11485     },
11486     
11487     setStartDate: function(startDate){
11488         this.startDate = startDate || -Infinity;
11489         if (this.startDate !== -Infinity) {
11490             this.startDate = this.parseDate(this.startDate);
11491         }
11492         this.update();
11493         this.updateNavArrows();
11494     },
11495
11496     setEndDate: function(endDate){
11497         this.endDate = endDate || Infinity;
11498         if (this.endDate !== Infinity) {
11499             this.endDate = this.parseDate(this.endDate);
11500         }
11501         this.update();
11502         this.updateNavArrows();
11503     },
11504     
11505     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11506         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11507         if (typeof(this.daysOfWeekDisabled) !== 'object') {
11508             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11509         }
11510         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11511             return parseInt(d, 10);
11512         });
11513         this.update();
11514         this.updateNavArrows();
11515     },
11516     
11517     updateNavArrows: function() {
11518         var d = new Date(this.viewDate),
11519         year = d.getUTCFullYear(),
11520         month = d.getUTCMonth();
11521         
11522         Roo.each(this.picker().select('.prev', true).elements, function(v){
11523             v.show();
11524             switch (this.viewMode) {
11525                 case 0:
11526
11527                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11528                         v.hide();
11529                     }
11530                     break;
11531                 case 1:
11532                 case 2:
11533                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11534                         v.hide();
11535                     }
11536                     break;
11537             }
11538         });
11539         
11540         Roo.each(this.picker().select('.next', true).elements, function(v){
11541             v.show();
11542             switch (this.viewMode) {
11543                 case 0:
11544
11545                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11546                         v.hide();
11547                     }
11548                     break;
11549                 case 1:
11550                 case 2:
11551                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11552                         v.hide();
11553                     }
11554                     break;
11555             }
11556         })
11557     },
11558     
11559     moveMonth: function(date, dir){
11560         if (!dir) return date;
11561         var new_date = new Date(date.valueOf()),
11562         day = new_date.getUTCDate(),
11563         month = new_date.getUTCMonth(),
11564         mag = Math.abs(dir),
11565         new_month, test;
11566         dir = dir > 0 ? 1 : -1;
11567         if (mag == 1){
11568             test = dir == -1
11569             // If going back one month, make sure month is not current month
11570             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11571             ? function(){
11572                 return new_date.getUTCMonth() == month;
11573             }
11574             // If going forward one month, make sure month is as expected
11575             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11576             : function(){
11577                 return new_date.getUTCMonth() != new_month;
11578             };
11579             new_month = month + dir;
11580             new_date.setUTCMonth(new_month);
11581             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11582             if (new_month < 0 || new_month > 11)
11583                 new_month = (new_month + 12) % 12;
11584         } else {
11585             // For magnitudes >1, move one month at a time...
11586             for (var i=0; i<mag; i++)
11587                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11588                 new_date = this.moveMonth(new_date, dir);
11589             // ...then reset the day, keeping it in the new month
11590             new_month = new_date.getUTCMonth();
11591             new_date.setUTCDate(day);
11592             test = function(){
11593                 return new_month != new_date.getUTCMonth();
11594             };
11595         }
11596         // Common date-resetting loop -- if date is beyond end of month, make it
11597         // end of month
11598         while (test()){
11599             new_date.setUTCDate(--day);
11600             new_date.setUTCMonth(new_month);
11601         }
11602         return new_date;
11603     },
11604
11605     moveYear: function(date, dir){
11606         return this.moveMonth(date, dir*12);
11607     },
11608
11609     dateWithinRange: function(date){
11610         return date >= this.startDate && date <= this.endDate;
11611     },
11612
11613     
11614     remove: function() {
11615         this.picker().remove();
11616     }
11617    
11618 });
11619
11620 Roo.apply(Roo.bootstrap.DateField,  {
11621     
11622     head : {
11623         tag: 'thead',
11624         cn: [
11625         {
11626             tag: 'tr',
11627             cn: [
11628             {
11629                 tag: 'th',
11630                 cls: 'prev',
11631                 html: '<i class="icon-arrow-left"/>'
11632             },
11633             {
11634                 tag: 'th',
11635                 cls: 'switch',
11636                 colspan: '5'
11637             },
11638             {
11639                 tag: 'th',
11640                 cls: 'next',
11641                 html: '<i class="icon-arrow-right"/>'
11642             }
11643
11644             ]
11645         }
11646         ]
11647     },
11648     
11649     content : {
11650         tag: 'tbody',
11651         cn: [
11652         {
11653             tag: 'tr',
11654             cn: [
11655             {
11656                 tag: 'td',
11657                 colspan: '7'
11658             }
11659             ]
11660         }
11661         ]
11662     },
11663     
11664     footer : {
11665         tag: 'tfoot',
11666         cn: [
11667         {
11668             tag: 'tr',
11669             cn: [
11670             {
11671                 tag: 'th',
11672                 colspan: '7',
11673                 cls: 'today'
11674             }
11675                     
11676             ]
11677         }
11678         ]
11679     },
11680     
11681     dates:{
11682         en: {
11683             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11684             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11685             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11686             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11687             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11688             today: "Today"
11689         }
11690     },
11691     
11692     modes: [
11693     {
11694         clsName: 'days',
11695         navFnc: 'Month',
11696         navStep: 1
11697     },
11698     {
11699         clsName: 'months',
11700         navFnc: 'FullYear',
11701         navStep: 1
11702     },
11703     {
11704         clsName: 'years',
11705         navFnc: 'FullYear',
11706         navStep: 10
11707     }]
11708 });
11709
11710 Roo.apply(Roo.bootstrap.DateField,  {
11711   
11712     template : {
11713         tag: 'div',
11714         cls: 'datepicker dropdown-menu',
11715         cn: [
11716         {
11717             tag: 'div',
11718             cls: 'datepicker-days',
11719             cn: [
11720             {
11721                 tag: 'table',
11722                 cls: 'table-condensed',
11723                 cn:[
11724                 Roo.bootstrap.DateField.head,
11725                 {
11726                     tag: 'tbody'
11727                 },
11728                 Roo.bootstrap.DateField.footer
11729                 ]
11730             }
11731             ]
11732         },
11733         {
11734             tag: 'div',
11735             cls: 'datepicker-months',
11736             cn: [
11737             {
11738                 tag: 'table',
11739                 cls: 'table-condensed',
11740                 cn:[
11741                 Roo.bootstrap.DateField.head,
11742                 Roo.bootstrap.DateField.content,
11743                 Roo.bootstrap.DateField.footer
11744                 ]
11745             }
11746             ]
11747         },
11748         {
11749             tag: 'div',
11750             cls: 'datepicker-years',
11751             cn: [
11752             {
11753                 tag: 'table',
11754                 cls: 'table-condensed',
11755                 cn:[
11756                 Roo.bootstrap.DateField.head,
11757                 Roo.bootstrap.DateField.content,
11758                 Roo.bootstrap.DateField.footer
11759                 ]
11760             }
11761             ]
11762         }
11763         ]
11764     }
11765 });
11766
11767  
11768
11769  /*
11770  * - LGPL
11771  *
11772  * TimeField
11773  * 
11774  */
11775
11776 /**
11777  * @class Roo.bootstrap.TimeField
11778  * @extends Roo.bootstrap.Input
11779  * Bootstrap DateField class
11780  * 
11781  * 
11782  * @constructor
11783  * Create a new TimeField
11784  * @param {Object} config The config object
11785  */
11786
11787 Roo.bootstrap.TimeField = function(config){
11788     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11789 };
11790
11791 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
11792     
11793     /**
11794      * @cfg {String} format
11795      * The default time format string which can be overriden for localization support.  The format must be
11796      * valid according to {@link Date#parseDate} (defaults to 'H:i').
11797      */
11798     format : "H:i",
11799        
11800     onRender: function(ct, position)
11801     {
11802         
11803         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11804                 
11805         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11806         
11807         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11808         
11809         this.pop = this.picker().select('>.datepicker-time',true).first();
11810         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
11811         
11812         this.picker().on('mousedown', this.onMousedown, this);
11813         this.picker().on('click', this.onClick, this);
11814         
11815         this.picker().addClass('datepicker-dropdown');
11816     
11817         this.fillTime();
11818         this.update();
11819             
11820         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11821         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11822         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11823         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11824         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11825         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11826
11827     },
11828     
11829     onClick: function(e) {
11830         e.stopPropagation();
11831         e.preventDefault();
11832     },
11833     
11834     picker : function()
11835     {
11836         return this.el.select('.datepicker', true).first();
11837     },
11838     
11839     fillTime: function()
11840     {    
11841         var time = this.pop.select('tbody', true).first();
11842         
11843         time.dom.innerHTML = '';
11844         
11845         time.createChild({
11846             tag: 'tr',
11847             cn: [
11848                 {
11849                     tag: 'td',
11850                     cn: [
11851                         {
11852                             tag: 'a',
11853                             href: '#',
11854                             cls: 'btn',
11855                             cn: [
11856                                 {
11857                                     tag: 'span',
11858                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
11859                                 }
11860                             ]
11861                         } 
11862                     ]
11863                 },
11864                 {
11865                     tag: 'td',
11866                     cls: 'separator'
11867                 },
11868                 {
11869                     tag: 'td',
11870                     cn: [
11871                         {
11872                             tag: 'a',
11873                             href: '#',
11874                             cls: 'btn',
11875                             cn: [
11876                                 {
11877                                     tag: 'span',
11878                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
11879                                 }
11880                             ]
11881                         }
11882                     ]
11883                 },
11884                 {
11885                     tag: 'td',
11886                     cls: 'separator'
11887                 }
11888             ]
11889         });
11890         
11891         time.createChild({
11892             tag: 'tr',
11893             cn: [
11894                 {
11895                     tag: 'td',
11896                     cn: [
11897                         {
11898                             tag: 'span',
11899                             cls: 'timepicker-hour',
11900                             html: '00'
11901                         }  
11902                     ]
11903                 },
11904                 {
11905                     tag: 'td',
11906                     cls: 'separator',
11907                     html: ':'
11908                 },
11909                 {
11910                     tag: 'td',
11911                     cn: [
11912                         {
11913                             tag: 'span',
11914                             cls: 'timepicker-minute',
11915                             html: '00'
11916                         }  
11917                     ]
11918                 },
11919                 {
11920                     tag: 'td',
11921                     cls: 'separator'
11922                 },
11923                 {
11924                     tag: 'td',
11925                     cn: [
11926                         {
11927                             tag: 'button',
11928                             type: 'button',
11929                             cls: 'btn btn-primary period',
11930                             html: 'AM'
11931                             
11932                         }
11933                     ]
11934                 }
11935             ]
11936         });
11937         
11938         time.createChild({
11939             tag: 'tr',
11940             cn: [
11941                 {
11942                     tag: 'td',
11943                     cn: [
11944                         {
11945                             tag: 'a',
11946                             href: '#',
11947                             cls: 'btn',
11948                             cn: [
11949                                 {
11950                                     tag: 'span',
11951                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
11952                                 }
11953                             ]
11954                         }
11955                     ]
11956                 },
11957                 {
11958                     tag: 'td',
11959                     cls: 'separator'
11960                 },
11961                 {
11962                     tag: 'td',
11963                     cn: [
11964                         {
11965                             tag: 'a',
11966                             href: '#',
11967                             cls: 'btn',
11968                             cn: [
11969                                 {
11970                                     tag: 'span',
11971                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
11972                                 }
11973                             ]
11974                         }
11975                     ]
11976                 },
11977                 {
11978                     tag: 'td',
11979                     cls: 'separator'
11980                 }
11981             ]
11982         });
11983         
11984     },
11985     
11986     update: function()
11987     {
11988         
11989         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
11990         
11991         this.fill();
11992     },
11993     
11994     fill: function() 
11995     {
11996         var hours = this.time.getHours();
11997         var minutes = this.time.getMinutes();
11998         var period = 'AM';
11999         
12000         if(hours > 11){
12001             period = 'PM';
12002         }
12003         
12004         if(hours == 0){
12005             hours = 12;
12006         }
12007         
12008         
12009         if(hours > 12){
12010             hours = hours - 12;
12011         }
12012         
12013         if(hours < 10){
12014             hours = '0' + hours;
12015         }
12016         
12017         if(minutes < 10){
12018             minutes = '0' + minutes;
12019         }
12020         
12021         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12022         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12023         this.pop.select('button', true).first().dom.innerHTML = period;
12024         
12025     },
12026     
12027     place: function()
12028     {   
12029         this.picker().removeClass(['bottom', 'top']);
12030         
12031         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12032             /*
12033              * place to the top of element!
12034              *
12035              */
12036             
12037             this.picker().addClass('top');
12038             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12039             
12040             return;
12041         }
12042         
12043         this.picker().addClass('bottom');
12044         
12045         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12046     },
12047   
12048     onFocus : function()
12049     {
12050         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12051         this.show();
12052     },
12053     
12054     onBlur : function()
12055     {
12056         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12057         this.hide();
12058     },
12059     
12060     show : function()
12061     {
12062         this.picker().show();
12063         this.pop.show();
12064         this.update();
12065         this.place();
12066     },
12067     
12068     hide : function()
12069     {
12070         this.picker().hide();
12071         this.pop.hide();
12072         
12073     },
12074     
12075     setTime : function()
12076     {
12077         this.hide();
12078         this.setValue(this.time.format(this.format));
12079     },
12080     
12081     onMousedown: function(e){
12082         e.stopPropagation();
12083         e.preventDefault();
12084     },
12085     
12086     onIncrementHours: function()
12087     {
12088         Roo.log('onIncrementHours');
12089         this.time = this.time.add(Date.HOUR, 1);
12090         this.update();
12091         
12092     },
12093     
12094     onDecrementHours: function()
12095     {
12096         Roo.log('onDecrementHours');
12097         this.time = this.time.add(Date.HOUR, -1);
12098         this.update();
12099     },
12100     
12101     onIncrementMinutes: function()
12102     {
12103         Roo.log('onIncrementMinutes');
12104         this.time = this.time.add(Date.MINUTE, 1);
12105         this.update();
12106     },
12107     
12108     onDecrementMinutes: function()
12109     {
12110         Roo.log('onDecrementMinutes');
12111         this.time = this.time.add(Date.MINUTE, -1);
12112         this.update();
12113     },
12114     
12115     onTogglePeriod: function()
12116     {
12117         Roo.log('onTogglePeriod');
12118         this.time = this.time.add(Date.HOUR, 12);
12119         this.update();
12120     }
12121     
12122    
12123 });
12124
12125 Roo.apply(Roo.bootstrap.TimeField,  {
12126     
12127     content : {
12128         tag: 'tbody',
12129         cn: [
12130             {
12131                 tag: 'tr',
12132                 cn: [
12133                 {
12134                     tag: 'td',
12135                     colspan: '7'
12136                 }
12137                 ]
12138             }
12139         ]
12140     },
12141     
12142     footer : {
12143         tag: 'tfoot',
12144         cn: [
12145             {
12146                 tag: 'tr',
12147                 cn: [
12148                 {
12149                     tag: 'th',
12150                     colspan: '7',
12151                     cls: '',
12152                     cn: [
12153                         {
12154                             tag: 'button',
12155                             cls: 'btn btn-info ok',
12156                             html: 'OK'
12157                         }
12158                     ]
12159                 }
12160
12161                 ]
12162             }
12163         ]
12164     }
12165 });
12166
12167 Roo.apply(Roo.bootstrap.TimeField,  {
12168   
12169     template : {
12170         tag: 'div',
12171         cls: 'datepicker dropdown-menu',
12172         cn: [
12173             {
12174                 tag: 'div',
12175                 cls: 'datepicker-time',
12176                 cn: [
12177                 {
12178                     tag: 'table',
12179                     cls: 'table-condensed',
12180                     cn:[
12181                     Roo.bootstrap.TimeField.content,
12182                     Roo.bootstrap.TimeField.footer
12183                     ]
12184                 }
12185                 ]
12186             }
12187         ]
12188     }
12189 });
12190
12191  
12192
12193  /*
12194  * - LGPL
12195  *
12196  * CheckBox
12197  * 
12198  */
12199
12200 /**
12201  * @class Roo.bootstrap.CheckBox
12202  * @extends Roo.bootstrap.Input
12203  * Bootstrap CheckBox class
12204  * 
12205  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12206  * @cfg {String} boxLabel The text that appears beside the checkbox
12207  * @cfg {Boolean} checked initnal the element
12208  * 
12209  * @constructor
12210  * Create a new CheckBox
12211  * @param {Object} config The config object
12212  */
12213
12214 Roo.bootstrap.CheckBox = function(config){
12215     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12216    
12217         this.addEvents({
12218             /**
12219             * @event check
12220             * Fires when the element is checked or unchecked.
12221             * @param {Roo.bootstrap.CheckBox} this This input
12222             * @param {Boolean} checked The new checked value
12223             */
12224            check : true
12225         });
12226 };
12227
12228 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
12229     
12230     inputType: 'checkbox',
12231     value: 1,
12232     valueOff: 0,
12233     boxLabel: false,
12234     checked: false,
12235     
12236     getAutoCreate : function()
12237     {
12238         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12239         
12240         var id = Roo.id();
12241         
12242         var cfg = {};
12243         
12244         cfg.cls = 'form-group' //input-group
12245         
12246         var input =  {
12247             tag: 'input',
12248             id : id,
12249             type : this.inputType,
12250             value : (!this.checked) ? this.valueOff : this.value,
12251             cls : 'form-box',
12252             placeholder : this.placeholder || ''
12253             
12254         };
12255         
12256         if (this.disabled) {
12257             input.disabled=true;
12258         }
12259         
12260         if(this.checked){
12261             input.checked = this.checked;
12262         }
12263         
12264         if (this.name) {
12265             input.name = this.name;
12266         }
12267         
12268         if (this.size) {
12269             input.cls += ' input-' + this.size;
12270         }
12271         
12272         var settings=this;
12273         ['xs','sm','md','lg'].map(function(size){
12274             if (settings[size]) {
12275                 cfg.cls += ' col-' + size + '-' + settings[size];
12276             }
12277         });
12278         
12279         var inputblock = input;
12280         
12281         if (this.before || this.after) {
12282             
12283             inputblock = {
12284                 cls : 'input-group',
12285                 cn :  [] 
12286             };
12287             if (this.before) {
12288                 inputblock.cn.push({
12289                     tag :'span',
12290                     cls : 'input-group-addon',
12291                     html : this.before
12292                 });
12293             }
12294             inputblock.cn.push(input);
12295             if (this.after) {
12296                 inputblock.cn.push({
12297                     tag :'span',
12298                     cls : 'input-group-addon',
12299                     html : this.after
12300                 });
12301             }
12302             
12303         };
12304         
12305         if (align ==='left' && this.fieldLabel.length) {
12306                 Roo.log("left and has label");
12307                 cfg.cn = [
12308                     
12309                     {
12310                         tag: 'label',
12311                         'for' :  id,
12312                         cls : 'control-label col-md-' + this.labelWidth,
12313                         html : this.fieldLabel
12314                         
12315                     },
12316                     {
12317                         cls : "col-md-" + (12 - this.labelWidth), 
12318                         cn: [
12319                             inputblock
12320                         ]
12321                     }
12322                     
12323                 ];
12324         } else if ( this.fieldLabel.length) {
12325                 Roo.log(" label");
12326                  cfg.cn = [
12327                    
12328                     {
12329                         tag: 'label',
12330                         'for': id,
12331                         cls: 'control-label box-input-label',
12332                         //cls : 'input-group-addon',
12333                         html : this.fieldLabel
12334                         
12335                     },
12336                     
12337                     inputblock
12338                     
12339                 ];
12340
12341         } else {
12342             
12343                    Roo.log(" no label && no align");
12344                 cfg.cn = [
12345                     
12346                         inputblock
12347                     
12348                 ];
12349                 
12350                 
12351         };
12352         
12353         if(this.boxLabel){
12354             cfg.cn.push({
12355                 tag: 'span',
12356                 'for': id,
12357                 cls: 'box-label',
12358                 html: this.boxLabel
12359             })
12360         }
12361         
12362         return cfg;
12363         
12364     },
12365     
12366     /**
12367      * return the real input element.
12368      */
12369     inputEl: function ()
12370     {
12371         return this.el.select('input.form-box',true).first();
12372     },
12373     
12374     initEvents : function()
12375     {
12376         Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12377         
12378         this.inputEl().on('click', this.onClick,  this);
12379         
12380     },
12381     
12382     onClick : function()
12383     {   
12384         this.setChecked(!this.checked);
12385     },
12386     
12387     setChecked : function(state,suppressEvent)
12388     {
12389         this.checked = state;
12390         
12391         if(suppressEvent !== true){
12392             this.fireEvent('check', this, state);
12393         }
12394         
12395         this.inputEl().dom.value = state ? this.value : this.valueOff;
12396         
12397     }
12398 });
12399
12400  
12401 /*
12402  * - LGPL
12403  *
12404  * Radio
12405  * 
12406  */
12407
12408 /**
12409  * @class Roo.bootstrap.Radio
12410  * @extends Roo.bootstrap.CheckBox
12411  * Bootstrap Radio class
12412
12413  * @constructor
12414  * Create a new Radio
12415  * @param {Object} config The config object
12416  */
12417
12418 Roo.bootstrap.Radio = function(config){
12419     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12420    
12421 };
12422
12423 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
12424     
12425     inputType: 'radio',
12426     
12427     getAutoCreate : function()
12428     {
12429         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12430         
12431         var id = Roo.id();
12432         
12433         var cfg = {};
12434         
12435         cfg.cls = 'form-group' //input-group
12436         
12437         var input =  {
12438             tag: 'input',
12439             id : id,
12440             type : this.inputType,
12441             value : (!this.checked) ? this.valueOff : this.value,
12442             cls : 'form-box',
12443             placeholder : this.placeholder || ''
12444             
12445         };
12446         
12447         if (this.disabled) {
12448             input.disabled=true;
12449         }
12450         
12451         if(this.checked){
12452             input.checked = this.checked;
12453         }
12454         
12455         if (this.name) {
12456             input.name = this.name;
12457         }
12458         
12459         if (this.size) {
12460             input.cls += ' input-' + this.size;
12461         }
12462         
12463         var settings=this;
12464         ['xs','sm','md','lg'].map(function(size){
12465             if (settings[size]) {
12466                 cfg.cls += ' col-' + size + '-' + settings[size];
12467             }
12468         });
12469         
12470         var inputblock = input;
12471         
12472         if (this.before || this.after) {
12473             
12474             inputblock = {
12475                 cls : 'input-group',
12476                 cn :  [] 
12477             };
12478             if (this.before) {
12479                 inputblock.cn.push({
12480                     tag :'span',
12481                     cls : 'input-group-addon',
12482                     html : this.before
12483                 });
12484             }
12485             inputblock.cn.push(input);
12486             if (this.after) {
12487                 inputblock.cn.push({
12488                     tag :'span',
12489                     cls : 'input-group-addon',
12490                     html : this.after
12491                 });
12492             }
12493             
12494         };
12495         
12496         if (align ==='left' && this.fieldLabel.length) {
12497                 Roo.log("left and has label");
12498                 cfg.cn = [
12499                     
12500                     {
12501                         tag: 'label',
12502                         'for' :  id,
12503                         cls : 'control-label col-md-' + this.labelWidth,
12504                         html : this.fieldLabel
12505                         
12506                     },
12507                     {
12508                         cls : "col-md-" + (12 - this.labelWidth), 
12509                         cn: [
12510                             inputblock
12511                         ]
12512                     }
12513                     
12514                 ];
12515         } else if ( this.fieldLabel.length) {
12516                 Roo.log(" label");
12517                  cfg.cn = [
12518                    
12519                     {
12520                         tag: 'label',
12521                         'for': id,
12522                         cls: 'control-label box-input-label',
12523                         //cls : 'input-group-addon',
12524                         html : this.fieldLabel
12525                         
12526                     },
12527                     
12528                     inputblock
12529                     
12530                 ];
12531
12532         } else {
12533             
12534                    Roo.log(" no label && no align");
12535                 cfg.cn = [
12536                     
12537                         inputblock
12538                     
12539                 ];
12540                 
12541                 
12542         };
12543         
12544         if(this.boxLabel){
12545             cfg.cn.push({
12546                 tag: 'span',
12547                 'for': id,
12548                 cls: 'box-label',
12549                 html: this.boxLabel
12550             })
12551         }
12552         
12553         return cfg;
12554         
12555     },
12556     
12557     onClick : function()
12558     {   
12559         this.setChecked(true);
12560     },
12561     
12562     setChecked : function(state,suppressEvent)
12563     {
12564         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12565             v.checked = false;
12566         });
12567         
12568         this.checked = state;
12569         
12570         if(suppressEvent !== true){
12571             this.fireEvent('check', this, state);
12572         }
12573         
12574         this.inputEl().dom.value = state ? this.value : this.valueOff;
12575         
12576     },
12577     
12578     getGroupValue : function()
12579     {
12580         if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
12581             return '';
12582         }
12583         
12584         return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
12585     },
12586     
12587     /**
12588      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12589      * @return {Mixed} value The field value
12590      */
12591     getValue : function(){
12592         return this.getGroupValue();
12593     }
12594     
12595 });
12596
12597  
12598 //<script type="text/javascript">
12599
12600 /*
12601  * Based  Ext JS Library 1.1.1
12602  * Copyright(c) 2006-2007, Ext JS, LLC.
12603  * LGPL
12604  *
12605  */
12606  
12607 /**
12608  * @class Roo.HtmlEditorCore
12609  * @extends Roo.Component
12610  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12611  *
12612  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12613  */
12614
12615 Roo.HtmlEditorCore = function(config){
12616     
12617     
12618     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12619     this.addEvents({
12620         /**
12621          * @event initialize
12622          * Fires when the editor is fully initialized (including the iframe)
12623          * @param {Roo.HtmlEditorCore} this
12624          */
12625         initialize: true,
12626         /**
12627          * @event activate
12628          * Fires when the editor is first receives the focus. Any insertion must wait
12629          * until after this event.
12630          * @param {Roo.HtmlEditorCore} this
12631          */
12632         activate: true,
12633          /**
12634          * @event beforesync
12635          * Fires before the textarea is updated with content from the editor iframe. Return false
12636          * to cancel the sync.
12637          * @param {Roo.HtmlEditorCore} this
12638          * @param {String} html
12639          */
12640         beforesync: true,
12641          /**
12642          * @event beforepush
12643          * Fires before the iframe editor is updated with content from the textarea. Return false
12644          * to cancel the push.
12645          * @param {Roo.HtmlEditorCore} this
12646          * @param {String} html
12647          */
12648         beforepush: true,
12649          /**
12650          * @event sync
12651          * Fires when the textarea is updated with content from the editor iframe.
12652          * @param {Roo.HtmlEditorCore} this
12653          * @param {String} html
12654          */
12655         sync: true,
12656          /**
12657          * @event push
12658          * Fires when the iframe editor is updated with content from the textarea.
12659          * @param {Roo.HtmlEditorCore} this
12660          * @param {String} html
12661          */
12662         push: true,
12663         
12664         /**
12665          * @event editorevent
12666          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12667          * @param {Roo.HtmlEditorCore} this
12668          */
12669         editorevent: true
12670     });
12671      
12672 };
12673
12674
12675 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
12676
12677
12678      /**
12679      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
12680      */
12681     
12682     owner : false,
12683     
12684      /**
12685      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
12686      *                        Roo.resizable.
12687      */
12688     resizable : false,
12689      /**
12690      * @cfg {Number} height (in pixels)
12691      */   
12692     height: 300,
12693    /**
12694      * @cfg {Number} width (in pixels)
12695      */   
12696     width: 500,
12697     
12698     /**
12699      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12700      * 
12701      */
12702     stylesheets: false,
12703     
12704     // id of frame..
12705     frameId: false,
12706     
12707     // private properties
12708     validationEvent : false,
12709     deferHeight: true,
12710     initialized : false,
12711     activated : false,
12712     sourceEditMode : false,
12713     onFocus : Roo.emptyFn,
12714     iframePad:3,
12715     hideMode:'offsets',
12716     
12717      
12718     
12719
12720     /**
12721      * Protected method that will not generally be called directly. It
12722      * is called when the editor initializes the iframe with HTML contents. Override this method if you
12723      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12724      */
12725     getDocMarkup : function(){
12726         // body styles..
12727         var st = '';
12728         Roo.log(this.stylesheets);
12729         
12730         // inherit styels from page...?? 
12731         if (this.stylesheets === false) {
12732             
12733             Roo.get(document.head).select('style').each(function(node) {
12734                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12735             });
12736             
12737             Roo.get(document.head).select('link').each(function(node) { 
12738                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12739             });
12740             
12741         } else if (!this.stylesheets.length) {
12742                 // simple..
12743                 st = '<style type="text/css">' +
12744                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12745                    '</style>';
12746         } else {
12747             Roo.each(this.stylesheets, function(s) {
12748                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12749             });
12750             
12751         }
12752         
12753         st +=  '<style type="text/css">' +
12754             'IMG { cursor: pointer } ' +
12755         '</style>';
12756
12757         
12758         return '<html><head>' + st  +
12759             //<style type="text/css">' +
12760             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12761             //'</style>' +
12762             ' </head><body class="roo-htmleditor-body"></body></html>';
12763     },
12764
12765     // private
12766     onRender : function(ct, position)
12767     {
12768         var _t = this;
12769         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12770         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12771         
12772         
12773         this.el.dom.style.border = '0 none';
12774         this.el.dom.setAttribute('tabIndex', -1);
12775         this.el.addClass('x-hidden hide');
12776         
12777         
12778         
12779         if(Roo.isIE){ // fix IE 1px bogus margin
12780             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12781         }
12782        
12783         
12784         this.frameId = Roo.id();
12785         
12786          
12787         
12788         var iframe = this.owner.wrap.createChild({
12789             tag: 'iframe',
12790             cls: 'form-control', // bootstrap..
12791             id: this.frameId,
12792             name: this.frameId,
12793             frameBorder : 'no',
12794             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
12795         }, this.el
12796         );
12797         
12798         
12799         this.iframe = iframe.dom;
12800
12801          this.assignDocWin();
12802         
12803         this.doc.designMode = 'on';
12804        
12805         this.doc.open();
12806         this.doc.write(this.getDocMarkup());
12807         this.doc.close();
12808
12809         
12810         var task = { // must defer to wait for browser to be ready
12811             run : function(){
12812                 //console.log("run task?" + this.doc.readyState);
12813                 this.assignDocWin();
12814                 if(this.doc.body || this.doc.readyState == 'complete'){
12815                     try {
12816                         this.doc.designMode="on";
12817                     } catch (e) {
12818                         return;
12819                     }
12820                     Roo.TaskMgr.stop(task);
12821                     this.initEditor.defer(10, this);
12822                 }
12823             },
12824             interval : 10,
12825             duration: 10000,
12826             scope: this
12827         };
12828         Roo.TaskMgr.start(task);
12829
12830         
12831          
12832     },
12833
12834     // private
12835     onResize : function(w, h)
12836     {
12837          Roo.log('resize: ' +w + ',' + h );
12838         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
12839         if(!this.iframe){
12840             return;
12841         }
12842         if(typeof w == 'number'){
12843             
12844             this.iframe.style.width = w + 'px';
12845         }
12846         if(typeof h == 'number'){
12847             
12848             this.iframe.style.height = h + 'px';
12849             if(this.doc){
12850                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
12851             }
12852         }
12853         
12854     },
12855
12856     /**
12857      * Toggles the editor between standard and source edit mode.
12858      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
12859      */
12860     toggleSourceEdit : function(sourceEditMode){
12861         
12862         this.sourceEditMode = sourceEditMode === true;
12863         
12864         if(this.sourceEditMode){
12865  
12866             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
12867             
12868         }else{
12869             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
12870             //this.iframe.className = '';
12871             this.deferFocus();
12872         }
12873         //this.setSize(this.owner.wrap.getSize());
12874         //this.fireEvent('editmodechange', this, this.sourceEditMode);
12875     },
12876
12877     
12878   
12879
12880     /**
12881      * Protected method that will not generally be called directly. If you need/want
12882      * custom HTML cleanup, this is the method you should override.
12883      * @param {String} html The HTML to be cleaned
12884      * return {String} The cleaned HTML
12885      */
12886     cleanHtml : function(html){
12887         html = String(html);
12888         if(html.length > 5){
12889             if(Roo.isSafari){ // strip safari nonsense
12890                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
12891             }
12892         }
12893         if(html == '&nbsp;'){
12894             html = '';
12895         }
12896         return html;
12897     },
12898
12899     /**
12900      * HTML Editor -> Textarea
12901      * Protected method that will not generally be called directly. Syncs the contents
12902      * of the editor iframe with the textarea.
12903      */
12904     syncValue : function(){
12905         if(this.initialized){
12906             var bd = (this.doc.body || this.doc.documentElement);
12907             //this.cleanUpPaste(); -- this is done else where and causes havoc..
12908             var html = bd.innerHTML;
12909             if(Roo.isSafari){
12910                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
12911                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
12912                 if(m && m[1]){
12913                     html = '<div style="'+m[0]+'">' + html + '</div>';
12914                 }
12915             }
12916             html = this.cleanHtml(html);
12917             // fix up the special chars.. normaly like back quotes in word...
12918             // however we do not want to do this with chinese..
12919             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
12920                 var cc = b.charCodeAt();
12921                 if (
12922                     (cc >= 0x4E00 && cc < 0xA000 ) ||
12923                     (cc >= 0x3400 && cc < 0x4E00 ) ||
12924                     (cc >= 0xf900 && cc < 0xfb00 )
12925                 ) {
12926                         return b;
12927                 }
12928                 return "&#"+cc+";" 
12929             });
12930             if(this.owner.fireEvent('beforesync', this, html) !== false){
12931                 this.el.dom.value = html;
12932                 this.owner.fireEvent('sync', this, html);
12933             }
12934         }
12935     },
12936
12937     /**
12938      * Protected method that will not generally be called directly. Pushes the value of the textarea
12939      * into the iframe editor.
12940      */
12941     pushValue : function(){
12942         if(this.initialized){
12943             var v = this.el.dom.value;
12944             
12945             if(v.length < 1){
12946                 v = '&#160;';
12947             }
12948             
12949             if(this.owner.fireEvent('beforepush', this, v) !== false){
12950                 var d = (this.doc.body || this.doc.documentElement);
12951                 d.innerHTML = v;
12952                 this.cleanUpPaste();
12953                 this.el.dom.value = d.innerHTML;
12954                 this.owner.fireEvent('push', this, v);
12955             }
12956         }
12957     },
12958
12959     // private
12960     deferFocus : function(){
12961         this.focus.defer(10, this);
12962     },
12963
12964     // doc'ed in Field
12965     focus : function(){
12966         if(this.win && !this.sourceEditMode){
12967             this.win.focus();
12968         }else{
12969             this.el.focus();
12970         }
12971     },
12972     
12973     assignDocWin: function()
12974     {
12975         var iframe = this.iframe;
12976         
12977          if(Roo.isIE){
12978             this.doc = iframe.contentWindow.document;
12979             this.win = iframe.contentWindow;
12980         } else {
12981             if (!Roo.get(this.frameId)) {
12982                 return;
12983             }
12984             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
12985             this.win = Roo.get(this.frameId).dom.contentWindow;
12986         }
12987     },
12988     
12989     // private
12990     initEditor : function(){
12991         //console.log("INIT EDITOR");
12992         this.assignDocWin();
12993         
12994         
12995         
12996         this.doc.designMode="on";
12997         this.doc.open();
12998         this.doc.write(this.getDocMarkup());
12999         this.doc.close();
13000         
13001         var dbody = (this.doc.body || this.doc.documentElement);
13002         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13003         // this copies styles from the containing element into thsi one..
13004         // not sure why we need all of this..
13005         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13006         ss['background-attachment'] = 'fixed'; // w3c
13007         dbody.bgProperties = 'fixed'; // ie
13008         Roo.DomHelper.applyStyles(dbody, ss);
13009         Roo.EventManager.on(this.doc, {
13010             //'mousedown': this.onEditorEvent,
13011             'mouseup': this.onEditorEvent,
13012             'dblclick': this.onEditorEvent,
13013             'click': this.onEditorEvent,
13014             'keyup': this.onEditorEvent,
13015             buffer:100,
13016             scope: this
13017         });
13018         if(Roo.isGecko){
13019             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13020         }
13021         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13022             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13023         }
13024         this.initialized = true;
13025
13026         this.owner.fireEvent('initialize', this);
13027         this.pushValue();
13028     },
13029
13030     // private
13031     onDestroy : function(){
13032         
13033         
13034         
13035         if(this.rendered){
13036             
13037             //for (var i =0; i < this.toolbars.length;i++) {
13038             //    // fixme - ask toolbars for heights?
13039             //    this.toolbars[i].onDestroy();
13040            // }
13041             
13042             //this.wrap.dom.innerHTML = '';
13043             //this.wrap.remove();
13044         }
13045     },
13046
13047     // private
13048     onFirstFocus : function(){
13049         
13050         this.assignDocWin();
13051         
13052         
13053         this.activated = true;
13054          
13055     
13056         if(Roo.isGecko){ // prevent silly gecko errors
13057             this.win.focus();
13058             var s = this.win.getSelection();
13059             if(!s.focusNode || s.focusNode.nodeType != 3){
13060                 var r = s.getRangeAt(0);
13061                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13062                 r.collapse(true);
13063                 this.deferFocus();
13064             }
13065             try{
13066                 this.execCmd('useCSS', true);
13067                 this.execCmd('styleWithCSS', false);
13068             }catch(e){}
13069         }
13070         this.owner.fireEvent('activate', this);
13071     },
13072
13073     // private
13074     adjustFont: function(btn){
13075         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13076         //if(Roo.isSafari){ // safari
13077         //    adjust *= 2;
13078        // }
13079         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13080         if(Roo.isSafari){ // safari
13081             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13082             v =  (v < 10) ? 10 : v;
13083             v =  (v > 48) ? 48 : v;
13084             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13085             
13086         }
13087         
13088         
13089         v = Math.max(1, v+adjust);
13090         
13091         this.execCmd('FontSize', v  );
13092     },
13093
13094     onEditorEvent : function(e){
13095         this.owner.fireEvent('editorevent', this, e);
13096       //  this.updateToolbar();
13097         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13098     },
13099
13100     insertTag : function(tg)
13101     {
13102         // could be a bit smarter... -> wrap the current selected tRoo..
13103         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13104             
13105             range = this.createRange(this.getSelection());
13106             var wrappingNode = this.doc.createElement(tg.toLowerCase());
13107             wrappingNode.appendChild(range.extractContents());
13108             range.insertNode(wrappingNode);
13109
13110             return;
13111             
13112             
13113             
13114         }
13115         this.execCmd("formatblock",   tg);
13116         
13117     },
13118     
13119     insertText : function(txt)
13120     {
13121         
13122         
13123         var range = this.createRange();
13124         range.deleteContents();
13125                //alert(Sender.getAttribute('label'));
13126                
13127         range.insertNode(this.doc.createTextNode(txt));
13128     } ,
13129     
13130      
13131
13132     /**
13133      * Executes a Midas editor command on the editor document and performs necessary focus and
13134      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13135      * @param {String} cmd The Midas command
13136      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13137      */
13138     relayCmd : function(cmd, value){
13139         this.win.focus();
13140         this.execCmd(cmd, value);
13141         this.owner.fireEvent('editorevent', this);
13142         //this.updateToolbar();
13143         this.owner.deferFocus();
13144     },
13145
13146     /**
13147      * Executes a Midas editor command directly on the editor document.
13148      * For visual commands, you should use {@link #relayCmd} instead.
13149      * <b>This should only be called after the editor is initialized.</b>
13150      * @param {String} cmd The Midas command
13151      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13152      */
13153     execCmd : function(cmd, value){
13154         this.doc.execCommand(cmd, false, value === undefined ? null : value);
13155         this.syncValue();
13156     },
13157  
13158  
13159    
13160     /**
13161      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13162      * to insert tRoo.
13163      * @param {String} text | dom node.. 
13164      */
13165     insertAtCursor : function(text)
13166     {
13167         
13168         
13169         
13170         if(!this.activated){
13171             return;
13172         }
13173         /*
13174         if(Roo.isIE){
13175             this.win.focus();
13176             var r = this.doc.selection.createRange();
13177             if(r){
13178                 r.collapse(true);
13179                 r.pasteHTML(text);
13180                 this.syncValue();
13181                 this.deferFocus();
13182             
13183             }
13184             return;
13185         }
13186         */
13187         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13188             this.win.focus();
13189             
13190             
13191             // from jquery ui (MIT licenced)
13192             var range, node;
13193             var win = this.win;
13194             
13195             if (win.getSelection && win.getSelection().getRangeAt) {
13196                 range = win.getSelection().getRangeAt(0);
13197                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13198                 range.insertNode(node);
13199             } else if (win.document.selection && win.document.selection.createRange) {
13200                 // no firefox support
13201                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13202                 win.document.selection.createRange().pasteHTML(txt);
13203             } else {
13204                 // no firefox support
13205                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13206                 this.execCmd('InsertHTML', txt);
13207             } 
13208             
13209             this.syncValue();
13210             
13211             this.deferFocus();
13212         }
13213     },
13214  // private
13215     mozKeyPress : function(e){
13216         if(e.ctrlKey){
13217             var c = e.getCharCode(), cmd;
13218           
13219             if(c > 0){
13220                 c = String.fromCharCode(c).toLowerCase();
13221                 switch(c){
13222                     case 'b':
13223                         cmd = 'bold';
13224                         break;
13225                     case 'i':
13226                         cmd = 'italic';
13227                         break;
13228                     
13229                     case 'u':
13230                         cmd = 'underline';
13231                         break;
13232                     
13233                     case 'v':
13234                         this.cleanUpPaste.defer(100, this);
13235                         return;
13236                         
13237                 }
13238                 if(cmd){
13239                     this.win.focus();
13240                     this.execCmd(cmd);
13241                     this.deferFocus();
13242                     e.preventDefault();
13243                 }
13244                 
13245             }
13246         }
13247     },
13248
13249     // private
13250     fixKeys : function(){ // load time branching for fastest keydown performance
13251         if(Roo.isIE){
13252             return function(e){
13253                 var k = e.getKey(), r;
13254                 if(k == e.TAB){
13255                     e.stopEvent();
13256                     r = this.doc.selection.createRange();
13257                     if(r){
13258                         r.collapse(true);
13259                         r.pasteHTML('&#160;&#160;&#160;&#160;');
13260                         this.deferFocus();
13261                     }
13262                     return;
13263                 }
13264                 
13265                 if(k == e.ENTER){
13266                     r = this.doc.selection.createRange();
13267                     if(r){
13268                         var target = r.parentElement();
13269                         if(!target || target.tagName.toLowerCase() != 'li'){
13270                             e.stopEvent();
13271                             r.pasteHTML('<br />');
13272                             r.collapse(false);
13273                             r.select();
13274                         }
13275                     }
13276                 }
13277                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13278                     this.cleanUpPaste.defer(100, this);
13279                     return;
13280                 }
13281                 
13282                 
13283             };
13284         }else if(Roo.isOpera){
13285             return function(e){
13286                 var k = e.getKey();
13287                 if(k == e.TAB){
13288                     e.stopEvent();
13289                     this.win.focus();
13290                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
13291                     this.deferFocus();
13292                 }
13293                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13294                     this.cleanUpPaste.defer(100, this);
13295                     return;
13296                 }
13297                 
13298             };
13299         }else if(Roo.isSafari){
13300             return function(e){
13301                 var k = e.getKey();
13302                 
13303                 if(k == e.TAB){
13304                     e.stopEvent();
13305                     this.execCmd('InsertText','\t');
13306                     this.deferFocus();
13307                     return;
13308                 }
13309                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13310                     this.cleanUpPaste.defer(100, this);
13311                     return;
13312                 }
13313                 
13314              };
13315         }
13316     }(),
13317     
13318     getAllAncestors: function()
13319     {
13320         var p = this.getSelectedNode();
13321         var a = [];
13322         if (!p) {
13323             a.push(p); // push blank onto stack..
13324             p = this.getParentElement();
13325         }
13326         
13327         
13328         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13329             a.push(p);
13330             p = p.parentNode;
13331         }
13332         a.push(this.doc.body);
13333         return a;
13334     },
13335     lastSel : false,
13336     lastSelNode : false,
13337     
13338     
13339     getSelection : function() 
13340     {
13341         this.assignDocWin();
13342         return Roo.isIE ? this.doc.selection : this.win.getSelection();
13343     },
13344     
13345     getSelectedNode: function() 
13346     {
13347         // this may only work on Gecko!!!
13348         
13349         // should we cache this!!!!
13350         
13351         
13352         
13353          
13354         var range = this.createRange(this.getSelection()).cloneRange();
13355         
13356         if (Roo.isIE) {
13357             var parent = range.parentElement();
13358             while (true) {
13359                 var testRange = range.duplicate();
13360                 testRange.moveToElementText(parent);
13361                 if (testRange.inRange(range)) {
13362                     break;
13363                 }
13364                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13365                     break;
13366                 }
13367                 parent = parent.parentElement;
13368             }
13369             return parent;
13370         }
13371         
13372         // is ancestor a text element.
13373         var ac =  range.commonAncestorContainer;
13374         if (ac.nodeType == 3) {
13375             ac = ac.parentNode;
13376         }
13377         
13378         var ar = ac.childNodes;
13379          
13380         var nodes = [];
13381         var other_nodes = [];
13382         var has_other_nodes = false;
13383         for (var i=0;i<ar.length;i++) {
13384             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
13385                 continue;
13386             }
13387             // fullly contained node.
13388             
13389             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13390                 nodes.push(ar[i]);
13391                 continue;
13392             }
13393             
13394             // probably selected..
13395             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13396                 other_nodes.push(ar[i]);
13397                 continue;
13398             }
13399             // outer..
13400             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
13401                 continue;
13402             }
13403             
13404             
13405             has_other_nodes = true;
13406         }
13407         if (!nodes.length && other_nodes.length) {
13408             nodes= other_nodes;
13409         }
13410         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13411             return false;
13412         }
13413         
13414         return nodes[0];
13415     },
13416     createRange: function(sel)
13417     {
13418         // this has strange effects when using with 
13419         // top toolbar - not sure if it's a great idea.
13420         //this.editor.contentWindow.focus();
13421         if (typeof sel != "undefined") {
13422             try {
13423                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13424             } catch(e) {
13425                 return this.doc.createRange();
13426             }
13427         } else {
13428             return this.doc.createRange();
13429         }
13430     },
13431     getParentElement: function()
13432     {
13433         
13434         this.assignDocWin();
13435         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13436         
13437         var range = this.createRange(sel);
13438          
13439         try {
13440             var p = range.commonAncestorContainer;
13441             while (p.nodeType == 3) { // text node
13442                 p = p.parentNode;
13443             }
13444             return p;
13445         } catch (e) {
13446             return null;
13447         }
13448     
13449     },
13450     /***
13451      *
13452      * Range intersection.. the hard stuff...
13453      *  '-1' = before
13454      *  '0' = hits..
13455      *  '1' = after.
13456      *         [ -- selected range --- ]
13457      *   [fail]                        [fail]
13458      *
13459      *    basically..
13460      *      if end is before start or  hits it. fail.
13461      *      if start is after end or hits it fail.
13462      *
13463      *   if either hits (but other is outside. - then it's not 
13464      *   
13465      *    
13466      **/
13467     
13468     
13469     // @see http://www.thismuchiknow.co.uk/?p=64.
13470     rangeIntersectsNode : function(range, node)
13471     {
13472         var nodeRange = node.ownerDocument.createRange();
13473         try {
13474             nodeRange.selectNode(node);
13475         } catch (e) {
13476             nodeRange.selectNodeContents(node);
13477         }
13478     
13479         var rangeStartRange = range.cloneRange();
13480         rangeStartRange.collapse(true);
13481     
13482         var rangeEndRange = range.cloneRange();
13483         rangeEndRange.collapse(false);
13484     
13485         var nodeStartRange = nodeRange.cloneRange();
13486         nodeStartRange.collapse(true);
13487     
13488         var nodeEndRange = nodeRange.cloneRange();
13489         nodeEndRange.collapse(false);
13490     
13491         return rangeStartRange.compareBoundaryPoints(
13492                  Range.START_TO_START, nodeEndRange) == -1 &&
13493                rangeEndRange.compareBoundaryPoints(
13494                  Range.START_TO_START, nodeStartRange) == 1;
13495         
13496          
13497     },
13498     rangeCompareNode : function(range, node)
13499     {
13500         var nodeRange = node.ownerDocument.createRange();
13501         try {
13502             nodeRange.selectNode(node);
13503         } catch (e) {
13504             nodeRange.selectNodeContents(node);
13505         }
13506         
13507         
13508         range.collapse(true);
13509     
13510         nodeRange.collapse(true);
13511      
13512         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13513         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
13514          
13515         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13516         
13517         var nodeIsBefore   =  ss == 1;
13518         var nodeIsAfter    = ee == -1;
13519         
13520         if (nodeIsBefore && nodeIsAfter)
13521             return 0; // outer
13522         if (!nodeIsBefore && nodeIsAfter)
13523             return 1; //right trailed.
13524         
13525         if (nodeIsBefore && !nodeIsAfter)
13526             return 2;  // left trailed.
13527         // fully contined.
13528         return 3;
13529     },
13530
13531     // private? - in a new class?
13532     cleanUpPaste :  function()
13533     {
13534         // cleans up the whole document..
13535          Roo.log('cleanuppaste');
13536         this.cleanUpChildren(this.doc.body);
13537         var clean = this.cleanWordChars(this.doc.body.innerHTML);
13538         if (clean != this.doc.body.innerHTML) {
13539             this.doc.body.innerHTML = clean;
13540         }
13541         
13542     },
13543     
13544     cleanWordChars : function(input) {// change the chars to hex code
13545         var he = Roo.HtmlEditorCore;
13546         
13547         var output = input;
13548         Roo.each(he.swapCodes, function(sw) { 
13549             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13550             
13551             output = output.replace(swapper, sw[1]);
13552         });
13553         
13554         return output;
13555     },
13556     
13557     
13558     cleanUpChildren : function (n)
13559     {
13560         if (!n.childNodes.length) {
13561             return;
13562         }
13563         for (var i = n.childNodes.length-1; i > -1 ; i--) {
13564            this.cleanUpChild(n.childNodes[i]);
13565         }
13566     },
13567     
13568     
13569         
13570     
13571     cleanUpChild : function (node)
13572     {
13573         var ed = this;
13574         //console.log(node);
13575         if (node.nodeName == "#text") {
13576             // clean up silly Windows -- stuff?
13577             return; 
13578         }
13579         if (node.nodeName == "#comment") {
13580             node.parentNode.removeChild(node);
13581             // clean up silly Windows -- stuff?
13582             return; 
13583         }
13584         
13585         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13586             // remove node.
13587             node.parentNode.removeChild(node);
13588             return;
13589             
13590         }
13591         
13592         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13593         
13594         // remove <a name=....> as rendering on yahoo mailer is borked with this.
13595         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13596         
13597         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13598         //    remove_keep_children = true;
13599         //}
13600         
13601         if (remove_keep_children) {
13602             this.cleanUpChildren(node);
13603             // inserts everything just before this node...
13604             while (node.childNodes.length) {
13605                 var cn = node.childNodes[0];
13606                 node.removeChild(cn);
13607                 node.parentNode.insertBefore(cn, node);
13608             }
13609             node.parentNode.removeChild(node);
13610             return;
13611         }
13612         
13613         if (!node.attributes || !node.attributes.length) {
13614             this.cleanUpChildren(node);
13615             return;
13616         }
13617         
13618         function cleanAttr(n,v)
13619         {
13620             
13621             if (v.match(/^\./) || v.match(/^\//)) {
13622                 return;
13623             }
13624             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13625                 return;
13626             }
13627             if (v.match(/^#/)) {
13628                 return;
13629             }
13630 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13631             node.removeAttribute(n);
13632             
13633         }
13634         
13635         function cleanStyle(n,v)
13636         {
13637             if (v.match(/expression/)) { //XSS?? should we even bother..
13638                 node.removeAttribute(n);
13639                 return;
13640             }
13641             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13642             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13643             
13644             
13645             var parts = v.split(/;/);
13646             var clean = [];
13647             
13648             Roo.each(parts, function(p) {
13649                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13650                 if (!p.length) {
13651                     return true;
13652                 }
13653                 var l = p.split(':').shift().replace(/\s+/g,'');
13654                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13655                 
13656                 
13657                 if ( cblack.indexOf(l) > -1) {
13658 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13659                     //node.removeAttribute(n);
13660                     return true;
13661                 }
13662                 //Roo.log()
13663                 // only allow 'c whitelisted system attributes'
13664                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
13665 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13666                     //node.removeAttribute(n);
13667                     return true;
13668                 }
13669                 
13670                 
13671                  
13672                 
13673                 clean.push(p);
13674                 return true;
13675             });
13676             if (clean.length) { 
13677                 node.setAttribute(n, clean.join(';'));
13678             } else {
13679                 node.removeAttribute(n);
13680             }
13681             
13682         }
13683         
13684         
13685         for (var i = node.attributes.length-1; i > -1 ; i--) {
13686             var a = node.attributes[i];
13687             //console.log(a);
13688             
13689             if (a.name.toLowerCase().substr(0,2)=='on')  {
13690                 node.removeAttribute(a.name);
13691                 continue;
13692             }
13693             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13694                 node.removeAttribute(a.name);
13695                 continue;
13696             }
13697             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13698                 cleanAttr(a.name,a.value); // fixme..
13699                 continue;
13700             }
13701             if (a.name == 'style') {
13702                 cleanStyle(a.name,a.value);
13703                 continue;
13704             }
13705             /// clean up MS crap..
13706             // tecnically this should be a list of valid class'es..
13707             
13708             
13709             if (a.name == 'class') {
13710                 if (a.value.match(/^Mso/)) {
13711                     node.className = '';
13712                 }
13713                 
13714                 if (a.value.match(/body/)) {
13715                     node.className = '';
13716                 }
13717                 continue;
13718             }
13719             
13720             // style cleanup!?
13721             // class cleanup?
13722             
13723         }
13724         
13725         
13726         this.cleanUpChildren(node);
13727         
13728         
13729     }
13730     
13731     
13732     // hide stuff that is not compatible
13733     /**
13734      * @event blur
13735      * @hide
13736      */
13737     /**
13738      * @event change
13739      * @hide
13740      */
13741     /**
13742      * @event focus
13743      * @hide
13744      */
13745     /**
13746      * @event specialkey
13747      * @hide
13748      */
13749     /**
13750      * @cfg {String} fieldClass @hide
13751      */
13752     /**
13753      * @cfg {String} focusClass @hide
13754      */
13755     /**
13756      * @cfg {String} autoCreate @hide
13757      */
13758     /**
13759      * @cfg {String} inputType @hide
13760      */
13761     /**
13762      * @cfg {String} invalidClass @hide
13763      */
13764     /**
13765      * @cfg {String} invalidText @hide
13766      */
13767     /**
13768      * @cfg {String} msgFx @hide
13769      */
13770     /**
13771      * @cfg {String} validateOnBlur @hide
13772      */
13773 });
13774
13775 Roo.HtmlEditorCore.white = [
13776         'area', 'br', 'img', 'input', 'hr', 'wbr',
13777         
13778        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
13779        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
13780        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
13781        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
13782        'table',   'ul',         'xmp', 
13783        
13784        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
13785       'thead',   'tr', 
13786      
13787       'dir', 'menu', 'ol', 'ul', 'dl',
13788        
13789       'embed',  'object'
13790 ];
13791
13792
13793 Roo.HtmlEditorCore.black = [
13794     //    'embed',  'object', // enable - backend responsiblity to clean thiese
13795         'applet', // 
13796         'base',   'basefont', 'bgsound', 'blink',  'body', 
13797         'frame',  'frameset', 'head',    'html',   'ilayer', 
13798         'iframe', 'layer',  'link',     'meta',    'object',   
13799         'script', 'style' ,'title',  'xml' // clean later..
13800 ];
13801 Roo.HtmlEditorCore.clean = [
13802     'script', 'style', 'title', 'xml'
13803 ];
13804 Roo.HtmlEditorCore.remove = [
13805     'font'
13806 ];
13807 // attributes..
13808
13809 Roo.HtmlEditorCore.ablack = [
13810     'on'
13811 ];
13812     
13813 Roo.HtmlEditorCore.aclean = [ 
13814     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
13815 ];
13816
13817 // protocols..
13818 Roo.HtmlEditorCore.pwhite= [
13819         'http',  'https',  'mailto'
13820 ];
13821
13822 // white listed style attributes.
13823 Roo.HtmlEditorCore.cwhite= [
13824       //  'text-align', /// default is to allow most things..
13825       
13826          
13827 //        'font-size'//??
13828 ];
13829
13830 // black listed style attributes.
13831 Roo.HtmlEditorCore.cblack= [
13832       //  'font-size' -- this can be set by the project 
13833 ];
13834
13835
13836 Roo.HtmlEditorCore.swapCodes   =[ 
13837     [    8211, "--" ], 
13838     [    8212, "--" ], 
13839     [    8216,  "'" ],  
13840     [    8217, "'" ],  
13841     [    8220, '"' ],  
13842     [    8221, '"' ],  
13843     [    8226, "*" ],  
13844     [    8230, "..." ]
13845 ]; 
13846
13847     /*
13848  * - LGPL
13849  *
13850  * HtmlEditor
13851  * 
13852  */
13853
13854 /**
13855  * @class Roo.bootstrap.HtmlEditor
13856  * @extends Roo.bootstrap.Component
13857  * Bootstrap HtmlEditor class
13858
13859  * @constructor
13860  * Create a new HtmlEditor
13861  * @param {Object} config The config object
13862  */
13863
13864 Roo.bootstrap.HtmlEditor = function(config){
13865     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
13866     if (!this.toolbars) {
13867         this.toolbars = [];
13868     }
13869     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
13870     this.addEvents({
13871             /**
13872              * @event initialize
13873              * Fires when the editor is fully initialized (including the iframe)
13874              * @param {HtmlEditor} this
13875              */
13876             initialize: true,
13877             /**
13878              * @event activate
13879              * Fires when the editor is first receives the focus. Any insertion must wait
13880              * until after this event.
13881              * @param {HtmlEditor} this
13882              */
13883             activate: true,
13884              /**
13885              * @event beforesync
13886              * Fires before the textarea is updated with content from the editor iframe. Return false
13887              * to cancel the sync.
13888              * @param {HtmlEditor} this
13889              * @param {String} html
13890              */
13891             beforesync: true,
13892              /**
13893              * @event beforepush
13894              * Fires before the iframe editor is updated with content from the textarea. Return false
13895              * to cancel the push.
13896              * @param {HtmlEditor} this
13897              * @param {String} html
13898              */
13899             beforepush: true,
13900              /**
13901              * @event sync
13902              * Fires when the textarea is updated with content from the editor iframe.
13903              * @param {HtmlEditor} this
13904              * @param {String} html
13905              */
13906             sync: true,
13907              /**
13908              * @event push
13909              * Fires when the iframe editor is updated with content from the textarea.
13910              * @param {HtmlEditor} this
13911              * @param {String} html
13912              */
13913             push: true,
13914              /**
13915              * @event editmodechange
13916              * Fires when the editor switches edit modes
13917              * @param {HtmlEditor} this
13918              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
13919              */
13920             editmodechange: true,
13921             /**
13922              * @event editorevent
13923              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13924              * @param {HtmlEditor} this
13925              */
13926             editorevent: true,
13927             /**
13928              * @event firstfocus
13929              * Fires when on first focus - needed by toolbars..
13930              * @param {HtmlEditor} this
13931              */
13932             firstfocus: true,
13933             /**
13934              * @event autosave
13935              * Auto save the htmlEditor value as a file into Events
13936              * @param {HtmlEditor} this
13937              */
13938             autosave: true,
13939             /**
13940              * @event savedpreview
13941              * preview the saved version of htmlEditor
13942              * @param {HtmlEditor} this
13943              */
13944             savedpreview: true
13945         });
13946 };
13947
13948
13949 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
13950     
13951     
13952       /**
13953      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
13954      */
13955     toolbars : false,
13956    
13957      /**
13958      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
13959      *                        Roo.resizable.
13960      */
13961     resizable : false,
13962      /**
13963      * @cfg {Number} height (in pixels)
13964      */   
13965     height: 300,
13966    /**
13967      * @cfg {Number} width (in pixels)
13968      */   
13969     width: 500,
13970     
13971     /**
13972      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13973      * 
13974      */
13975     stylesheets: false,
13976     
13977     // id of frame..
13978     frameId: false,
13979     
13980     // private properties
13981     validationEvent : false,
13982     deferHeight: true,
13983     initialized : false,
13984     activated : false,
13985     
13986     onFocus : Roo.emptyFn,
13987     iframePad:3,
13988     hideMode:'offsets',
13989     
13990     
13991     tbContainer : false,
13992     
13993     toolbarContainer :function() {
13994         return this.wrap.select('.x-html-editor-tb',true).first();
13995     },
13996
13997     /**
13998      * Protected method that will not generally be called directly. It
13999      * is called when the editor creates its toolbar. Override this method if you need to
14000      * add custom toolbar buttons.
14001      * @param {HtmlEditor} editor
14002      */
14003     createToolbar : function(){
14004         
14005         this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14006         this.toolbars[0].render(this.toolbarContainer());
14007          
14008         Roo.log("create toolbars");
14009         return;
14010         if (!editor.toolbars || !editor.toolbars.length) {
14011             editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14012         }
14013         
14014         for (var i =0 ; i < editor.toolbars.length;i++) {
14015             editor.toolbars[i] = Roo.factory(
14016                     typeof(editor.toolbars[i]) == 'string' ?
14017                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
14018                 Roo.bootstrap.HtmlEditor);
14019             editor.toolbars[i].init(editor);
14020         }
14021          
14022         
14023     },
14024
14025      
14026     // private
14027     onRender : function(ct, position)
14028     {
14029        // Roo.log("Call onRender: " + this.xtype);
14030         
14031         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14032       
14033         this.wrap = this.inputEl().wrap({
14034             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14035         });
14036         
14037         this.editorcore.onRender(ct, position);
14038          
14039         if (this.resizable) {
14040             this.resizeEl = new Roo.Resizable(this.wrap, {
14041                 pinned : true,
14042                 wrap: true,
14043                 dynamic : true,
14044                 minHeight : this.height,
14045                 height: this.height,
14046                 handles : this.resizable,
14047                 width: this.width,
14048                 listeners : {
14049                     resize : function(r, w, h) {
14050                         _t.onResize(w,h); // -something
14051                     }
14052                 }
14053             });
14054             
14055         }
14056         this.createToolbar(this);
14057        
14058         
14059         if(!this.width){
14060             this.setSize(this.wrap.getSize());
14061         }
14062         if (this.resizeEl) {
14063             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14064             // should trigger onReize..
14065         }
14066         
14067 //        if(this.autosave && this.w){
14068 //            this.autoSaveFn = setInterval(this.autosave, 1000);
14069 //        }
14070     },
14071
14072     // private
14073     onResize : function(w, h)
14074     {
14075         Roo.log('resize: ' +w + ',' + h );
14076         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14077         var ew = false;
14078         var eh = false;
14079         
14080         if(this.inputEl() ){
14081             if(typeof w == 'number'){
14082                 var aw = w - this.wrap.getFrameWidth('lr');
14083                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14084                 ew = aw;
14085             }
14086             if(typeof h == 'number'){
14087                  var tbh = -11;  // fixme it needs to tool bar size!
14088                 for (var i =0; i < this.toolbars.length;i++) {
14089                     // fixme - ask toolbars for heights?
14090                     tbh += this.toolbars[i].el.getHeight();
14091                     //if (this.toolbars[i].footer) {
14092                     //    tbh += this.toolbars[i].footer.el.getHeight();
14093                     //}
14094                 }
14095               
14096                 
14097                 
14098                 
14099                 
14100                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14101                 ah -= 5; // knock a few pixes off for look..
14102                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14103                 var eh = ah;
14104             }
14105         }
14106         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14107         this.editorcore.onResize(ew,eh);
14108         
14109     },
14110
14111     /**
14112      * Toggles the editor between standard and source edit mode.
14113      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14114      */
14115     toggleSourceEdit : function(sourceEditMode)
14116     {
14117         this.editorcore.toggleSourceEdit(sourceEditMode);
14118         
14119         if(this.editorcore.sourceEditMode){
14120             Roo.log('editor - showing textarea');
14121             
14122 //            Roo.log('in');
14123 //            Roo.log(this.syncValue());
14124             this.editorcore.syncValue();
14125             this.inputEl().removeClass('hide');
14126             this.inputEl().dom.removeAttribute('tabIndex');
14127             this.inputEl().focus();
14128         }else{
14129             Roo.log('editor - hiding textarea');
14130 //            Roo.log('out')
14131 //            Roo.log(this.pushValue()); 
14132             this.editorcore.pushValue();
14133             
14134             this.inputEl().addClass('hide');
14135             this.inputEl().dom.setAttribute('tabIndex', -1);
14136             //this.deferFocus();
14137         }
14138          
14139         this.setSize(this.wrap.getSize());
14140         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14141     },
14142  
14143     // private (for BoxComponent)
14144     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14145
14146     // private (for BoxComponent)
14147     getResizeEl : function(){
14148         return this.wrap;
14149     },
14150
14151     // private (for BoxComponent)
14152     getPositionEl : function(){
14153         return this.wrap;
14154     },
14155
14156     // private
14157     initEvents : function(){
14158         this.originalValue = this.getValue();
14159     },
14160
14161     /**
14162      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14163      * @method
14164      */
14165     markInvalid : Roo.emptyFn,
14166     /**
14167      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14168      * @method
14169      */
14170     clearInvalid : Roo.emptyFn,
14171
14172     setValue : function(v){
14173         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14174         this.editorcore.pushValue();
14175     },
14176
14177      
14178     // private
14179     deferFocus : function(){
14180         this.focus.defer(10, this);
14181     },
14182
14183     // doc'ed in Field
14184     focus : function(){
14185         this.editorcore.focus();
14186         
14187     },
14188       
14189
14190     // private
14191     onDestroy : function(){
14192         
14193         
14194         
14195         if(this.rendered){
14196             
14197             for (var i =0; i < this.toolbars.length;i++) {
14198                 // fixme - ask toolbars for heights?
14199                 this.toolbars[i].onDestroy();
14200             }
14201             
14202             this.wrap.dom.innerHTML = '';
14203             this.wrap.remove();
14204         }
14205     },
14206
14207     // private
14208     onFirstFocus : function(){
14209         //Roo.log("onFirstFocus");
14210         this.editorcore.onFirstFocus();
14211          for (var i =0; i < this.toolbars.length;i++) {
14212             this.toolbars[i].onFirstFocus();
14213         }
14214         
14215     },
14216     
14217     // private
14218     syncValue : function()
14219     {
14220         this.editorcore.syncValue();
14221     }
14222      
14223     
14224     // hide stuff that is not compatible
14225     /**
14226      * @event blur
14227      * @hide
14228      */
14229     /**
14230      * @event change
14231      * @hide
14232      */
14233     /**
14234      * @event focus
14235      * @hide
14236      */
14237     /**
14238      * @event specialkey
14239      * @hide
14240      */
14241     /**
14242      * @cfg {String} fieldClass @hide
14243      */
14244     /**
14245      * @cfg {String} focusClass @hide
14246      */
14247     /**
14248      * @cfg {String} autoCreate @hide
14249      */
14250     /**
14251      * @cfg {String} inputType @hide
14252      */
14253     /**
14254      * @cfg {String} invalidClass @hide
14255      */
14256     /**
14257      * @cfg {String} invalidText @hide
14258      */
14259     /**
14260      * @cfg {String} msgFx @hide
14261      */
14262     /**
14263      * @cfg {String} validateOnBlur @hide
14264      */
14265 });
14266  
14267     
14268    
14269    
14270    
14271     
14272 /**
14273  * @class Roo.bootstrap.Table.AbstractSelectionModel
14274  * @extends Roo.util.Observable
14275  * Abstract base class for grid SelectionModels.  It provides the interface that should be
14276  * implemented by descendant classes.  This class should not be directly instantiated.
14277  * @constructor
14278  */
14279 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14280     this.locked = false;
14281     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14282 };
14283
14284
14285 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
14286     /** @ignore Called by the grid automatically. Do not call directly. */
14287     init : function(grid){
14288         this.grid = grid;
14289         this.initEvents();
14290     },
14291
14292     /**
14293      * Locks the selections.
14294      */
14295     lock : function(){
14296         this.locked = true;
14297     },
14298
14299     /**
14300      * Unlocks the selections.
14301      */
14302     unlock : function(){
14303         this.locked = false;
14304     },
14305
14306     /**
14307      * Returns true if the selections are locked.
14308      * @return {Boolean}
14309      */
14310     isLocked : function(){
14311         return this.locked;
14312     }
14313 });
14314 /**
14315  * @class Roo.bootstrap.Table.ColumnModel
14316  * @extends Roo.util.Observable
14317  * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14318  * the columns in the table.
14319  
14320  * @constructor
14321  * @param {Object} config An Array of column config objects. See this class's
14322  * config objects for details.
14323 */
14324 Roo.bootstrap.Table.ColumnModel = function(config){
14325         /**
14326      * The config passed into the constructor
14327      */
14328     this.config = config;
14329     this.lookup = {};
14330
14331     // if no id, create one
14332     // if the column does not have a dataIndex mapping,
14333     // map it to the order it is in the config
14334     for(var i = 0, len = config.length; i < len; i++){
14335         var c = config[i];
14336         if(typeof c.dataIndex == "undefined"){
14337             c.dataIndex = i;
14338         }
14339         if(typeof c.renderer == "string"){
14340             c.renderer = Roo.util.Format[c.renderer];
14341         }
14342         if(typeof c.id == "undefined"){
14343             c.id = Roo.id();
14344         }
14345 //        if(c.editor && c.editor.xtype){
14346 //            c.editor  = Roo.factory(c.editor, Roo.grid);
14347 //        }
14348 //        if(c.editor && c.editor.isFormField){
14349 //            c.editor = new Roo.grid.GridEditor(c.editor);
14350 //        }
14351
14352         this.lookup[c.id] = c;
14353     }
14354
14355     /**
14356      * The width of columns which have no width specified (defaults to 100)
14357      * @type Number
14358      */
14359     this.defaultWidth = 100;
14360
14361     /**
14362      * Default sortable of columns which have no sortable specified (defaults to false)
14363      * @type Boolean
14364      */
14365     this.defaultSortable = false;
14366
14367     this.addEvents({
14368         /**
14369              * @event widthchange
14370              * Fires when the width of a column changes.
14371              * @param {ColumnModel} this
14372              * @param {Number} columnIndex The column index
14373              * @param {Number} newWidth The new width
14374              */
14375             "widthchange": true,
14376         /**
14377              * @event headerchange
14378              * Fires when the text of a header changes.
14379              * @param {ColumnModel} this
14380              * @param {Number} columnIndex The column index
14381              * @param {Number} newText The new header text
14382              */
14383             "headerchange": true,
14384         /**
14385              * @event hiddenchange
14386              * Fires when a column is hidden or "unhidden".
14387              * @param {ColumnModel} this
14388              * @param {Number} columnIndex The column index
14389              * @param {Boolean} hidden true if hidden, false otherwise
14390              */
14391             "hiddenchange": true,
14392             /**
14393          * @event columnmoved
14394          * Fires when a column is moved.
14395          * @param {ColumnModel} this
14396          * @param {Number} oldIndex
14397          * @param {Number} newIndex
14398          */
14399         "columnmoved" : true,
14400         /**
14401          * @event columlockchange
14402          * Fires when a column's locked state is changed
14403          * @param {ColumnModel} this
14404          * @param {Number} colIndex
14405          * @param {Boolean} locked true if locked
14406          */
14407         "columnlockchange" : true
14408     });
14409     Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14410 };
14411 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14412     /**
14413      * @cfg {String} header The header text to display in the Grid view.
14414      */
14415     /**
14416      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14417      * {@link Roo.data.Record} definition from which to draw the column's value. If not
14418      * specified, the column's index is used as an index into the Record's data Array.
14419      */
14420     /**
14421      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14422      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14423      */
14424     /**
14425      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14426      * Defaults to the value of the {@link #defaultSortable} property.
14427      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14428      */
14429     /**
14430      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
14431      */
14432     /**
14433      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
14434      */
14435     /**
14436      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14437      */
14438     /**
14439      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14440      */
14441     /**
14442      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14443      * given the cell's data value. See {@link #setRenderer}. If not specified, the
14444      * default renderer uses the raw data value.
14445      */
14446     /**
14447      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
14448      */
14449
14450     /**
14451      * Returns the id of the column at the specified index.
14452      * @param {Number} index The column index
14453      * @return {String} the id
14454      */
14455     getColumnId : function(index){
14456         return this.config[index].id;
14457     },
14458
14459     /**
14460      * Returns the column for a specified id.
14461      * @param {String} id The column id
14462      * @return {Object} the column
14463      */
14464     getColumnById : function(id){
14465         return this.lookup[id];
14466     },
14467
14468     
14469     /**
14470      * Returns the column for a specified dataIndex.
14471      * @param {String} dataIndex The column dataIndex
14472      * @return {Object|Boolean} the column or false if not found
14473      */
14474     getColumnByDataIndex: function(dataIndex){
14475         var index = this.findColumnIndex(dataIndex);
14476         return index > -1 ? this.config[index] : false;
14477     },
14478     
14479     /**
14480      * Returns the index for a specified column id.
14481      * @param {String} id The column id
14482      * @return {Number} the index, or -1 if not found
14483      */
14484     getIndexById : function(id){
14485         for(var i = 0, len = this.config.length; i < len; i++){
14486             if(this.config[i].id == id){
14487                 return i;
14488             }
14489         }
14490         return -1;
14491     },
14492     
14493     /**
14494      * Returns the index for a specified column dataIndex.
14495      * @param {String} dataIndex The column dataIndex
14496      * @return {Number} the index, or -1 if not found
14497      */
14498     
14499     findColumnIndex : function(dataIndex){
14500         for(var i = 0, len = this.config.length; i < len; i++){
14501             if(this.config[i].dataIndex == dataIndex){
14502                 return i;
14503             }
14504         }
14505         return -1;
14506     },
14507     
14508     
14509     moveColumn : function(oldIndex, newIndex){
14510         var c = this.config[oldIndex];
14511         this.config.splice(oldIndex, 1);
14512         this.config.splice(newIndex, 0, c);
14513         this.dataMap = null;
14514         this.fireEvent("columnmoved", this, oldIndex, newIndex);
14515     },
14516
14517     isLocked : function(colIndex){
14518         return this.config[colIndex].locked === true;
14519     },
14520
14521     setLocked : function(colIndex, value, suppressEvent){
14522         if(this.isLocked(colIndex) == value){
14523             return;
14524         }
14525         this.config[colIndex].locked = value;
14526         if(!suppressEvent){
14527             this.fireEvent("columnlockchange", this, colIndex, value);
14528         }
14529     },
14530
14531     getTotalLockedWidth : function(){
14532         var totalWidth = 0;
14533         for(var i = 0; i < this.config.length; i++){
14534             if(this.isLocked(i) && !this.isHidden(i)){
14535                 this.totalWidth += this.getColumnWidth(i);
14536             }
14537         }
14538         return totalWidth;
14539     },
14540
14541     getLockedCount : function(){
14542         for(var i = 0, len = this.config.length; i < len; i++){
14543             if(!this.isLocked(i)){
14544                 return i;
14545             }
14546         }
14547     },
14548
14549     /**
14550      * Returns the number of columns.
14551      * @return {Number}
14552      */
14553     getColumnCount : function(visibleOnly){
14554         if(visibleOnly === true){
14555             var c = 0;
14556             for(var i = 0, len = this.config.length; i < len; i++){
14557                 if(!this.isHidden(i)){
14558                     c++;
14559                 }
14560             }
14561             return c;
14562         }
14563         return this.config.length;
14564     },
14565
14566     /**
14567      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
14568      * @param {Function} fn
14569      * @param {Object} scope (optional)
14570      * @return {Array} result
14571      */
14572     getColumnsBy : function(fn, scope){
14573         var r = [];
14574         for(var i = 0, len = this.config.length; i < len; i++){
14575             var c = this.config[i];
14576             if(fn.call(scope||this, c, i) === true){
14577                 r[r.length] = c;
14578             }
14579         }
14580         return r;
14581     },
14582
14583     /**
14584      * Returns true if the specified column is sortable.
14585      * @param {Number} col The column index
14586      * @return {Boolean}
14587      */
14588     isSortable : function(col){
14589         if(typeof this.config[col].sortable == "undefined"){
14590             return this.defaultSortable;
14591         }
14592         return this.config[col].sortable;
14593     },
14594
14595     /**
14596      * Returns the rendering (formatting) function defined for the column.
14597      * @param {Number} col The column index.
14598      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
14599      */
14600     getRenderer : function(col){
14601         if(!this.config[col].renderer){
14602             return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
14603         }
14604         return this.config[col].renderer;
14605     },
14606
14607     /**
14608      * Sets the rendering (formatting) function for a column.
14609      * @param {Number} col The column index
14610      * @param {Function} fn The function to use to process the cell's raw data
14611      * to return HTML markup for the grid view. The render function is called with
14612      * the following parameters:<ul>
14613      * <li>Data value.</li>
14614      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
14615      * <li>css A CSS style string to apply to the table cell.</li>
14616      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
14617      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
14618      * <li>Row index</li>
14619      * <li>Column index</li>
14620      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
14621      */
14622     setRenderer : function(col, fn){
14623         this.config[col].renderer = fn;
14624     },
14625
14626     /**
14627      * Returns the width for the specified column.
14628      * @param {Number} col The column index
14629      * @return {Number}
14630      */
14631     getColumnWidth : function(col){
14632         return this.config[col].width * 1 || this.defaultWidth;
14633     },
14634
14635     /**
14636      * Sets the width for a column.
14637      * @param {Number} col The column index
14638      * @param {Number} width The new width
14639      */
14640     setColumnWidth : function(col, width, suppressEvent){
14641         this.config[col].width = width;
14642         this.totalWidth = null;
14643         if(!suppressEvent){
14644              this.fireEvent("widthchange", this, col, width);
14645         }
14646     },
14647
14648     /**
14649      * Returns the total width of all columns.
14650      * @param {Boolean} includeHidden True to include hidden column widths
14651      * @return {Number}
14652      */
14653     getTotalWidth : function(includeHidden){
14654         if(!this.totalWidth){
14655             this.totalWidth = 0;
14656             for(var i = 0, len = this.config.length; i < len; i++){
14657                 if(includeHidden || !this.isHidden(i)){
14658                     this.totalWidth += this.getColumnWidth(i);
14659                 }
14660             }
14661         }
14662         return this.totalWidth;
14663     },
14664
14665     /**
14666      * Returns the header for the specified column.
14667      * @param {Number} col The column index
14668      * @return {String}
14669      */
14670     getColumnHeader : function(col){
14671         return this.config[col].header;
14672     },
14673
14674     /**
14675      * Sets the header for a column.
14676      * @param {Number} col The column index
14677      * @param {String} header The new header
14678      */
14679     setColumnHeader : function(col, header){
14680         this.config[col].header = header;
14681         this.fireEvent("headerchange", this, col, header);
14682     },
14683
14684     /**
14685      * Returns the tooltip for the specified column.
14686      * @param {Number} col The column index
14687      * @return {String}
14688      */
14689     getColumnTooltip : function(col){
14690             return this.config[col].tooltip;
14691     },
14692     /**
14693      * Sets the tooltip for a column.
14694      * @param {Number} col The column index
14695      * @param {String} tooltip The new tooltip
14696      */
14697     setColumnTooltip : function(col, tooltip){
14698             this.config[col].tooltip = tooltip;
14699     },
14700
14701     /**
14702      * Returns the dataIndex for the specified column.
14703      * @param {Number} col The column index
14704      * @return {Number}
14705      */
14706     getDataIndex : function(col){
14707         return this.config[col].dataIndex;
14708     },
14709
14710     /**
14711      * Sets the dataIndex for a column.
14712      * @param {Number} col The column index
14713      * @param {Number} dataIndex The new dataIndex
14714      */
14715     setDataIndex : function(col, dataIndex){
14716         this.config[col].dataIndex = dataIndex;
14717     },
14718
14719     
14720     
14721     /**
14722      * Returns true if the cell is editable.
14723      * @param {Number} colIndex The column index
14724      * @param {Number} rowIndex The row index
14725      * @return {Boolean}
14726      */
14727     isCellEditable : function(colIndex, rowIndex){
14728         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
14729     },
14730
14731     /**
14732      * Returns the editor defined for the cell/column.
14733      * return false or null to disable editing.
14734      * @param {Number} colIndex The column index
14735      * @param {Number} rowIndex The row index
14736      * @return {Object}
14737      */
14738     getCellEditor : function(colIndex, rowIndex){
14739         return this.config[colIndex].editor;
14740     },
14741
14742     /**
14743      * Sets if a column is editable.
14744      * @param {Number} col The column index
14745      * @param {Boolean} editable True if the column is editable
14746      */
14747     setEditable : function(col, editable){
14748         this.config[col].editable = editable;
14749     },
14750
14751
14752     /**
14753      * Returns true if the column is hidden.
14754      * @param {Number} colIndex The column index
14755      * @return {Boolean}
14756      */
14757     isHidden : function(colIndex){
14758         return this.config[colIndex].hidden;
14759     },
14760
14761
14762     /**
14763      * Returns true if the column width cannot be changed
14764      */
14765     isFixed : function(colIndex){
14766         return this.config[colIndex].fixed;
14767     },
14768
14769     /**
14770      * Returns true if the column can be resized
14771      * @return {Boolean}
14772      */
14773     isResizable : function(colIndex){
14774         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
14775     },
14776     /**
14777      * Sets if a column is hidden.
14778      * @param {Number} colIndex The column index
14779      * @param {Boolean} hidden True if the column is hidden
14780      */
14781     setHidden : function(colIndex, hidden){
14782         this.config[colIndex].hidden = hidden;
14783         this.totalWidth = null;
14784         this.fireEvent("hiddenchange", this, colIndex, hidden);
14785     },
14786
14787     /**
14788      * Sets the editor for a column.
14789      * @param {Number} col The column index
14790      * @param {Object} editor The editor object
14791      */
14792     setEditor : function(col, editor){
14793         this.config[col].editor = editor;
14794     }
14795 });
14796
14797 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
14798         if(typeof value == "string" && value.length < 1){
14799             return "&#160;";
14800         }
14801         return value;
14802 };
14803
14804 // Alias for backwards compatibility
14805 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
14806
14807 /**
14808  * @extends Roo.bootstrap.Table.AbstractSelectionModel
14809  * @class Roo.bootstrap.Table.RowSelectionModel
14810  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
14811  * It supports multiple selections and keyboard selection/navigation. 
14812  * @constructor
14813  * @param {Object} config
14814  */
14815
14816 Roo.bootstrap.Table.RowSelectionModel = function(config){
14817     Roo.apply(this, config);
14818     this.selections = new Roo.util.MixedCollection(false, function(o){
14819         return o.id;
14820     });
14821
14822     this.last = false;
14823     this.lastActive = false;
14824
14825     this.addEvents({
14826         /**
14827              * @event selectionchange
14828              * Fires when the selection changes
14829              * @param {SelectionModel} this
14830              */
14831             "selectionchange" : true,
14832         /**
14833              * @event afterselectionchange
14834              * Fires after the selection changes (eg. by key press or clicking)
14835              * @param {SelectionModel} this
14836              */
14837             "afterselectionchange" : true,
14838         /**
14839              * @event beforerowselect
14840              * Fires when a row is selected being selected, return false to cancel.
14841              * @param {SelectionModel} this
14842              * @param {Number} rowIndex The selected index
14843              * @param {Boolean} keepExisting False if other selections will be cleared
14844              */
14845             "beforerowselect" : true,
14846         /**
14847              * @event rowselect
14848              * Fires when a row is selected.
14849              * @param {SelectionModel} this
14850              * @param {Number} rowIndex The selected index
14851              * @param {Roo.data.Record} r The record
14852              */
14853             "rowselect" : true,
14854         /**
14855              * @event rowdeselect
14856              * Fires when a row is deselected.
14857              * @param {SelectionModel} this
14858              * @param {Number} rowIndex The selected index
14859              */
14860         "rowdeselect" : true
14861     });
14862     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
14863     this.locked = false;
14864 };
14865
14866 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
14867     /**
14868      * @cfg {Boolean} singleSelect
14869      * True to allow selection of only one row at a time (defaults to false)
14870      */
14871     singleSelect : false,
14872
14873     // private
14874     initEvents : function(){
14875
14876         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
14877             this.grid.on("mousedown", this.handleMouseDown, this);
14878         }else{ // allow click to work like normal
14879             this.grid.on("rowclick", this.handleDragableRowClick, this);
14880         }
14881
14882         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
14883             "up" : function(e){
14884                 if(!e.shiftKey){
14885                     this.selectPrevious(e.shiftKey);
14886                 }else if(this.last !== false && this.lastActive !== false){
14887                     var last = this.last;
14888                     this.selectRange(this.last,  this.lastActive-1);
14889                     this.grid.getView().focusRow(this.lastActive);
14890                     if(last !== false){
14891                         this.last = last;
14892                     }
14893                 }else{
14894                     this.selectFirstRow();
14895                 }
14896                 this.fireEvent("afterselectionchange", this);
14897             },
14898             "down" : function(e){
14899                 if(!e.shiftKey){
14900                     this.selectNext(e.shiftKey);
14901                 }else if(this.last !== false && this.lastActive !== false){
14902                     var last = this.last;
14903                     this.selectRange(this.last,  this.lastActive+1);
14904                     this.grid.getView().focusRow(this.lastActive);
14905                     if(last !== false){
14906                         this.last = last;
14907                     }
14908                 }else{
14909                     this.selectFirstRow();
14910                 }
14911                 this.fireEvent("afterselectionchange", this);
14912             },
14913             scope: this
14914         });
14915
14916         var view = this.grid.view;
14917         view.on("refresh", this.onRefresh, this);
14918         view.on("rowupdated", this.onRowUpdated, this);
14919         view.on("rowremoved", this.onRemove, this);
14920     },
14921
14922     // private
14923     onRefresh : function(){
14924         var ds = this.grid.dataSource, i, v = this.grid.view;
14925         var s = this.selections;
14926         s.each(function(r){
14927             if((i = ds.indexOfId(r.id)) != -1){
14928                 v.onRowSelect(i);
14929             }else{
14930                 s.remove(r);
14931             }
14932         });
14933     },
14934
14935     // private
14936     onRemove : function(v, index, r){
14937         this.selections.remove(r);
14938     },
14939
14940     // private
14941     onRowUpdated : function(v, index, r){
14942         if(this.isSelected(r)){
14943             v.onRowSelect(index);
14944         }
14945     },
14946
14947     /**
14948      * Select records.
14949      * @param {Array} records The records to select
14950      * @param {Boolean} keepExisting (optional) True to keep existing selections
14951      */
14952     selectRecords : function(records, keepExisting){
14953         if(!keepExisting){
14954             this.clearSelections();
14955         }
14956         var ds = this.grid.dataSource;
14957         for(var i = 0, len = records.length; i < len; i++){
14958             this.selectRow(ds.indexOf(records[i]), true);
14959         }
14960     },
14961
14962     /**
14963      * Gets the number of selected rows.
14964      * @return {Number}
14965      */
14966     getCount : function(){
14967         return this.selections.length;
14968     },
14969
14970     /**
14971      * Selects the first row in the grid.
14972      */
14973     selectFirstRow : function(){
14974         this.selectRow(0);
14975     },
14976
14977     /**
14978      * Select the last row.
14979      * @param {Boolean} keepExisting (optional) True to keep existing selections
14980      */
14981     selectLastRow : function(keepExisting){
14982         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
14983     },
14984
14985     /**
14986      * Selects the row immediately following the last selected row.
14987      * @param {Boolean} keepExisting (optional) True to keep existing selections
14988      */
14989     selectNext : function(keepExisting){
14990         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
14991             this.selectRow(this.last+1, keepExisting);
14992             this.grid.getView().focusRow(this.last);
14993         }
14994     },
14995
14996     /**
14997      * Selects the row that precedes the last selected row.
14998      * @param {Boolean} keepExisting (optional) True to keep existing selections
14999      */
15000     selectPrevious : function(keepExisting){
15001         if(this.last){
15002             this.selectRow(this.last-1, keepExisting);
15003             this.grid.getView().focusRow(this.last);
15004         }
15005     },
15006
15007     /**
15008      * Returns the selected records
15009      * @return {Array} Array of selected records
15010      */
15011     getSelections : function(){
15012         return [].concat(this.selections.items);
15013     },
15014
15015     /**
15016      * Returns the first selected record.
15017      * @return {Record}
15018      */
15019     getSelected : function(){
15020         return this.selections.itemAt(0);
15021     },
15022
15023
15024     /**
15025      * Clears all selections.
15026      */
15027     clearSelections : function(fast){
15028         if(this.locked) return;
15029         if(fast !== true){
15030             var ds = this.grid.dataSource;
15031             var s = this.selections;
15032             s.each(function(r){
15033                 this.deselectRow(ds.indexOfId(r.id));
15034             }, this);
15035             s.clear();
15036         }else{
15037             this.selections.clear();
15038         }
15039         this.last = false;
15040     },
15041
15042
15043     /**
15044      * Selects all rows.
15045      */
15046     selectAll : function(){
15047         if(this.locked) return;
15048         this.selections.clear();
15049         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15050             this.selectRow(i, true);
15051         }
15052     },
15053
15054     /**
15055      * Returns True if there is a selection.
15056      * @return {Boolean}
15057      */
15058     hasSelection : function(){
15059         return this.selections.length > 0;
15060     },
15061
15062     /**
15063      * Returns True if the specified row is selected.
15064      * @param {Number/Record} record The record or index of the record to check
15065      * @return {Boolean}
15066      */
15067     isSelected : function(index){
15068         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15069         return (r && this.selections.key(r.id) ? true : false);
15070     },
15071
15072     /**
15073      * Returns True if the specified record id is selected.
15074      * @param {String} id The id of record to check
15075      * @return {Boolean}
15076      */
15077     isIdSelected : function(id){
15078         return (this.selections.key(id) ? true : false);
15079     },
15080
15081     // private
15082     handleMouseDown : function(e, t){
15083         var view = this.grid.getView(), rowIndex;
15084         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15085             return;
15086         };
15087         if(e.shiftKey && this.last !== false){
15088             var last = this.last;
15089             this.selectRange(last, rowIndex, e.ctrlKey);
15090             this.last = last; // reset the last
15091             view.focusRow(rowIndex);
15092         }else{
15093             var isSelected = this.isSelected(rowIndex);
15094             if(e.button !== 0 && isSelected){
15095                 view.focusRow(rowIndex);
15096             }else if(e.ctrlKey && isSelected){
15097                 this.deselectRow(rowIndex);
15098             }else if(!isSelected){
15099                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15100                 view.focusRow(rowIndex);
15101             }
15102         }
15103         this.fireEvent("afterselectionchange", this);
15104     },
15105     // private
15106     handleDragableRowClick :  function(grid, rowIndex, e) 
15107     {
15108         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15109             this.selectRow(rowIndex, false);
15110             grid.view.focusRow(rowIndex);
15111              this.fireEvent("afterselectionchange", this);
15112         }
15113     },
15114     
15115     /**
15116      * Selects multiple rows.
15117      * @param {Array} rows Array of the indexes of the row to select
15118      * @param {Boolean} keepExisting (optional) True to keep existing selections
15119      */
15120     selectRows : function(rows, keepExisting){
15121         if(!keepExisting){
15122             this.clearSelections();
15123         }
15124         for(var i = 0, len = rows.length; i < len; i++){
15125             this.selectRow(rows[i], true);
15126         }
15127     },
15128
15129     /**
15130      * Selects a range of rows. All rows in between startRow and endRow are also selected.
15131      * @param {Number} startRow The index of the first row in the range
15132      * @param {Number} endRow The index of the last row in the range
15133      * @param {Boolean} keepExisting (optional) True to retain existing selections
15134      */
15135     selectRange : function(startRow, endRow, keepExisting){
15136         if(this.locked) return;
15137         if(!keepExisting){
15138             this.clearSelections();
15139         }
15140         if(startRow <= endRow){
15141             for(var i = startRow; i <= endRow; i++){
15142                 this.selectRow(i, true);
15143             }
15144         }else{
15145             for(var i = startRow; i >= endRow; i--){
15146                 this.selectRow(i, true);
15147             }
15148         }
15149     },
15150
15151     /**
15152      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15153      * @param {Number} startRow The index of the first row in the range
15154      * @param {Number} endRow The index of the last row in the range
15155      */
15156     deselectRange : function(startRow, endRow, preventViewNotify){
15157         if(this.locked) return;
15158         for(var i = startRow; i <= endRow; i++){
15159             this.deselectRow(i, preventViewNotify);
15160         }
15161     },
15162
15163     /**
15164      * Selects a row.
15165      * @param {Number} row The index of the row to select
15166      * @param {Boolean} keepExisting (optional) True to keep existing selections
15167      */
15168     selectRow : function(index, keepExisting, preventViewNotify){
15169         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15170         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15171             if(!keepExisting || this.singleSelect){
15172                 this.clearSelections();
15173             }
15174             var r = this.grid.dataSource.getAt(index);
15175             this.selections.add(r);
15176             this.last = this.lastActive = index;
15177             if(!preventViewNotify){
15178                 this.grid.getView().onRowSelect(index);
15179             }
15180             this.fireEvent("rowselect", this, index, r);
15181             this.fireEvent("selectionchange", this);
15182         }
15183     },
15184
15185     /**
15186      * Deselects a row.
15187      * @param {Number} row The index of the row to deselect
15188      */
15189     deselectRow : function(index, preventViewNotify){
15190         if(this.locked) return;
15191         if(this.last == index){
15192             this.last = false;
15193         }
15194         if(this.lastActive == index){
15195             this.lastActive = false;
15196         }
15197         var r = this.grid.dataSource.getAt(index);
15198         this.selections.remove(r);
15199         if(!preventViewNotify){
15200             this.grid.getView().onRowDeselect(index);
15201         }
15202         this.fireEvent("rowdeselect", this, index);
15203         this.fireEvent("selectionchange", this);
15204     },
15205
15206     // private
15207     restoreLast : function(){
15208         if(this._last){
15209             this.last = this._last;
15210         }
15211     },
15212
15213     // private
15214     acceptsNav : function(row, col, cm){
15215         return !cm.isHidden(col) && cm.isCellEditable(col, row);
15216     },
15217
15218     // private
15219     onEditorKey : function(field, e){
15220         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15221         if(k == e.TAB){
15222             e.stopEvent();
15223             ed.completeEdit();
15224             if(e.shiftKey){
15225                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15226             }else{
15227                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15228             }
15229         }else if(k == e.ENTER && !e.ctrlKey){
15230             e.stopEvent();
15231             ed.completeEdit();
15232             if(e.shiftKey){
15233                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15234             }else{
15235                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15236             }
15237         }else if(k == e.ESC){
15238             ed.cancelEdit();
15239         }
15240         if(newCell){
15241             g.startEditing(newCell[0], newCell[1]);
15242         }
15243     }
15244 });