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](false));
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](false));
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](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @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
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * image
967  * 
968  */
969
970
971 /**
972  * @class Roo.bootstrap.Img
973  * @extends Roo.bootstrap.Component
974  * Bootstrap Img class
975  * @cfg {Boolean} imgResponsive false | true
976  * @cfg {String} border rounded | circle | thumbnail
977  * @cfg {String} src image source
978  * @cfg {String} alt image alternative text
979  * @cfg {String} href a tag href
980  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
981  * 
982  * @constructor
983  * Create a new Input
984  * @param {Object} config The config object
985  */
986
987 Roo.bootstrap.Img = function(config){
988     Roo.bootstrap.Img.superclass.constructor.call(this, config);
989     
990     this.addEvents({
991         // img events
992         /**
993          * @event click
994          * The img click event for the img.
995          * @param {Roo.EventObject} e
996          */
997         "click" : true
998     });
999 };
1000
1001 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1002     
1003     imgResponsive: true,
1004     border: '',
1005     src: '',
1006     href: false,
1007     target: false,
1008
1009     getAutoCreate : function(){
1010         
1011         var cfg = {
1012             tag: 'img',
1013             cls: (this.imgResponsive) ? 'img-responsive' : '',
1014             html : null
1015         }
1016         
1017         cfg.html = this.html || cfg.html;
1018         
1019         cfg.src = this.src || cfg.src;
1020         
1021         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1022             cfg.cls += ' img-' + this.border;
1023         }
1024         
1025         if(this.alt){
1026             cfg.alt = this.alt;
1027         }
1028         
1029         if(this.href){
1030             var a = {
1031                 tag: 'a',
1032                 href: this.href,
1033                 cn: [
1034                     cfg
1035                 ]
1036             }
1037             
1038             if(this.target){
1039                 a.target = this.target;
1040             }
1041             
1042         }
1043         
1044         
1045         return (this.href) ? a : cfg;
1046     },
1047     
1048     initEvents: function() {
1049         
1050         if(!this.href){
1051             this.el.on('click', this.onClick, this);
1052         }
1053     },
1054     
1055     onClick : function(e)
1056     {
1057         Roo.log('img onclick');
1058         this.fireEvent('click', this, e);
1059     }
1060    
1061 });
1062
1063  /*
1064  * - LGPL
1065  *
1066  * image
1067  * 
1068  */
1069
1070
1071 /**
1072  * @class Roo.bootstrap.Link
1073  * @extends Roo.bootstrap.Component
1074  * Bootstrap Link Class
1075  * @cfg {String} alt image alternative text
1076  * @cfg {String} href a tag href
1077  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1078  * @cfg {String} html the content of the link.
1079
1080  * 
1081  * @constructor
1082  * Create a new Input
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Link = function(config){
1087     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // img events
1091         /**
1092          * @event click
1093          * The img click event for the img.
1094          * @param {Roo.EventObject} e
1095          */
1096         "click" : true
1097     });
1098 };
1099
1100 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1101     
1102     href: false,
1103     target: false,
1104
1105     getAutoCreate : function(){
1106         
1107         var cfg = {
1108             tag: 'a',
1109             html : this.html || 'html-missing'
1110         }
1111         
1112         
1113         if(this.alt){
1114             cfg.alt = this.alt;
1115         }
1116         cfg.href = this.href || '#';
1117         if(this.target){
1118             cfg.target = this.target;
1119         }
1120         
1121         return cfg;
1122     },
1123     
1124     initEvents: function() {
1125         
1126         if(!this.href){
1127             this.el.on('click', this.onClick, this);
1128         }
1129     },
1130     
1131     onClick : function(e)
1132     {
1133         //Roo.log('img onclick');
1134         this.fireEvent('click', this, e);
1135     }
1136    
1137 });
1138
1139  /*
1140  * - LGPL
1141  *
1142  * header
1143  * 
1144  */
1145
1146 /**
1147  * @class Roo.bootstrap.Header
1148  * @extends Roo.bootstrap.Component
1149  * Bootstrap Header class
1150  * @cfg {String} html content of header
1151  * @cfg {Number} level (1|2|3|4|5|6) default 1
1152  * 
1153  * @constructor
1154  * Create a new Header
1155  * @param {Object} config The config object
1156  */
1157
1158
1159 Roo.bootstrap.Header  = function(config){
1160     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1161 };
1162
1163 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1164     
1165     //href : false,
1166     html : false,
1167     level : 1,
1168     
1169     
1170     
1171     getAutoCreate : function(){
1172         
1173         var cfg = {
1174             tag: 'h' + (1 *this.level),
1175             html: this.html || 'fill in html'
1176         } ;
1177         
1178         return cfg;
1179     }
1180    
1181 });
1182
1183  
1184
1185  /*
1186  * Based on:
1187  * Ext JS Library 1.1.1
1188  * Copyright(c) 2006-2007, Ext JS, LLC.
1189  *
1190  * Originally Released Under LGPL - original licence link has changed is not relivant.
1191  *
1192  * Fork - LGPL
1193  * <script type="text/javascript">
1194  */
1195  
1196 /**
1197  * @class Roo.bootstrap.MenuMgr
1198  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1199  * @singleton
1200  */
1201 Roo.bootstrap.MenuMgr = function(){
1202    var menus, active, groups = {}, attached = false, lastShow = new Date();
1203
1204    // private - called when first menu is created
1205    function init(){
1206        menus = {};
1207        active = new Roo.util.MixedCollection();
1208        Roo.get(document).addKeyListener(27, function(){
1209            if(active.length > 0){
1210                hideAll();
1211            }
1212        });
1213    }
1214
1215    // private
1216    function hideAll(){
1217        if(active && active.length > 0){
1218            var c = active.clone();
1219            c.each(function(m){
1220                m.hide();
1221            });
1222        }
1223    }
1224
1225    // private
1226    function onHide(m){
1227        active.remove(m);
1228        if(active.length < 1){
1229            Roo.get(document).un("mouseup", onMouseDown);
1230             
1231            attached = false;
1232        }
1233    }
1234
1235    // private
1236    function onShow(m){
1237        var last = active.last();
1238        lastShow = new Date();
1239        active.add(m);
1240        if(!attached){
1241           Roo.get(document).on("mouseup", onMouseDown);
1242            
1243            attached = true;
1244        }
1245        if(m.parentMenu){
1246           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1247           m.parentMenu.activeChild = m;
1248        }else if(last && last.isVisible()){
1249           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250        }
1251    }
1252
1253    // private
1254    function onBeforeHide(m){
1255        if(m.activeChild){
1256            m.activeChild.hide();
1257        }
1258        if(m.autoHideTimer){
1259            clearTimeout(m.autoHideTimer);
1260            delete m.autoHideTimer;
1261        }
1262    }
1263
1264    // private
1265    function onBeforeShow(m){
1266        var pm = m.parentMenu;
1267        if(!pm && !m.allowOtherMenus){
1268            hideAll();
1269        }else if(pm && pm.activeChild && active != m){
1270            pm.activeChild.hide();
1271        }
1272    }
1273
1274    // private
1275    function onMouseDown(e){
1276         Roo.log("on MouseDown");
1277         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1278            hideAll();
1279         }
1280         
1281         
1282    }
1283
1284    // private
1285    function onBeforeCheck(mi, state){
1286        if(state){
1287            var g = groups[mi.group];
1288            for(var i = 0, l = g.length; i < l; i++){
1289                if(g[i] != mi){
1290                    g[i].setChecked(false);
1291                }
1292            }
1293        }
1294    }
1295
1296    return {
1297
1298        /**
1299         * Hides all menus that are currently visible
1300         */
1301        hideAll : function(){
1302             hideAll();  
1303        },
1304
1305        // private
1306        register : function(menu){
1307            if(!menus){
1308                init();
1309            }
1310            menus[menu.id] = menu;
1311            menu.on("beforehide", onBeforeHide);
1312            menu.on("hide", onHide);
1313            menu.on("beforeshow", onBeforeShow);
1314            menu.on("show", onShow);
1315            var g = menu.group;
1316            if(g && menu.events["checkchange"]){
1317                if(!groups[g]){
1318                    groups[g] = [];
1319                }
1320                groups[g].push(menu);
1321                menu.on("checkchange", onCheck);
1322            }
1323        },
1324
1325         /**
1326          * Returns a {@link Roo.menu.Menu} object
1327          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1328          * be used to generate and return a new Menu instance.
1329          */
1330        get : function(menu){
1331            if(typeof menu == "string"){ // menu id
1332                return menus[menu];
1333            }else if(menu.events){  // menu instance
1334                return menu;
1335            }
1336            /*else if(typeof menu.length == 'number'){ // array of menu items?
1337                return new Roo.bootstrap.Menu({items:menu});
1338            }else{ // otherwise, must be a config
1339                return new Roo.bootstrap.Menu(menu);
1340            }
1341            */
1342            return false;
1343        },
1344
1345        // private
1346        unregister : function(menu){
1347            delete menus[menu.id];
1348            menu.un("beforehide", onBeforeHide);
1349            menu.un("hide", onHide);
1350            menu.un("beforeshow", onBeforeShow);
1351            menu.un("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                groups[g].remove(menu);
1355                menu.un("checkchange", onCheck);
1356            }
1357        },
1358
1359        // private
1360        registerCheckable : function(menuItem){
1361            var g = menuItem.group;
1362            if(g){
1363                if(!groups[g]){
1364                    groups[g] = [];
1365                }
1366                groups[g].push(menuItem);
1367                menuItem.on("beforecheckchange", onBeforeCheck);
1368            }
1369        },
1370
1371        // private
1372        unregisterCheckable : function(menuItem){
1373            var g = menuItem.group;
1374            if(g){
1375                groups[g].remove(menuItem);
1376                menuItem.un("beforecheckchange", onBeforeCheck);
1377            }
1378        }
1379    };
1380 }();/*
1381  * - LGPL
1382  *
1383  * menu
1384  * 
1385  */
1386
1387 /**
1388  * @class Roo.bootstrap.Menu
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Menu class - container for MenuItems
1391  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1392  * 
1393  * @constructor
1394  * Create a new Menu
1395  * @param {Object} config The config object
1396  */
1397
1398
1399 Roo.bootstrap.Menu = function(config){
1400     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1401     if (this.registerMenu) {
1402         Roo.bootstrap.MenuMgr.register(this);
1403     }
1404     this.addEvents({
1405         /**
1406          * @event beforeshow
1407          * Fires before this menu is displayed
1408          * @param {Roo.menu.Menu} this
1409          */
1410         beforeshow : true,
1411         /**
1412          * @event beforehide
1413          * Fires before this menu is hidden
1414          * @param {Roo.menu.Menu} this
1415          */
1416         beforehide : true,
1417         /**
1418          * @event show
1419          * Fires after this menu is displayed
1420          * @param {Roo.menu.Menu} this
1421          */
1422         show : true,
1423         /**
1424          * @event hide
1425          * Fires after this menu is hidden
1426          * @param {Roo.menu.Menu} this
1427          */
1428         hide : true,
1429         /**
1430          * @event click
1431          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1432          * @param {Roo.menu.Menu} this
1433          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1434          * @param {Roo.EventObject} e
1435          */
1436         click : true,
1437         /**
1438          * @event mouseover
1439          * Fires when the mouse is hovering over this menu
1440          * @param {Roo.menu.Menu} this
1441          * @param {Roo.EventObject} e
1442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443          */
1444         mouseover : true,
1445         /**
1446          * @event mouseout
1447          * Fires when the mouse exits this menu
1448          * @param {Roo.menu.Menu} this
1449          * @param {Roo.EventObject} e
1450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451          */
1452         mouseout : true,
1453         /**
1454          * @event itemclick
1455          * Fires when a menu item contained in this menu is clicked
1456          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1457          * @param {Roo.EventObject} e
1458          */
1459         itemclick: true
1460     });
1461     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1462 };
1463
1464 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1465     
1466    /// html : false,
1467     //align : '',
1468     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1469     type: false,
1470     /**
1471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1472      */
1473     registerMenu : true,
1474     
1475     menuItems :false, // stores the menu items..
1476     
1477     hidden:true,
1478     
1479     parentMenu : false,
1480     
1481     getChildContainer : function() {
1482         return this.el;  
1483     },
1484     
1485     getAutoCreate : function(){
1486          
1487         //if (['right'].indexOf(this.align)!==-1) {
1488         //    cfg.cn[1].cls += ' pull-right'
1489         //}
1490         
1491         
1492         var cfg = {
1493             tag : 'ul',
1494             cls : 'dropdown-menu' ,
1495             style : 'z-index:1000'
1496             
1497         }
1498         
1499         if (this.type === 'submenu') {
1500             cfg.cls = 'submenu active';
1501         }
1502         if (this.type === 'treeview') {
1503             cfg.cls = 'treeview-menu';
1504         }
1505         
1506         return cfg;
1507     },
1508     initEvents : function() {
1509         
1510        // Roo.log("ADD event");
1511        // Roo.log(this.triggerEl.dom);
1512         this.triggerEl.on('click', this.onTriggerPress, this);
1513         this.triggerEl.addClass('dropdown-toggle');
1514         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1515
1516         this.el.on("mouseover", this.onMouseOver, this);
1517         this.el.on("mouseout", this.onMouseOut, this);
1518         
1519         
1520     },
1521     findTargetItem : function(e){
1522         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1523         if(!t){
1524             return false;
1525         }
1526         //Roo.log(t);         Roo.log(t.id);
1527         if(t && t.id){
1528             //Roo.log(this.menuitems);
1529             return this.menuitems.get(t.id);
1530             
1531             //return this.items.get(t.menuItemId);
1532         }
1533         
1534         return false;
1535     },
1536     onClick : function(e){
1537         Roo.log("menu.onClick");
1538         var t = this.findTargetItem(e);
1539         if(!t){
1540             return;
1541         }
1542         Roo.log(e);
1543         /*
1544         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1545             if(t == this.activeItem && t.shouldDeactivate(e)){
1546                 this.activeItem.deactivate();
1547                 delete this.activeItem;
1548                 return;
1549             }
1550             if(t.canActivate){
1551                 this.setActiveItem(t, true);
1552             }
1553             return;
1554             
1555             
1556         }
1557         */
1558         Roo.log('pass click event');
1559         
1560         t.onClick(e);
1561         
1562         this.fireEvent("click", this, t, e);
1563         
1564         this.hide();
1565     },
1566      onMouseOver : function(e){
1567         var t  = this.findTargetItem(e);
1568         //Roo.log(t);
1569         //if(t){
1570         //    if(t.canActivate && !t.disabled){
1571         //        this.setActiveItem(t, true);
1572         //    }
1573         //}
1574         
1575         this.fireEvent("mouseover", this, e, t);
1576     },
1577     isVisible : function(){
1578         return !this.hidden;
1579     },
1580      onMouseOut : function(e){
1581         var t  = this.findTargetItem(e);
1582         
1583         //if(t ){
1584         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1585         //        this.activeItem.deactivate();
1586         //        delete this.activeItem;
1587         //    }
1588         //}
1589         this.fireEvent("mouseout", this, e, t);
1590     },
1591     
1592     
1593     /**
1594      * Displays this menu relative to another element
1595      * @param {String/HTMLElement/Roo.Element} element The element to align to
1596      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1597      * the element (defaults to this.defaultAlign)
1598      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1599      */
1600     show : function(el, pos, parentMenu){
1601         this.parentMenu = parentMenu;
1602         if(!this.el){
1603             this.render();
1604         }
1605         this.fireEvent("beforeshow", this);
1606         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1607     },
1608      /**
1609      * Displays this menu at a specific xy position
1610      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1611      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1612      */
1613     showAt : function(xy, parentMenu, /* private: */_e){
1614         this.parentMenu = parentMenu;
1615         if(!this.el){
1616             this.render();
1617         }
1618         if(_e !== false){
1619             this.fireEvent("beforeshow", this);
1620             
1621             //xy = this.el.adjustForConstraints(xy);
1622         }
1623         //this.el.setXY(xy);
1624         //this.el.show();
1625         this.hideMenuItems();
1626         this.hidden = false;
1627         this.triggerEl.addClass('open');
1628         this.focus();
1629         this.fireEvent("show", this);
1630     },
1631     
1632     focus : function(){
1633         return;
1634         if(!this.hidden){
1635             this.doFocus.defer(50, this);
1636         }
1637     },
1638
1639     doFocus : function(){
1640         if(!this.hidden){
1641             this.focusEl.focus();
1642         }
1643     },
1644
1645     /**
1646      * Hides this menu and optionally all parent menus
1647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1648      */
1649     hide : function(deep){
1650         
1651         this.hideMenuItems();
1652         if(this.el && this.isVisible()){
1653             this.fireEvent("beforehide", this);
1654             if(this.activeItem){
1655                 this.activeItem.deactivate();
1656                 this.activeItem = null;
1657             }
1658             this.triggerEl.removeClass('open');;
1659             this.hidden = true;
1660             this.fireEvent("hide", this);
1661         }
1662         if(deep === true && this.parentMenu){
1663             this.parentMenu.hide(true);
1664         }
1665     },
1666     
1667     onTriggerPress  : function(e)
1668     {
1669         
1670         Roo.log('trigger press');
1671         //Roo.log(e.getTarget());
1672        // Roo.log(this.triggerEl.dom);
1673         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1674             return;
1675         }
1676         if (this.isVisible()) {
1677             Roo.log('hide');
1678             this.hide();
1679         } else {
1680             this.show(this.triggerEl, false, false);
1681         }
1682         
1683         
1684     },
1685     
1686          
1687        
1688     
1689     hideMenuItems : function()
1690     {
1691         //$(backdrop).remove()
1692         Roo.select('.open',true).each(function(aa) {
1693             
1694             aa.removeClass('open');
1695           //var parent = getParent($(this))
1696           //var relatedTarget = { relatedTarget: this }
1697           
1698            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1699           //if (e.isDefaultPrevented()) return
1700            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1701         })
1702     },
1703     addxtypeChild : function (tree, cntr) {
1704         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1705           
1706         this.menuitems.add(comp);
1707         return comp;
1708
1709     },
1710     getEl : function()
1711     {
1712         Roo.log(this.el);
1713         return this.el;
1714     }
1715 });
1716
1717  
1718  /*
1719  * - LGPL
1720  *
1721  * menu item
1722  * 
1723  */
1724
1725
1726 /**
1727  * @class Roo.bootstrap.MenuItem
1728  * @extends Roo.bootstrap.Component
1729  * Bootstrap MenuItem class
1730  * @cfg {String} html the menu label
1731  * @cfg {String} href the link
1732  * @cfg {Boolean} preventDefault (true | false) default true
1733  * 
1734  * 
1735  * @constructor
1736  * Create a new MenuItem
1737  * @param {Object} config The config object
1738  */
1739
1740
1741 Roo.bootstrap.MenuItem = function(config){
1742     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743     this.addEvents({
1744         // raw events
1745         /**
1746          * @event click
1747          * The raw click event for the entire grid.
1748          * @param {Roo.EventObject} e
1749          */
1750         "click" : true
1751     });
1752 };
1753
1754 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1755     
1756     href : false,
1757     html : false,
1758     preventDefault: true,
1759     
1760     getAutoCreate : function(){
1761         var cfg= {
1762             tag: 'li',
1763             cls: 'dropdown-menu-item',
1764             cn: [
1765                     {
1766                         tag : 'a',
1767                         href : '#',
1768                         html : 'Link'
1769                     }
1770                 ]
1771         };
1772         if (this.parent().type == 'treeview') {
1773             cfg.cls = 'treeview-menu';
1774         }
1775         
1776         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1777         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1778         return cfg;
1779     },
1780     
1781     initEvents: function() {
1782         
1783         //this.el.select('a').on('click', this.onClick, this);
1784         
1785     },
1786     onClick : function(e)
1787     {
1788         Roo.log('item on click ');
1789         //if(this.preventDefault){
1790         //    e.preventDefault();
1791         //}
1792         //this.parent().hideMenuItems();
1793         
1794         this.fireEvent('click', this, e);
1795     },
1796     getEl : function()
1797     {
1798         return this.el;
1799     }
1800 });
1801
1802  
1803
1804  /*
1805  * - LGPL
1806  *
1807  * menu separator
1808  * 
1809  */
1810
1811
1812 /**
1813  * @class Roo.bootstrap.MenuSeparator
1814  * @extends Roo.bootstrap.Component
1815  * Bootstrap MenuSeparator class
1816  * 
1817  * @constructor
1818  * Create a new MenuItem
1819  * @param {Object} config The config object
1820  */
1821
1822
1823 Roo.bootstrap.MenuSeparator = function(config){
1824     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1825 };
1826
1827 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1828     
1829     getAutoCreate : function(){
1830         var cfg = {
1831             cls: 'divider',
1832             tag : 'li'
1833         };
1834         
1835         return cfg;
1836     }
1837    
1838 });
1839
1840  
1841
1842  
1843 /*
1844 <div class="modal fade">
1845   <div class="modal-dialog">
1846     <div class="modal-content">
1847       <div class="modal-header">
1848         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1849         <h4 class="modal-title">Modal title</h4>
1850       </div>
1851       <div class="modal-body">
1852         <p>One fine body&hellip;</p>
1853       </div>
1854       <div class="modal-footer">
1855         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1856         <button type="button" class="btn btn-primary">Save changes</button>
1857       </div>
1858     </div><!-- /.modal-content -->
1859   </div><!-- /.modal-dialog -->
1860 </div><!-- /.modal -->
1861 */
1862 /*
1863  * - LGPL
1864  *
1865  * page contgainer.
1866  * 
1867  */
1868
1869 /**
1870  * @class Roo.bootstrap.Modal
1871  * @extends Roo.bootstrap.Component
1872  * Bootstrap Modal class
1873  * @cfg {String} title Title of dialog
1874  * @cfg {Boolean} specificTitle (true|false) default false
1875  * @cfg {Array} buttons Array of buttons or standard button set..
1876  * @cfg {String} buttonPosition (left|right|center) default right
1877  * 
1878  * @constructor
1879  * Create a new Modal Dialog
1880  * @param {Object} config The config object
1881  */
1882
1883 Roo.bootstrap.Modal = function(config){
1884     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885     this.addEvents({
1886         // raw events
1887         /**
1888          * @event btnclick
1889          * The raw btnclick event for the button
1890          * @param {Roo.EventObject} e
1891          */
1892         "btnclick" : true
1893     });
1894     this.buttons = this.buttons || [];
1895 };
1896
1897 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1898     
1899     title : 'test dialog',
1900    
1901     buttons : false,
1902     
1903     // set on load...
1904     body:  false,
1905     
1906     specificTitle: false,
1907     
1908     buttonPosition: 'right',
1909     
1910     onRender : function(ct, position)
1911     {
1912         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1913      
1914         if(!this.el){
1915             var cfg = Roo.apply({},  this.getAutoCreate());
1916             cfg.id = Roo.id();
1917             //if(!cfg.name){
1918             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1919             //}
1920             //if (!cfg.name.length) {
1921             //    delete cfg.name;
1922            // }
1923             if (this.cls) {
1924                 cfg.cls += ' ' + this.cls;
1925             }
1926             if (this.style) {
1927                 cfg.style = this.style;
1928             }
1929             this.el = Roo.get(document.body).createChild(cfg, position);
1930         }
1931         //var type = this.el.dom.type;
1932         
1933         if(this.tabIndex !== undefined){
1934             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935         }
1936         
1937         
1938         
1939         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1940         this.maskEl.enableDisplayMode("block");
1941         this.maskEl.hide();
1942         //this.el.addClass("x-dlg-modal");
1943     
1944         if (this.buttons.length) {
1945             Roo.each(this.buttons, function(bb) {
1946                 b = Roo.apply({}, bb);
1947                 b.xns = b.xns || Roo.bootstrap;
1948                 b.xtype = b.xtype || 'Button';
1949                 if (typeof(b.listeners) == 'undefined') {
1950                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1951                 }
1952                 
1953                 var btn = Roo.factory(b);
1954                 
1955                 btn.onRender(this.el.select('.modal-footer div').first());
1956                 
1957             },this);
1958         }
1959         // render the children.
1960         var nitems = [];
1961         
1962         if(typeof(this.items) != 'undefined'){
1963             var items = this.items;
1964             delete this.items;
1965
1966             for(var i =0;i < items.length;i++) {
1967                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1968             }
1969         }
1970         
1971         this.items = nitems;
1972         
1973         this.body = this.el.select('.modal-body',true).first();
1974         this.close = this.el.select('.modal-header .close', true).first();
1975         this.footer = this.el.select('.modal-footer',true).first();
1976         this.initEvents();
1977         //this.el.addClass([this.fieldClass, this.cls]);
1978         
1979     },
1980     getAutoCreate : function(){
1981         
1982         
1983         var bdy = {
1984                 cls : 'modal-body',
1985                 html : this.html || ''
1986         };
1987         
1988         var title = {
1989             tag: 'h4',
1990             cls : 'modal-title',
1991             html : this.title
1992         };
1993         
1994         if(this.specificTitle){
1995             title = this.title;
1996         };
1997         
1998         return modal = {
1999             cls: "modal fade",
2000             style : 'display: none',
2001             cn : [
2002                 {
2003                     cls: "modal-dialog",
2004                     cn : [
2005                         {
2006                             cls : "modal-content",
2007                             cn : [
2008                                 {
2009                                     cls : 'modal-header',
2010                                     cn : [
2011                                         {
2012                                             tag: 'button',
2013                                             cls : 'close',
2014                                             html : '&times'
2015                                         },
2016                                         title
2017                                     ]
2018                                 },
2019                                 bdy,
2020                                 {
2021                                     cls : 'modal-footer',
2022                                     cn : [
2023                                         {
2024                                             tag: 'div',
2025                                             cls: 'btn-' + this.buttonPosition
2026                                         }
2027                                     ]
2028                                     
2029                                 }
2030                                 
2031                                 
2032                             ]
2033                             
2034                         }
2035                     ]
2036                         
2037                 }
2038             ]
2039             
2040             
2041         };
2042           
2043     },
2044     getChildContainer : function() {
2045          
2046          return this.el.select('.modal-body',true).first();
2047         
2048     },
2049     getButtonContainer : function() {
2050          return this.el.select('.modal-footer div',true).first();
2051         
2052     },
2053     initEvents : function()
2054     {
2055         this.el.select('.modal-header .close').on('click', this.hide, this);
2056 //        
2057 //        this.addxtype(this);
2058     },
2059     show : function() {
2060         
2061         if (!this.rendered) {
2062             this.render();
2063         }
2064        
2065         this.el.addClass('on');
2066         this.el.removeClass('fade');
2067         this.el.setStyle('display', 'block');
2068         Roo.get(document.body).addClass("x-body-masked");
2069         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2070         this.maskEl.show();
2071         this.el.setStyle('zIndex', '10001');
2072         this.fireEvent('show', this);
2073         
2074         
2075     },
2076     hide : function()
2077     {
2078         Roo.log('Modal hide?!');
2079         this.maskEl.hide();
2080         Roo.get(document.body).removeClass("x-body-masked");
2081         this.el.removeClass('on');
2082         this.el.addClass('fade');
2083         this.el.setStyle('display', 'none');
2084         this.fireEvent('hide', this);
2085     },
2086     
2087     addButton : function(str, cb)
2088     {
2089          
2090         
2091         var b = Roo.apply({}, { html : str } );
2092         b.xns = b.xns || Roo.bootstrap;
2093         b.xtype = b.xtype || 'Button';
2094         if (typeof(b.listeners) == 'undefined') {
2095             b.listeners = { click : cb.createDelegate(this)  };
2096         }
2097         
2098         var btn = Roo.factory(b);
2099            
2100         btn.onRender(this.el.select('.modal-footer div').first());
2101         
2102         return btn;   
2103        
2104     },
2105     
2106     setDefaultButton : function(btn)
2107     {
2108         //this.el.select('.modal-footer').()
2109     },
2110     resizeTo: function(w,h)
2111     {
2112         // skip..
2113     },
2114     setContentSize  : function(w, h)
2115     {
2116         
2117     },
2118     onButtonClick: function(btn,e)
2119     {
2120         //Roo.log([a,b,c]);
2121         this.fireEvent('btnclick', btn.name, e);
2122     },
2123     setTitle: function(str) {
2124         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2125         
2126     }
2127 });
2128
2129
2130 Roo.apply(Roo.bootstrap.Modal,  {
2131     /**
2132          * Button config that displays a single OK button
2133          * @type Object
2134          */
2135         OK :  [{
2136             name : 'ok',
2137             weight : 'primary',
2138             html : 'OK'
2139         }], 
2140         /**
2141          * Button config that displays Yes and No buttons
2142          * @type Object
2143          */
2144         YESNO : [
2145             {
2146                 name  : 'no',
2147                 html : 'No'
2148             },
2149             {
2150                 name  :'yes',
2151                 weight : 'primary',
2152                 html : 'Yes'
2153             }
2154         ],
2155         
2156         /**
2157          * Button config that displays OK and Cancel buttons
2158          * @type Object
2159          */
2160         OKCANCEL : [
2161             {
2162                name : 'cancel',
2163                 html : 'Cancel'
2164             },
2165             {
2166                 name : 'ok',
2167                 weight : 'primary',
2168                 html : 'OK'
2169             }
2170         ],
2171         /**
2172          * Button config that displays Yes, No and Cancel buttons
2173          * @type Object
2174          */
2175         YESNOCANCEL : [
2176             {
2177                 name : 'yes',
2178                 weight : 'primary',
2179                 html : 'Yes'
2180             },
2181             {
2182                 name : 'no',
2183                 html : 'No'
2184             },
2185             {
2186                 name : 'cancel',
2187                 html : 'Cancel'
2188             }
2189         ]
2190 });
2191  /*
2192  * - LGPL
2193  *
2194  * messagebox - can be used as a replace
2195  * 
2196  */
2197 /**
2198  * @class Roo.MessageBox
2199  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2200  * Example usage:
2201  *<pre><code>
2202 // Basic alert:
2203 Roo.Msg.alert('Status', 'Changes saved successfully.');
2204
2205 // Prompt for user data:
2206 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2207     if (btn == 'ok'){
2208         // process text value...
2209     }
2210 });
2211
2212 // Show a dialog using config options:
2213 Roo.Msg.show({
2214    title:'Save Changes?',
2215    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2216    buttons: Roo.Msg.YESNOCANCEL,
2217    fn: processResult,
2218    animEl: 'elId'
2219 });
2220 </code></pre>
2221  * @singleton
2222  */
2223 Roo.bootstrap.MessageBox = function(){
2224     var dlg, opt, mask, waitTimer;
2225     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2226     var buttons, activeTextEl, bwidth;
2227
2228     
2229     // private
2230     var handleButton = function(button){
2231         dlg.hide();
2232         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2233     };
2234
2235     // private
2236     var handleHide = function(){
2237         if(opt && opt.cls){
2238             dlg.el.removeClass(opt.cls);
2239         }
2240         //if(waitTimer){
2241         //    Roo.TaskMgr.stop(waitTimer);
2242         //    waitTimer = null;
2243         //}
2244     };
2245
2246     // private
2247     var updateButtons = function(b){
2248         var width = 0;
2249         if(!b){
2250             buttons["ok"].hide();
2251             buttons["cancel"].hide();
2252             buttons["yes"].hide();
2253             buttons["no"].hide();
2254             //dlg.footer.dom.style.display = 'none';
2255             return width;
2256         }
2257         dlg.footer.dom.style.display = '';
2258         for(var k in buttons){
2259             if(typeof buttons[k] != "function"){
2260                 if(b[k]){
2261                     buttons[k].show();
2262                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2263                     width += buttons[k].el.getWidth()+15;
2264                 }else{
2265                     buttons[k].hide();
2266                 }
2267             }
2268         }
2269         return width;
2270     };
2271
2272     // private
2273     var handleEsc = function(d, k, e){
2274         if(opt && opt.closable !== false){
2275             dlg.hide();
2276         }
2277         if(e){
2278             e.stopEvent();
2279         }
2280     };
2281
2282     return {
2283         /**
2284          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2285          * @return {Roo.BasicDialog} The BasicDialog element
2286          */
2287         getDialog : function(){
2288            if(!dlg){
2289                 dlg = new Roo.bootstrap.Modal( {
2290                     //draggable: true,
2291                     //resizable:false,
2292                     //constraintoviewport:false,
2293                     //fixedcenter:true,
2294                     //collapsible : false,
2295                     //shim:true,
2296                     //modal: true,
2297                   //  width:400,
2298                   //  height:100,
2299                     //buttonAlign:"center",
2300                     closeClick : function(){
2301                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2302                             handleButton("no");
2303                         }else{
2304                             handleButton("cancel");
2305                         }
2306                     }
2307                 });
2308                 dlg.render();
2309                 dlg.on("hide", handleHide);
2310                 mask = dlg.mask;
2311                 //dlg.addKeyListener(27, handleEsc);
2312                 buttons = {};
2313                 this.buttons = buttons;
2314                 var bt = this.buttonText;
2315                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2316                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2317                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2318                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2319                 Roo.log(buttons)
2320                 bodyEl = dlg.body.createChild({
2321
2322                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2323                         '<textarea class="roo-mb-textarea"></textarea>' +
2324                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2325                 });
2326                 msgEl = bodyEl.dom.firstChild;
2327                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2328                 textboxEl.enableDisplayMode();
2329                 textboxEl.addKeyListener([10,13], function(){
2330                     if(dlg.isVisible() && opt && opt.buttons){
2331                         if(opt.buttons.ok){
2332                             handleButton("ok");
2333                         }else if(opt.buttons.yes){
2334                             handleButton("yes");
2335                         }
2336                     }
2337                 });
2338                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2339                 textareaEl.enableDisplayMode();
2340                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2341                 progressEl.enableDisplayMode();
2342                 var pf = progressEl.dom.firstChild;
2343                 if (pf) {
2344                     pp = Roo.get(pf.firstChild);
2345                     pp.setHeight(pf.offsetHeight);
2346                 }
2347                 
2348             }
2349             return dlg;
2350         },
2351
2352         /**
2353          * Updates the message box body text
2354          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2355          * the XHTML-compliant non-breaking space character '&amp;#160;')
2356          * @return {Roo.MessageBox} This message box
2357          */
2358         updateText : function(text){
2359             if(!dlg.isVisible() && !opt.width){
2360                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2361             }
2362             msgEl.innerHTML = text || '&#160;';
2363       
2364             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2365             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2366             var w = Math.max(
2367                     Math.min(opt.width || cw , this.maxWidth), 
2368                     Math.max(opt.minWidth || this.minWidth, bwidth)
2369             );
2370             if(opt.prompt){
2371                 activeTextEl.setWidth(w);
2372             }
2373             if(dlg.isVisible()){
2374                 dlg.fixedcenter = false;
2375             }
2376             // to big, make it scroll. = But as usual stupid IE does not support
2377             // !important..
2378             
2379             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2380                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2381                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2382             } else {
2383                 bodyEl.dom.style.height = '';
2384                 bodyEl.dom.style.overflowY = '';
2385             }
2386             if (cw > w) {
2387                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2388             } else {
2389                 bodyEl.dom.style.overflowX = '';
2390             }
2391             
2392             dlg.setContentSize(w, bodyEl.getHeight());
2393             if(dlg.isVisible()){
2394                 dlg.fixedcenter = true;
2395             }
2396             return this;
2397         },
2398
2399         /**
2400          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2401          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2402          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2403          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2404          * @return {Roo.MessageBox} This message box
2405          */
2406         updateProgress : function(value, text){
2407             if(text){
2408                 this.updateText(text);
2409             }
2410             if (pp) { // weird bug on my firefox - for some reason this is not defined
2411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2412             }
2413             return this;
2414         },        
2415
2416         /**
2417          * Returns true if the message box is currently displayed
2418          * @return {Boolean} True if the message box is visible, else false
2419          */
2420         isVisible : function(){
2421             return dlg && dlg.isVisible();  
2422         },
2423
2424         /**
2425          * Hides the message box if it is displayed
2426          */
2427         hide : function(){
2428             if(this.isVisible()){
2429                 dlg.hide();
2430             }  
2431         },
2432
2433         /**
2434          * Displays a new message box, or reinitializes an existing message box, based on the config options
2435          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2436          * The following config object properties are supported:
2437          * <pre>
2438 Property    Type             Description
2439 ----------  ---------------  ------------------------------------------------------------------------------------
2440 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2441                                    closes (defaults to undefined)
2442 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2443                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2444 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2445                                    progress and wait dialogs will ignore this property and always hide the
2446                                    close button as they can only be closed programmatically.
2447 cls               String           A custom CSS class to apply to the message box element
2448 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2449                                    displayed (defaults to 75)
2450 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2451                                    function will be btn (the name of the button that was clicked, if applicable,
2452                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2453                                    Progress and wait dialogs will ignore this option since they do not respond to
2454                                    user actions and can only be closed programmatically, so any required function
2455                                    should be called by the same code after it closes the dialog.
2456 icon              String           A CSS class that provides a background image to be used as an icon for
2457                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2458 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2459 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2460 modal             Boolean          False to allow user interaction with the page while the message box is
2461                                    displayed (defaults to true)
2462 msg               String           A string that will replace the existing message box body text (defaults
2463                                    to the XHTML-compliant non-breaking space character '&#160;')
2464 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2465 progress          Boolean          True to display a progress bar (defaults to false)
2466 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2467 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2468 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2469 title             String           The title text
2470 value             String           The string value to set into the active textbox element if displayed
2471 wait              Boolean          True to display a progress bar (defaults to false)
2472 width             Number           The width of the dialog in pixels
2473 </pre>
2474          *
2475          * Example usage:
2476          * <pre><code>
2477 Roo.Msg.show({
2478    title: 'Address',
2479    msg: 'Please enter your address:',
2480    width: 300,
2481    buttons: Roo.MessageBox.OKCANCEL,
2482    multiline: true,
2483    fn: saveAddress,
2484    animEl: 'addAddressBtn'
2485 });
2486 </code></pre>
2487          * @param {Object} config Configuration options
2488          * @return {Roo.MessageBox} This message box
2489          */
2490         show : function(options)
2491         {
2492             
2493             // this causes nightmares if you show one dialog after another
2494             // especially on callbacks..
2495              
2496             if(this.isVisible()){
2497                 
2498                 this.hide();
2499                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2500                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2501                 Roo.log("New Dialog Message:" +  options.msg )
2502                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2503                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2504                 
2505             }
2506             var d = this.getDialog();
2507             opt = options;
2508             d.setTitle(opt.title || "&#160;");
2509             d.close.setDisplayed(opt.closable !== false);
2510             activeTextEl = textboxEl;
2511             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512             if(opt.prompt){
2513                 if(opt.multiline){
2514                     textboxEl.hide();
2515                     textareaEl.show();
2516                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2517                         opt.multiline : this.defaultTextHeight);
2518                     activeTextEl = textareaEl;
2519                 }else{
2520                     textboxEl.show();
2521                     textareaEl.hide();
2522                 }
2523             }else{
2524                 textboxEl.hide();
2525                 textareaEl.hide();
2526             }
2527             progressEl.setDisplayed(opt.progress === true);
2528             this.updateProgress(0);
2529             activeTextEl.dom.value = opt.value || "";
2530             if(opt.prompt){
2531                 dlg.setDefaultButton(activeTextEl);
2532             }else{
2533                 var bs = opt.buttons;
2534                 var db = null;
2535                 if(bs && bs.ok){
2536                     db = buttons["ok"];
2537                 }else if(bs && bs.yes){
2538                     db = buttons["yes"];
2539                 }
2540                 dlg.setDefaultButton(db);
2541             }
2542             bwidth = updateButtons(opt.buttons);
2543             this.updateText(opt.msg);
2544             if(opt.cls){
2545                 d.el.addClass(opt.cls);
2546             }
2547             d.proxyDrag = opt.proxyDrag === true;
2548             d.modal = opt.modal !== false;
2549             d.mask = opt.modal !== false ? mask : false;
2550             if(!d.isVisible()){
2551                 // force it to the end of the z-index stack so it gets a cursor in FF
2552                 document.body.appendChild(dlg.el.dom);
2553                 d.animateTarget = null;
2554                 d.show(options.animEl);
2555             }
2556             return this;
2557         },
2558
2559         /**
2560          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2561          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2562          * and closing the message box when the process is complete.
2563          * @param {String} title The title bar text
2564          * @param {String} msg The message box body text
2565          * @return {Roo.MessageBox} This message box
2566          */
2567         progress : function(title, msg){
2568             this.show({
2569                 title : title,
2570                 msg : msg,
2571                 buttons: false,
2572                 progress:true,
2573                 closable:false,
2574                 minWidth: this.minProgressWidth,
2575                 modal : true
2576             });
2577             return this;
2578         },
2579
2580         /**
2581          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2582          * If a callback function is passed it will be called after the user clicks the button, and the
2583          * id of the button that was clicked will be passed as the only parameter to the callback
2584          * (could also be the top-right close button).
2585          * @param {String} title The title bar text
2586          * @param {String} msg The message box body text
2587          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2588          * @param {Object} scope (optional) The scope of the callback function
2589          * @return {Roo.MessageBox} This message box
2590          */
2591         alert : function(title, msg, fn, scope){
2592             this.show({
2593                 title : title,
2594                 msg : msg,
2595                 buttons: this.OK,
2596                 fn: fn,
2597                 scope : scope,
2598                 modal : true
2599             });
2600             return this;
2601         },
2602
2603         /**
2604          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2605          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2606          * You are responsible for closing the message box when the process is complete.
2607          * @param {String} msg The message box body text
2608          * @param {String} title (optional) The title bar text
2609          * @return {Roo.MessageBox} This message box
2610          */
2611         wait : function(msg, title){
2612             this.show({
2613                 title : title,
2614                 msg : msg,
2615                 buttons: false,
2616                 closable:false,
2617                 progress:true,
2618                 modal:true,
2619                 width:300,
2620                 wait:true
2621             });
2622             waitTimer = Roo.TaskMgr.start({
2623                 run: function(i){
2624                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2625                 },
2626                 interval: 1000
2627             });
2628             return this;
2629         },
2630
2631         /**
2632          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2633          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2634          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2635          * @param {String} title The title bar text
2636          * @param {String} msg The message box body text
2637          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2638          * @param {Object} scope (optional) The scope of the callback function
2639          * @return {Roo.MessageBox} This message box
2640          */
2641         confirm : function(title, msg, fn, scope){
2642             this.show({
2643                 title : title,
2644                 msg : msg,
2645                 buttons: this.YESNO,
2646                 fn: fn,
2647                 scope : scope,
2648                 modal : true
2649             });
2650             return this;
2651         },
2652
2653         /**
2654          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2655          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2656          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2657          * (could also be the top-right close button) and the text that was entered will be passed as the two
2658          * parameters to the callback.
2659          * @param {String} title The title bar text
2660          * @param {String} msg The message box body text
2661          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2662          * @param {Object} scope (optional) The scope of the callback function
2663          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2664          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2665          * @return {Roo.MessageBox} This message box
2666          */
2667         prompt : function(title, msg, fn, scope, multiline){
2668             this.show({
2669                 title : title,
2670                 msg : msg,
2671                 buttons: this.OKCANCEL,
2672                 fn: fn,
2673                 minWidth:250,
2674                 scope : scope,
2675                 prompt:true,
2676                 multiline: multiline,
2677                 modal : true
2678             });
2679             return this;
2680         },
2681
2682         /**
2683          * Button config that displays a single OK button
2684          * @type Object
2685          */
2686         OK : {ok:true},
2687         /**
2688          * Button config that displays Yes and No buttons
2689          * @type Object
2690          */
2691         YESNO : {yes:true, no:true},
2692         /**
2693          * Button config that displays OK and Cancel buttons
2694          * @type Object
2695          */
2696         OKCANCEL : {ok:true, cancel:true},
2697         /**
2698          * Button config that displays Yes, No and Cancel buttons
2699          * @type Object
2700          */
2701         YESNOCANCEL : {yes:true, no:true, cancel:true},
2702
2703         /**
2704          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2705          * @type Number
2706          */
2707         defaultTextHeight : 75,
2708         /**
2709          * The maximum width in pixels of the message box (defaults to 600)
2710          * @type Number
2711          */
2712         maxWidth : 600,
2713         /**
2714          * The minimum width in pixels of the message box (defaults to 100)
2715          * @type Number
2716          */
2717         minWidth : 100,
2718         /**
2719          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2720          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2721          * @type Number
2722          */
2723         minProgressWidth : 250,
2724         /**
2725          * An object containing the default button text strings that can be overriden for localized language support.
2726          * Supported properties are: ok, cancel, yes and no.
2727          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2728          * @type Object
2729          */
2730         buttonText : {
2731             ok : "OK",
2732             cancel : "Cancel",
2733             yes : "Yes",
2734             no : "No"
2735         }
2736     };
2737 }();
2738
2739 /**
2740  * Shorthand for {@link Roo.MessageBox}
2741  */
2742 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2743 Roo.Msg = Roo.Msg || Roo.MessageBox;
2744 /*
2745  * - LGPL
2746  *
2747  * navbar
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.Navbar
2753  * @extends Roo.bootstrap.Component
2754  * Bootstrap Navbar class
2755
2756  * @constructor
2757  * Create a new Navbar
2758  * @param {Object} config The config object
2759  */
2760
2761
2762 Roo.bootstrap.Navbar = function(config){
2763     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2764     
2765 };
2766
2767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2768     
2769     
2770    
2771     // private
2772     navItems : false,
2773     loadMask : false,
2774     
2775     
2776     getAutoCreate : function(){
2777         
2778         
2779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2780         
2781     },
2782     
2783     initEvents :function ()
2784     {
2785         //Roo.log(this.el.select('.navbar-toggle',true));
2786         this.el.select('.navbar-toggle',true).on('click', function() {
2787            // Roo.log('click');
2788             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2789         }, this);
2790         
2791         var mark = {
2792             tag: "div",
2793             cls:"x-dlg-mask"
2794         }
2795         
2796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2797         
2798         var size = this.el.getSize();
2799         this.maskEl.setSize(size.width, size.height);
2800         this.maskEl.enableDisplayMode("block");
2801         this.maskEl.hide();
2802         
2803         if(this.loadMask){
2804             this.maskEl.show();
2805         }
2806     },
2807     
2808     
2809     getChildContainer : function()
2810     {
2811         if (this.el.select('.collapse').getCount()) {
2812             return this.el.select('.collapse',true).first();
2813         }
2814         
2815         return this.el;
2816     },
2817     
2818     mask : function()
2819     {
2820         this.maskEl.show();
2821     },
2822     
2823     unmask : function()
2824     {
2825         this.maskEl.hide();
2826     } 
2827     
2828     
2829     
2830     
2831 });
2832
2833
2834
2835  
2836
2837  /*
2838  * - LGPL
2839  *
2840  * navbar
2841  * 
2842  */
2843
2844 /**
2845  * @class Roo.bootstrap.NavSimplebar
2846  * @extends Roo.bootstrap.Navbar
2847  * Bootstrap Sidebar class
2848  *
2849  * @cfg {Boolean} inverse is inverted color
2850  * 
2851  * @cfg {String} type (nav | pills | tabs)
2852  * @cfg {Boolean} arrangement stacked | justified
2853  * @cfg {String} align (left | right) alignment
2854  * 
2855  * @cfg {Boolean} main (true|false) main nav bar? default false
2856  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2857  * 
2858  * @cfg {String} tag (header|footer|nav|div) default is nav 
2859
2860  * 
2861  * 
2862  * 
2863  * @constructor
2864  * Create a new Sidebar
2865  * @param {Object} config The config object
2866  */
2867
2868
2869 Roo.bootstrap.NavSimplebar = function(config){
2870     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2871 };
2872
2873 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2874     
2875     inverse: false,
2876     
2877     type: false,
2878     arrangement: '',
2879     align : false,
2880     
2881     
2882     
2883     main : false,
2884     
2885     
2886     tag : false,
2887     
2888     
2889     getAutoCreate : function(){
2890         
2891         
2892         var cfg = {
2893             tag : this.tag || 'div',
2894             cls : 'navbar'
2895         };
2896           
2897         
2898         cfg.cn = [
2899             {
2900                 cls: 'nav',
2901                 tag : 'ul'
2902             }
2903         ];
2904         
2905          
2906         this.type = this.type || 'nav';
2907         if (['tabs','pills'].indexOf(this.type)!==-1) {
2908             cfg.cn[0].cls += ' nav-' + this.type
2909         
2910         
2911         } else {
2912             if (this.type!=='nav') {
2913                 Roo.log('nav type must be nav/tabs/pills')
2914             }
2915             cfg.cn[0].cls += ' navbar-nav'
2916         }
2917         
2918         
2919         
2920         
2921         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2922             cfg.cn[0].cls += ' nav-' + this.arrangement;
2923         }
2924         
2925         
2926         if (this.align === 'right') {
2927             cfg.cn[0].cls += ' navbar-right';
2928         }
2929         
2930         if (this.inverse) {
2931             cfg.cls += ' navbar-inverse';
2932             
2933         }
2934         
2935         
2936         return cfg;
2937     
2938         
2939     }
2940     
2941     
2942     
2943 });
2944
2945
2946
2947  
2948
2949  
2950        /*
2951  * - LGPL
2952  *
2953  * navbar
2954  * 
2955  */
2956
2957 /**
2958  * @class Roo.bootstrap.NavHeaderbar
2959  * @extends Roo.bootstrap.NavSimplebar
2960  * Bootstrap Sidebar class
2961  *
2962  * @cfg {String} brand what is brand
2963  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2964  * @cfg {String} brand_href href of the brand
2965  * 
2966  * @constructor
2967  * Create a new Sidebar
2968  * @param {Object} config The config object
2969  */
2970
2971
2972 Roo.bootstrap.NavHeaderbar = function(config){
2973     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2974 };
2975
2976 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
2977     
2978     position: '',
2979     brand: '',
2980     brand_href: false,
2981     
2982     
2983     getAutoCreate : function(){
2984         
2985         
2986         
2987         var   cfg = {
2988             tag: this.nav || 'nav',
2989             cls: 'navbar',
2990             role: 'navigation',
2991             cn: [
2992                 {
2993                     tag: 'div',
2994                     cls: 'navbar-header',
2995                     cn: [
2996                         {
2997                         tag: 'button',
2998                         type: 'button',
2999                         cls: 'navbar-toggle',
3000                         'data-toggle': 'collapse',
3001                         cn: [
3002                             {
3003                                 tag: 'span',
3004                                 cls: 'sr-only',
3005                                 html: 'Toggle navigation'
3006                             },
3007                             {
3008                                 tag: 'span',
3009                                 cls: 'icon-bar'
3010                             },
3011                             {
3012                                 tag: 'span',
3013                                 cls: 'icon-bar'
3014                             },
3015                             {
3016                                 tag: 'span',
3017                                 cls: 'icon-bar'
3018                             }
3019                         ]
3020                         }
3021                     ]
3022                 },
3023                 {
3024                 tag: 'div',
3025                 cls: 'collapse navbar-collapse'
3026                 }
3027             ]
3028         };
3029         
3030         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3031         
3032         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3033             cfg.cls += ' navbar-' + this.position;
3034             
3035             // tag can override this..
3036             
3037             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3038         }
3039         
3040         if (this.brand !== '') {
3041             cfg.cn[0].cn.push({
3042                 tag: 'a',
3043                 href: this.brand_href ? this.brand_href : '#',
3044                 cls: 'navbar-brand',
3045                 cn: [
3046                 this.brand
3047                 ]
3048             });
3049         }
3050         
3051         if(this.main){
3052             cfg.cls += ' main-nav';
3053         }
3054         
3055         
3056         return cfg;
3057
3058         
3059     }
3060     
3061     
3062     
3063 });
3064
3065
3066
3067  
3068
3069  /*
3070  * - LGPL
3071  *
3072  * navbar
3073  * 
3074  */
3075
3076 /**
3077  * @class Roo.bootstrap.NavSidebar
3078  * @extends Roo.bootstrap.Navbar
3079  * Bootstrap Sidebar class
3080  * 
3081  * @constructor
3082  * Create a new Sidebar
3083  * @param {Object} config The config object
3084  */
3085
3086
3087 Roo.bootstrap.NavSidebar = function(config){
3088     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3089 };
3090
3091 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3092     
3093     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3094     
3095     getAutoCreate : function(){
3096         
3097         
3098         return  {
3099             tag: 'div',
3100             cls: 'sidebar sidebar-nav'
3101         };
3102     
3103         
3104     }
3105     
3106     
3107     
3108 });
3109
3110
3111
3112  
3113
3114  /*
3115  * - LGPL
3116  *
3117  * nav group
3118  * 
3119  */
3120
3121 /**
3122  * @class Roo.bootstrap.NavGroup
3123  * @extends Roo.bootstrap.Component
3124  * Bootstrap NavGroup class
3125  * @cfg {String} align left | right
3126  * @cfg {Boolean} inverse false | true
3127  * @cfg {String} type (nav|pills|tab) default nav
3128  * @cfg {String} navId - reference Id for navbar.
3129
3130  * 
3131  * @constructor
3132  * Create a new nav group
3133  * @param {Object} config The config object
3134  */
3135
3136 Roo.bootstrap.NavGroup = function(config){
3137     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3138     this.navItems = [];
3139     Roo.bootstrap.NavGroup.register(this);
3140      this.addEvents({
3141         /**
3142              * @event changed
3143              * Fires when the active item changes
3144              * @param {Roo.bootstrap.NavGroup} this
3145              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3146              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3147          */
3148         'changed': true
3149      });
3150     
3151 };
3152
3153 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3154     
3155     align: '',
3156     inverse: false,
3157     form: false,
3158     type: 'nav',
3159     navId : '',
3160     // private
3161     
3162     navItems : false,
3163     
3164     getAutoCreate : function()
3165     {
3166         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3167         
3168         cfg = {
3169             tag : 'ul',
3170             cls: 'nav' 
3171         }
3172         
3173         if (['tabs','pills'].indexOf(this.type)!==-1) {
3174             cfg.cls += ' nav-' + this.type
3175         } else {
3176             if (this.type!=='nav') {
3177                 Roo.log('nav type must be nav/tabs/pills')
3178             }
3179             cfg.cls += ' navbar-nav'
3180         }
3181         
3182         if (this.parent().sidebar) {
3183             cfg = {
3184                 tag: 'ul',
3185                 cls: 'dashboard-menu sidebar-menu'
3186             }
3187             
3188             return cfg;
3189         }
3190         
3191         if (this.form === true) {
3192             cfg = {
3193                 tag: 'form',
3194                 cls: 'navbar-form'
3195             }
3196             
3197             if (this.align === 'right') {
3198                 cfg.cls += ' navbar-right';
3199             } else {
3200                 cfg.cls += ' navbar-left';
3201             }
3202         }
3203         
3204         if (this.align === 'right') {
3205             cfg.cls += ' navbar-right';
3206         }
3207         
3208         if (this.inverse) {
3209             cfg.cls += ' navbar-inverse';
3210             
3211         }
3212         
3213         
3214         return cfg;
3215     },
3216     
3217     setActiveItem : function(item)
3218     {
3219         var prev = false;
3220         Roo.each(this.navItems, function(v){
3221             if (v == item) {
3222                 return ;
3223             }
3224             if (v.isActive()) {
3225                 v.setActive(false, true);
3226                 prev = v;
3227                 
3228             }
3229             
3230         });
3231
3232         item.setActive(true, true);
3233         this.fireEvent('changed', this, item, prev);
3234         
3235         
3236     },
3237     
3238     addItem : function(cfg)
3239     {
3240         var cn = new Roo.bootstrap.NavItem(cfg);
3241         this.register(cn);
3242         cn.parentId = this.id;
3243         cn.onRender(this.el, null);
3244         return cn;
3245     },
3246     
3247     register : function(item)
3248     {
3249         this.navItems.push( item);
3250         item.navId = this.navId;
3251     
3252     },
3253     getNavItem: function(tabId)
3254     {
3255         var ret = false;
3256         Roo.each(this.navItems, function(e) {
3257             if (e.tabId == tabId) {
3258                ret =  e;
3259                return false;
3260             }
3261             return true;
3262             
3263         });
3264         return ret;
3265     }
3266     
3267     
3268     
3269     
3270 });
3271
3272  
3273 Roo.apply(Roo.bootstrap.NavGroup, {
3274     
3275     groups: {},
3276     
3277     register : function(navgrp)
3278     {
3279         this.groups[navgrp.navId] = navgrp;
3280         
3281     },
3282     get: function(navId) {
3283         return this.groups[navId];
3284     }
3285     
3286     
3287     
3288 });
3289
3290  /*
3291  * - LGPL
3292  *
3293  * row
3294  * 
3295  */
3296
3297 /**
3298  * @class Roo.bootstrap.NavItem
3299  * @extends Roo.bootstrap.Component
3300  * Bootstrap Navbar.NavItem class
3301  * @cfg {String} href  link to
3302  * @cfg {String} html content of button
3303  * @cfg {String} badge text inside badge
3304  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3305  * @cfg {String} glyphicon name of glyphicon
3306  * @cfg {String} icon name of font awesome icon
3307  * @cfg {Boolean} active Is item active
3308  * @cfg {Boolean} disabled Is item disabled
3309  
3310  * @cfg {Boolean} preventDefault (true | false) default false
3311  * @cfg {String} tabId the tab that this item activates.
3312  * @cfg {String} tagtype (a|span) render as a href or span?
3313   
3314  * @constructor
3315  * Create a new Navbar Item
3316  * @param {Object} config The config object
3317  */
3318 Roo.bootstrap.NavItem = function(config){
3319     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3320     this.addEvents({
3321         // raw events
3322         /**
3323          * @event click
3324          * The raw click event for the entire grid.
3325          * @param {Roo.EventObject} e
3326          */
3327         "click" : true,
3328          /**
3329             * @event changed
3330             * Fires when the active item active state changes
3331             * @param {Roo.bootstrap.NavItem} this
3332             * @param {boolean} state the new state
3333              
3334          */
3335         'changed': true
3336     });
3337    
3338 };
3339
3340 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3341     
3342     href: false,
3343     html: '',
3344     badge: '',
3345     icon: false,
3346     glyphicon: false,
3347     active: false,
3348     preventDefault : false,
3349     tabId : false,
3350     tagtype : 'a',
3351     disabled : false,
3352     
3353     getAutoCreate : function(){
3354          
3355         var cfg = {
3356             tag: 'li',
3357             cls: 'nav-item',
3358             cn : [
3359                 {
3360                     tag: this.tagtype,
3361                     href : this.href || "#",
3362                     html: this.html || ''
3363                 }
3364             ]
3365         }
3366             
3367         if (this.active) {
3368             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3369         }
3370             
3371         // glyphicon and icon go before content..
3372         if (this.glyphicon || this.icon) {
3373              if (this.icon) {
3374                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3375             } else {
3376                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3377             }
3378         }
3379         
3380         
3381         
3382         if (this.menu) {
3383             
3384             cfg.cn[0].html += " <span class='caret'></span>";
3385          
3386         }
3387         
3388         if (this.badge !== '') {
3389              
3390             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3391         }
3392         if (this.disabled) {
3393             cfg.cls += ' disabled';
3394         }
3395         
3396         
3397         return cfg;
3398     },
3399     initEvents: function() {
3400        // Roo.log('init events?');
3401        // Roo.log(this.el.dom);
3402         if (typeof (this.menu) != 'undefined') {
3403             this.menu.parentType = this.xtype;
3404             this.menu.triggerEl = this.el;
3405             this.addxtype(Roo.apply({}, this.menu));
3406         }
3407
3408        
3409         this.el.select('a',true).on('click', this.onClick, this);
3410         // at this point parent should be available..
3411         this.parent().register(this);
3412     },
3413     
3414     onClick : function(e)
3415     {
3416          
3417         if(this.preventDefault){
3418             e.preventDefault();
3419         }
3420         if (this.disabled) {
3421             return;
3422         }
3423         Roo.log("fire event clicked");
3424         if(this.fireEvent('click', this, e) === false){
3425             return;
3426         };
3427         
3428         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3429              if (typeof(this.parent().setActiveItem) !== 'undefined') {
3430                 this.parent().setActiveItem(this);
3431             }
3432             
3433             
3434             
3435         } 
3436     },
3437     
3438     isActive: function () {
3439         return this.active
3440     },
3441     setActive : function(state, fire)
3442     {
3443         this.active = state;
3444         if (!state ) {
3445             this.el.removeClass('active');
3446         } else if (!this.el.hasClass('active')) {
3447             this.el.addClass('active');
3448         }
3449         if (fire) {
3450             this.fireEvent('changed', this, state);
3451         }
3452         
3453         
3454     },
3455      // this should not be here...
3456     setDisabled : function(state)
3457     {
3458         this.disabled = state;
3459         if (!state ) {
3460             this.el.removeClass('disabled');
3461         } else if (!this.el.hasClass('disabled')) {
3462             this.el.addClass('disabled');
3463         }
3464         
3465     }
3466 });
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * sidebar item
3473  *
3474  *  li
3475  *    <span> icon </span>
3476  *    <span> text </span>
3477  *    <span>badge </span>
3478  */
3479
3480 /**
3481  * @class Roo.bootstrap.NavSidebarItem
3482  * @extends Roo.bootstrap.NavItem
3483  * Bootstrap Navbar.NavSidebarItem class
3484  * @constructor
3485  * Create a new Navbar Button
3486  * @param {Object} config The config object
3487  */
3488 Roo.bootstrap.NavSidebarItem = function(config){
3489     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3490     this.addEvents({
3491         // raw events
3492         /**
3493          * @event click
3494          * The raw click event for the entire grid.
3495          * @param {Roo.EventObject} e
3496          */
3497         "click" : true,
3498          /**
3499             * @event changed
3500             * Fires when the active item active state changes
3501             * @param {Roo.bootstrap.NavSidebarItem} this
3502             * @param {boolean} state the new state
3503              
3504          */
3505         'changed': true
3506     });
3507    
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3511     
3512     
3513     getAutoCreate : function(){
3514         
3515         
3516         var a = {
3517                 tag: 'a',
3518                 href : this.href || '#',
3519                 cls: '',
3520                 html : '',
3521                 cn : []
3522         };
3523         var cfg = {
3524             tag: 'li',
3525             cls: '',
3526             cn: [ a ]
3527         }
3528         var span = {
3529             tag: 'span',
3530             html : this.html || ''
3531         }
3532         
3533         
3534         if (this.active) {
3535             cfg.cls += ' active';
3536         }
3537         
3538         // left icon..
3539         if (this.glyphicon || this.icon) {
3540             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3541             a.cn.push({ tag : 'i', cls : c }) ;
3542         }
3543         // html..
3544         a.cn.push(span);
3545         // then badge..
3546         if (this.badge !== '') {
3547             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3548         }
3549         // fi
3550         if (this.menu) {
3551             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3552             a.cls += 'dropdown-toggle treeview' ;
3553             
3554         }
3555         
3556         
3557         
3558         return cfg;
3559          
3560            
3561     }
3562    
3563      
3564  
3565 });
3566  
3567
3568  /*
3569  * - LGPL
3570  *
3571  * row
3572  * 
3573  */
3574
3575 /**
3576  * @class Roo.bootstrap.Row
3577  * @extends Roo.bootstrap.Component
3578  * Bootstrap Row class (contains columns...)
3579  * 
3580  * @constructor
3581  * Create a new Row
3582  * @param {Object} config The config object
3583  */
3584
3585 Roo.bootstrap.Row = function(config){
3586     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3587 };
3588
3589 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3590     
3591     getAutoCreate : function(){
3592        return {
3593             cls: 'row clearfix'
3594        };
3595     }
3596     
3597     
3598 });
3599
3600  
3601
3602  /*
3603  * - LGPL
3604  *
3605  * element
3606  * 
3607  */
3608
3609 /**
3610  * @class Roo.bootstrap.Element
3611  * @extends Roo.bootstrap.Component
3612  * Bootstrap Element class
3613  * @cfg {String} html contents of the element
3614  * @cfg {String} tag tag of the element
3615  * @cfg {String} cls class of the element
3616  * 
3617  * @constructor
3618  * Create a new Element
3619  * @param {Object} config The config object
3620  */
3621
3622 Roo.bootstrap.Element = function(config){
3623     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3624 };
3625
3626 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3627     
3628     tag: 'div',
3629     cls: '',
3630     html: '',
3631      
3632     
3633     getAutoCreate : function(){
3634         
3635         var cfg = {
3636             tag: this.tag,
3637             cls: this.cls,
3638             html: this.html
3639         }
3640         
3641         
3642         
3643         return cfg;
3644     }
3645    
3646 });
3647
3648  
3649
3650  /*
3651  * - LGPL
3652  *
3653  * pagination
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Pagination
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Pagination class
3661  * @cfg {String} size xs | sm | md | lg
3662  * @cfg {Boolean} inverse false | true
3663  * 
3664  * @constructor
3665  * Create a new Pagination
3666  * @param {Object} config The config object
3667  */
3668
3669 Roo.bootstrap.Pagination = function(config){
3670     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3671 };
3672
3673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3674     
3675     cls: false,
3676     size: false,
3677     inverse: false,
3678     
3679     getAutoCreate : function(){
3680         var cfg = {
3681             tag: 'ul',
3682                 cls: 'pagination'
3683         };
3684         if (this.inverse) {
3685             cfg.cls += ' inverse';
3686         }
3687         if (this.html) {
3688             cfg.html=this.html;
3689         }
3690         if (this.cls) {
3691             cfg.cls += " " + this.cls;
3692         }
3693         return cfg;
3694     }
3695    
3696 });
3697
3698  
3699
3700  /*
3701  * - LGPL
3702  *
3703  * Pagination item
3704  * 
3705  */
3706
3707
3708 /**
3709  * @class Roo.bootstrap.PaginationItem
3710  * @extends Roo.bootstrap.Component
3711  * Bootstrap PaginationItem class
3712  * @cfg {String} html text
3713  * @cfg {String} href the link
3714  * @cfg {Boolean} preventDefault (true | false) default true
3715  * @cfg {Boolean} active (true | false) default false
3716  * 
3717  * 
3718  * @constructor
3719  * Create a new PaginationItem
3720  * @param {Object} config The config object
3721  */
3722
3723
3724 Roo.bootstrap.PaginationItem = function(config){
3725     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3726     this.addEvents({
3727         // raw events
3728         /**
3729          * @event click
3730          * The raw click event for the entire grid.
3731          * @param {Roo.EventObject} e
3732          */
3733         "click" : true
3734     });
3735 };
3736
3737 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3738     
3739     href : false,
3740     html : false,
3741     preventDefault: true,
3742     active : false,
3743     cls : false,
3744     
3745     getAutoCreate : function(){
3746         var cfg= {
3747             tag: 'li',
3748             cn: [
3749                 {
3750                     tag : 'a',
3751                     href : this.href ? this.href : '#',
3752                     html : this.html ? this.html : ''
3753                 }
3754             ]
3755         };
3756         
3757         if(this.cls){
3758             cfg.cls = this.cls;
3759         }
3760         
3761         if(this.active){
3762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3763         }
3764         
3765         return cfg;
3766     },
3767     
3768     initEvents: function() {
3769         
3770         this.el.on('click', this.onClick, this);
3771         
3772     },
3773     onClick : function(e)
3774     {
3775         Roo.log('PaginationItem on click ');
3776         if(this.preventDefault){
3777             e.preventDefault();
3778         }
3779         
3780         this.fireEvent('click', this, e);
3781     }
3782    
3783 });
3784
3785  
3786
3787  /*
3788  * - LGPL
3789  *
3790  * slider
3791  * 
3792  */
3793
3794
3795 /**
3796  * @class Roo.bootstrap.Slider
3797  * @extends Roo.bootstrap.Component
3798  * Bootstrap Slider class
3799  *    
3800  * @constructor
3801  * Create a new Slider
3802  * @param {Object} config The config object
3803  */
3804
3805 Roo.bootstrap.Slider = function(config){
3806     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3807 };
3808
3809 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3810     
3811     getAutoCreate : function(){
3812         
3813         var cfg = {
3814             tag: 'div',
3815             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3816             cn: [
3817                 {
3818                     tag: 'a',
3819                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3820                 }
3821             ]
3822         }
3823         
3824         return cfg;
3825     }
3826    
3827 });
3828
3829  /*
3830  * - LGPL
3831  *
3832  * table
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Table
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Table class
3840  * @cfg {String} cls table class
3841  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3842  * @cfg {String} bgcolor Specifies the background color for a table
3843  * @cfg {Number} border Specifies whether the table cells should have borders or not
3844  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3845  * @cfg {Number} cellspacing Specifies the space between cells
3846  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3847  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3848  * @cfg {String} sortable Specifies that the table should be sortable
3849  * @cfg {String} summary Specifies a summary of the content of a table
3850  * @cfg {Number} width Specifies the width of a table
3851  * 
3852  * @cfg {boolean} striped Should the rows be alternative striped
3853  * @cfg {boolean} bordered Add borders to the table
3854  * @cfg {boolean} hover Add hover highlighting
3855  * @cfg {boolean} condensed Format condensed
3856  * @cfg {boolean} responsive Format condensed
3857  * @cfg {Boolean} loadMask (true|false) default false
3858  *
3859  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
3860  
3861  * 
3862  * @constructor
3863  * Create a new Table
3864  * @param {Object} config The config object
3865  */
3866
3867 Roo.bootstrap.Table = function(config){
3868     Roo.bootstrap.Table.superclass.constructor.call(this, config);
3869     
3870     if (this.sm) {
3871         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3872         this.sm = this.selModel;
3873         this.sm.xmodule = this.xmodule || false;
3874     }
3875     if (this.cm && typeof(this.cm.config) == 'undefined') {
3876         this.colModel = Roo.factory(this.cm, Roo.grid);
3877         this.cm = this.colModel;
3878         this.cm.xmodule = this.xmodule || false;
3879     }
3880     if (this.store) {
3881         this.store= Roo.factory(this.store, Roo.data);
3882         this.ds = this.store;
3883         this.ds.xmodule = this.xmodule || false;
3884          
3885     }
3886     if (this.footer && this.store) {
3887         this.footer.dataSource = this.ds;
3888         this.footer = Roo.factory(this.footer);
3889     }
3890 };
3891
3892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
3893     
3894     cls: false,
3895     align: false,
3896     bgcolor: false,
3897     border: false,
3898     cellpadding: false,
3899     cellspacing: false,
3900     frame: false,
3901     rules: false,
3902     sortable: false,
3903     summary: false,
3904     width: false,
3905     striped : false,
3906     bordered: false,
3907     hover:  false,
3908     condensed : false,
3909     responsive : false,
3910     sm : false,
3911     cm : false,
3912     store : false,
3913     loadMask : false,
3914     
3915     getAutoCreate : function(){
3916         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3917         
3918         cfg = {
3919             tag: 'table',
3920             cls : 'table',
3921             cn : []
3922         }
3923             
3924         if (this.striped) {
3925             cfg.cls += ' table-striped';
3926         }
3927         if (this.hover) {
3928             cfg.cls += ' table-hover';
3929         }
3930         if (this.bordered) {
3931             cfg.cls += ' table-bordered';
3932         }
3933         if (this.condensed) {
3934             cfg.cls += ' table-condensed';
3935         }
3936         if (this.responsive) {
3937             cfg.cls += ' table-responsive';
3938         }
3939         
3940           
3941         
3942         
3943         if (this.cls) {
3944             cfg.cls+=  ' ' +this.cls;
3945         }
3946         
3947         // this lot should be simplifed...
3948         
3949         if (this.align) {
3950             cfg.align=this.align;
3951         }
3952         if (this.bgcolor) {
3953             cfg.bgcolor=this.bgcolor;
3954         }
3955         if (this.border) {
3956             cfg.border=this.border;
3957         }
3958         if (this.cellpadding) {
3959             cfg.cellpadding=this.cellpadding;
3960         }
3961         if (this.cellspacing) {
3962             cfg.cellspacing=this.cellspacing;
3963         }
3964         if (this.frame) {
3965             cfg.frame=this.frame;
3966         }
3967         if (this.rules) {
3968             cfg.rules=this.rules;
3969         }
3970         if (this.sortable) {
3971             cfg.sortable=this.sortable;
3972         }
3973         if (this.summary) {
3974             cfg.summary=this.summary;
3975         }
3976         if (this.width) {
3977             cfg.width=this.width;
3978         }
3979         
3980         if(this.store || this.cm){
3981             cfg.cn.push(this.renderHeader());
3982             cfg.cn.push(this.renderBody());
3983             cfg.cn.push(this.renderFooter());
3984             
3985             cfg.cls+=  ' TableGrid';
3986         }
3987         
3988         return cfg;
3989     },
3990 //    
3991 //    initTableGrid : function()
3992 //    {
3993 //        var cfg = {};
3994 //        
3995 //        var header = {
3996 //            tag: 'thead',
3997 //            cn : []
3998 //        };
3999 //        
4000 //        var cm = this.cm;
4001 //        
4002 //        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4003 //            header.cn.push({
4004 //                tag: 'th',
4005 //                html: cm.getColumnHeader(i)
4006 //            })
4007 //        }
4008 //        
4009 //        cfg.push(header);
4010 //        
4011 //        return cfg;
4012 //        
4013 //        
4014 //    },
4015     
4016     initEvents : function()
4017     {   
4018         if(!this.store || !this.cm){
4019             return;
4020         }
4021         
4022         Roo.log('initEvents with ds!!!!');
4023         
4024         var _this = this;
4025         
4026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4027             e.on('click', _this.sort, _this);
4028         });
4029 //        this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4030 //        this.maskEl.enableDisplayMode("block");
4031 //        this.maskEl.show();
4032         
4033         this.parent().el.setStyle('position', 'relative');
4034         if (this.footer) {
4035             this.footer.parentId = this.id;
4036             this.footer.onRender(this.el.select('tfoot tr td'), null);        
4037         }
4038         
4039         
4040         // mask should be using Roo.bootstrap.Mask...
4041         
4042         var mark = {
4043             tag: "div",
4044             cls:"x-dlg-mask",
4045             style: "text-align:center",
4046             cn: [
4047                 {
4048                     tag: "div",
4049                     style: "background-color:white;width:50%;margin:100 auto",
4050                     cn: [
4051                         {
4052                             tag: "img",
4053                             src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
4054                         },
4055                         {
4056                             tag: "span",
4057                             html: "Loading"
4058                         }
4059                         
4060                     ]
4061                 }
4062             ]
4063         }
4064         this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
4065         
4066         var size = this.parent().el.getSize();
4067         
4068         this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
4069         
4070         this.maskEl.enableDisplayMode("block");
4071         
4072         this.store.on('load', this.onLoad, this);
4073         this.store.on('beforeload', this.onBeforeLoad, this);
4074         
4075         this.store.load();
4076         
4077         
4078         
4079     },
4080     
4081     sort : function(e,el)
4082     {
4083         var col = Roo.get(el)
4084         
4085         if(!col.hasClass('sortable')){
4086             return;
4087         }
4088         
4089         var sort = col.attr('sort');
4090         var dir = 'ASC';
4091         
4092         if(col.hasClass('glyphicon-arrow-up')){
4093             dir = 'DESC';
4094         }
4095         
4096         this.store.sortInfo = {field : sort, direction : dir};
4097         
4098         this.store.load();
4099     },
4100     
4101     renderHeader : function()
4102     {
4103         var header = {
4104             tag: 'thead',
4105             cn : []
4106         };
4107         
4108         var cm = this.cm;
4109         
4110         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4111             
4112             var config = cm.config[i];
4113             
4114             if(typeof(config.hidden) != 'undefined' && config.hidden){
4115                 continue;
4116             }
4117                     
4118             var c = {
4119                 tag: 'th',
4120                 html: cm.getColumnHeader(i)
4121             };
4122             
4123             if(typeof(config.dataIndex) != 'undefined'){
4124                 c.sort = config.dataIndex;
4125             }
4126             
4127             if(typeof(config.sortable) != 'undefined' && config.sortable){
4128                 c.cls = 'sortable';
4129             }
4130             
4131             if(typeof(config.width) != 'undefined'){
4132                 c.style = 'width:' + config.width + 'px';
4133             }
4134             
4135             header.cn.push(c)
4136         }
4137         
4138         return header;
4139     },
4140     
4141     renderBody : function()
4142     {
4143         var body = {
4144             tag: 'tbody',
4145             cn : []
4146         };
4147         
4148         return body;
4149     },
4150     
4151     renderFooter : function()
4152     {
4153         var footer = {
4154             tag: 'tfoot',
4155             cn : [
4156                 {
4157                     tag: 'tr',
4158                     cn : [
4159                         {
4160                             tag : 'td',
4161                             colspan : 1
4162                         }
4163                     ]
4164                 }
4165             ]
4166         };
4167         
4168         return footer;
4169     },
4170     
4171     onLoad : function()
4172     {
4173         Roo.log('ds onload');
4174         
4175         var _this = this;
4176         var cm = this.cm;
4177         
4178         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4179             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4180             
4181             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4182                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4183             }
4184             
4185             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4186                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4187             }
4188         });
4189         
4190         var tbody = this.el.select('tbody', true).first();
4191         
4192         var renders = [];
4193                     
4194         if(this.store.getCount() > 0){
4195             this.store.data.each(function(d){
4196                 var row = {
4197                     tag : 'tr',
4198                     cn : []
4199                 };
4200                 
4201                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4202                     var config = cm.config[i];
4203                     
4204                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4205                         continue;
4206                     }
4207                     
4208                     var renderer = cm.getRenderer(i);
4209                     var value = '';
4210                     var id = Roo.id();
4211                     
4212                     if(typeof(renderer) !== 'undefined'){
4213                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4214                     }
4215                     
4216                     if(typeof(value) === 'object'){
4217                         renders.push({
4218                             id : id,
4219                             cfg : value 
4220                         })
4221                     }
4222                     
4223                     var td = {
4224                         tag: 'td',
4225                         id: id,
4226                         html: (typeof(value) === 'object') ? '' : value
4227                     };
4228                     
4229                     if(typeof(config.width) != 'undefined'){
4230                         td.style = 'width:' +  config.width + 'px';
4231                     }
4232                     
4233                     row.cn.push(td);
4234                    
4235                 }
4236                 
4237                 tbody.createChild(row);
4238                 
4239             });
4240         }
4241         
4242         
4243         if(renders.length){
4244             var _this = this;
4245             Roo.each(renders, function(r){
4246                 _this.renderColumn(r);
4247             })
4248         }
4249
4250         if(this.loadMask){
4251             this.maskEl.hide();
4252         }
4253     },
4254     
4255     onBeforeLoad : function()
4256     {
4257         Roo.log('ds onBeforeLoad');
4258         
4259         this.clear();
4260         
4261         if(this.loadMask){
4262             this.maskEl.show();
4263         }
4264     },
4265     
4266     clear : function()
4267     {
4268         this.el.select('tbody', true).first().dom.innerHTML = '';
4269     },
4270     
4271     getSelectionModel : function(){
4272         if(!this.selModel){
4273             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4274         }
4275         return this.selModel;
4276     },
4277     
4278     renderColumn : function(r)
4279     {
4280         var _this = this;
4281         r.cfg.render(Roo.get(r.id));
4282         
4283         if(r.cfg.cn){
4284             Roo.each(r.cfg.cn, function(c){
4285                 var child = {
4286                     id: r.id,
4287                     cfg: c
4288                 }
4289                 _this.renderColumn(child);
4290             })
4291         }
4292     }
4293    
4294 });
4295
4296  
4297
4298  /*
4299  * - LGPL
4300  *
4301  * table cell
4302  * 
4303  */
4304
4305 /**
4306  * @class Roo.bootstrap.TableCell
4307  * @extends Roo.bootstrap.Component
4308  * Bootstrap TableCell class
4309  * @cfg {String} html cell contain text
4310  * @cfg {String} cls cell class
4311  * @cfg {String} tag cell tag (td|th) default td
4312  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4313  * @cfg {String} align Aligns the content in a cell
4314  * @cfg {String} axis Categorizes cells
4315  * @cfg {String} bgcolor Specifies the background color of a cell
4316  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4317  * @cfg {Number} colspan Specifies the number of columns a cell should span
4318  * @cfg {String} headers Specifies one or more header cells a cell is related to
4319  * @cfg {Number} height Sets the height of a cell
4320  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4321  * @cfg {Number} rowspan Sets the number of rows a cell should span
4322  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4323  * @cfg {String} valign Vertical aligns the content in a cell
4324  * @cfg {Number} width Specifies the width of a cell
4325  * 
4326  * @constructor
4327  * Create a new TableCell
4328  * @param {Object} config The config object
4329  */
4330
4331 Roo.bootstrap.TableCell = function(config){
4332     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4333 };
4334
4335 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
4336     
4337     html: false,
4338     cls: false,
4339     tag: false,
4340     abbr: false,
4341     align: false,
4342     axis: false,
4343     bgcolor: false,
4344     charoff: false,
4345     colspan: false,
4346     headers: false,
4347     height: false,
4348     nowrap: false,
4349     rowspan: false,
4350     scope: false,
4351     valign: false,
4352     width: false,
4353     
4354     
4355     getAutoCreate : function(){
4356         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4357         
4358         cfg = {
4359             tag: 'td'
4360         }
4361         
4362         if(this.tag){
4363             cfg.tag = this.tag;
4364         }
4365         
4366         if (this.html) {
4367             cfg.html=this.html
4368         }
4369         if (this.cls) {
4370             cfg.cls=this.cls
4371         }
4372         if (this.abbr) {
4373             cfg.abbr=this.abbr
4374         }
4375         if (this.align) {
4376             cfg.align=this.align
4377         }
4378         if (this.axis) {
4379             cfg.axis=this.axis
4380         }
4381         if (this.bgcolor) {
4382             cfg.bgcolor=this.bgcolor
4383         }
4384         if (this.charoff) {
4385             cfg.charoff=this.charoff
4386         }
4387         if (this.colspan) {
4388             cfg.colspan=this.colspan
4389         }
4390         if (this.headers) {
4391             cfg.headers=this.headers
4392         }
4393         if (this.height) {
4394             cfg.height=this.height
4395         }
4396         if (this.nowrap) {
4397             cfg.nowrap=this.nowrap
4398         }
4399         if (this.rowspan) {
4400             cfg.rowspan=this.rowspan
4401         }
4402         if (this.scope) {
4403             cfg.scope=this.scope
4404         }
4405         if (this.valign) {
4406             cfg.valign=this.valign
4407         }
4408         if (this.width) {
4409             cfg.width=this.width
4410         }
4411         
4412         
4413         return cfg;
4414     }
4415    
4416 });
4417
4418  
4419
4420  /*
4421  * - LGPL
4422  *
4423  * table row
4424  * 
4425  */
4426
4427 /**
4428  * @class Roo.bootstrap.TableRow
4429  * @extends Roo.bootstrap.Component
4430  * Bootstrap TableRow class
4431  * @cfg {String} cls row class
4432  * @cfg {String} align Aligns the content in a table row
4433  * @cfg {String} bgcolor Specifies a background color for a table row
4434  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4435  * @cfg {String} valign Vertical aligns the content in a table row
4436  * 
4437  * @constructor
4438  * Create a new TableRow
4439  * @param {Object} config The config object
4440  */
4441
4442 Roo.bootstrap.TableRow = function(config){
4443     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4444 };
4445
4446 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
4447     
4448     cls: false,
4449     align: false,
4450     bgcolor: false,
4451     charoff: false,
4452     valign: false,
4453     
4454     getAutoCreate : function(){
4455         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4456         
4457         cfg = {
4458             tag: 'tr'
4459         }
4460             
4461         if(this.cls){
4462             cfg.cls = this.cls;
4463         }
4464         if(this.align){
4465             cfg.align = this.align;
4466         }
4467         if(this.bgcolor){
4468             cfg.bgcolor = this.bgcolor;
4469         }
4470         if(this.charoff){
4471             cfg.charoff = this.charoff;
4472         }
4473         if(this.valign){
4474             cfg.valign = this.valign;
4475         }
4476         
4477         return cfg;
4478     }
4479    
4480 });
4481
4482  
4483
4484  /*
4485  * - LGPL
4486  *
4487  * table body
4488  * 
4489  */
4490
4491 /**
4492  * @class Roo.bootstrap.TableBody
4493  * @extends Roo.bootstrap.Component
4494  * Bootstrap TableBody class
4495  * @cfg {String} cls element class
4496  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4497  * @cfg {String} align Aligns the content inside the element
4498  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4499  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4500  * 
4501  * @constructor
4502  * Create a new TableBody
4503  * @param {Object} config The config object
4504  */
4505
4506 Roo.bootstrap.TableBody = function(config){
4507     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4508 };
4509
4510 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
4511     
4512     cls: false,
4513     tag: false,
4514     align: false,
4515     charoff: false,
4516     valign: false,
4517     
4518     getAutoCreate : function(){
4519         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4520         
4521         cfg = {
4522             tag: 'tbody'
4523         }
4524             
4525         if (this.cls) {
4526             cfg.cls=this.cls
4527         }
4528         if(this.tag){
4529             cfg.tag = this.tag;
4530         }
4531         
4532         if(this.align){
4533             cfg.align = this.align;
4534         }
4535         if(this.charoff){
4536             cfg.charoff = this.charoff;
4537         }
4538         if(this.valign){
4539             cfg.valign = this.valign;
4540         }
4541         
4542         return cfg;
4543     }
4544     
4545     
4546 //    initEvents : function()
4547 //    {
4548 //        
4549 //        if(!this.store){
4550 //            return;
4551 //        }
4552 //        
4553 //        this.store = Roo.factory(this.store, Roo.data);
4554 //        this.store.on('load', this.onLoad, this);
4555 //        
4556 //        this.store.load();
4557 //        
4558 //    },
4559 //    
4560 //    onLoad: function () 
4561 //    {   
4562 //        this.fireEvent('load', this);
4563 //    }
4564 //    
4565 //   
4566 });
4567
4568  
4569
4570  /*
4571  * Based on:
4572  * Ext JS Library 1.1.1
4573  * Copyright(c) 2006-2007, Ext JS, LLC.
4574  *
4575  * Originally Released Under LGPL - original licence link has changed is not relivant.
4576  *
4577  * Fork - LGPL
4578  * <script type="text/javascript">
4579  */
4580
4581 // as we use this in bootstrap.
4582 Roo.namespace('Roo.form');
4583  /**
4584  * @class Roo.form.Action
4585  * Internal Class used to handle form actions
4586  * @constructor
4587  * @param {Roo.form.BasicForm} el The form element or its id
4588  * @param {Object} config Configuration options
4589  */
4590
4591  
4592  
4593 // define the action interface
4594 Roo.form.Action = function(form, options){
4595     this.form = form;
4596     this.options = options || {};
4597 };
4598 /**
4599  * Client Validation Failed
4600  * @const 
4601  */
4602 Roo.form.Action.CLIENT_INVALID = 'client';
4603 /**
4604  * Server Validation Failed
4605  * @const 
4606  */
4607 Roo.form.Action.SERVER_INVALID = 'server';
4608  /**
4609  * Connect to Server Failed
4610  * @const 
4611  */
4612 Roo.form.Action.CONNECT_FAILURE = 'connect';
4613 /**
4614  * Reading Data from Server Failed
4615  * @const 
4616  */
4617 Roo.form.Action.LOAD_FAILURE = 'load';
4618
4619 Roo.form.Action.prototype = {
4620     type : 'default',
4621     failureType : undefined,
4622     response : undefined,
4623     result : undefined,
4624
4625     // interface method
4626     run : function(options){
4627
4628     },
4629
4630     // interface method
4631     success : function(response){
4632
4633     },
4634
4635     // interface method
4636     handleResponse : function(response){
4637
4638     },
4639
4640     // default connection failure
4641     failure : function(response){
4642         
4643         this.response = response;
4644         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4645         this.form.afterAction(this, false);
4646     },
4647
4648     processResponse : function(response){
4649         this.response = response;
4650         if(!response.responseText){
4651             return true;
4652         }
4653         this.result = this.handleResponse(response);
4654         return this.result;
4655     },
4656
4657     // utility functions used internally
4658     getUrl : function(appendParams){
4659         var url = this.options.url || this.form.url || this.form.el.dom.action;
4660         if(appendParams){
4661             var p = this.getParams();
4662             if(p){
4663                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4664             }
4665         }
4666         return url;
4667     },
4668
4669     getMethod : function(){
4670         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4671     },
4672
4673     getParams : function(){
4674         var bp = this.form.baseParams;
4675         var p = this.options.params;
4676         if(p){
4677             if(typeof p == "object"){
4678                 p = Roo.urlEncode(Roo.applyIf(p, bp));
4679             }else if(typeof p == 'string' && bp){
4680                 p += '&' + Roo.urlEncode(bp);
4681             }
4682         }else if(bp){
4683             p = Roo.urlEncode(bp);
4684         }
4685         return p;
4686     },
4687
4688     createCallback : function(){
4689         return {
4690             success: this.success,
4691             failure: this.failure,
4692             scope: this,
4693             timeout: (this.form.timeout*1000),
4694             upload: this.form.fileUpload ? this.success : undefined
4695         };
4696     }
4697 };
4698
4699 Roo.form.Action.Submit = function(form, options){
4700     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4701 };
4702
4703 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4704     type : 'submit',
4705
4706     haveProgress : false,
4707     uploadComplete : false,
4708     
4709     // uploadProgress indicator.
4710     uploadProgress : function()
4711     {
4712         if (!this.form.progressUrl) {
4713             return;
4714         }
4715         
4716         if (!this.haveProgress) {
4717             Roo.MessageBox.progress("Uploading", "Uploading");
4718         }
4719         if (this.uploadComplete) {
4720            Roo.MessageBox.hide();
4721            return;
4722         }
4723         
4724         this.haveProgress = true;
4725    
4726         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4727         
4728         var c = new Roo.data.Connection();
4729         c.request({
4730             url : this.form.progressUrl,
4731             params: {
4732                 id : uid
4733             },
4734             method: 'GET',
4735             success : function(req){
4736                //console.log(data);
4737                 var rdata = false;
4738                 var edata;
4739                 try  {
4740                    rdata = Roo.decode(req.responseText)
4741                 } catch (e) {
4742                     Roo.log("Invalid data from server..");
4743                     Roo.log(edata);
4744                     return;
4745                 }
4746                 if (!rdata || !rdata.success) {
4747                     Roo.log(rdata);
4748                     Roo.MessageBox.alert(Roo.encode(rdata));
4749                     return;
4750                 }
4751                 var data = rdata.data;
4752                 
4753                 if (this.uploadComplete) {
4754                    Roo.MessageBox.hide();
4755                    return;
4756                 }
4757                    
4758                 if (data){
4759                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4760                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4761                     );
4762                 }
4763                 this.uploadProgress.defer(2000,this);
4764             },
4765        
4766             failure: function(data) {
4767                 Roo.log('progress url failed ');
4768                 Roo.log(data);
4769             },
4770             scope : this
4771         });
4772            
4773     },
4774     
4775     
4776     run : function()
4777     {
4778         // run get Values on the form, so it syncs any secondary forms.
4779         this.form.getValues();
4780         
4781         var o = this.options;
4782         var method = this.getMethod();
4783         var isPost = method == 'POST';
4784         if(o.clientValidation === false || this.form.isValid()){
4785             
4786             if (this.form.progressUrl) {
4787                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4788                     (new Date() * 1) + '' + Math.random());
4789                     
4790             } 
4791             
4792             
4793             Roo.Ajax.request(Roo.apply(this.createCallback(), {
4794                 form:this.form.el.dom,
4795                 url:this.getUrl(!isPost),
4796                 method: method,
4797                 params:isPost ? this.getParams() : null,
4798                 isUpload: this.form.fileUpload
4799             }));
4800             
4801             this.uploadProgress();
4802
4803         }else if (o.clientValidation !== false){ // client validation failed
4804             this.failureType = Roo.form.Action.CLIENT_INVALID;
4805             this.form.afterAction(this, false);
4806         }
4807     },
4808
4809     success : function(response)
4810     {
4811         this.uploadComplete= true;
4812         if (this.haveProgress) {
4813             Roo.MessageBox.hide();
4814         }
4815         
4816         
4817         var result = this.processResponse(response);
4818         if(result === true || result.success){
4819             this.form.afterAction(this, true);
4820             return;
4821         }
4822         if(result.errors){
4823             this.form.markInvalid(result.errors);
4824             this.failureType = Roo.form.Action.SERVER_INVALID;
4825         }
4826         this.form.afterAction(this, false);
4827     },
4828     failure : function(response)
4829     {
4830         this.uploadComplete= true;
4831         if (this.haveProgress) {
4832             Roo.MessageBox.hide();
4833         }
4834         
4835         this.response = response;
4836         this.failureType = Roo.form.Action.CONNECT_FAILURE;
4837         this.form.afterAction(this, false);
4838     },
4839     
4840     handleResponse : function(response){
4841         if(this.form.errorReader){
4842             var rs = this.form.errorReader.read(response);
4843             var errors = [];
4844             if(rs.records){
4845                 for(var i = 0, len = rs.records.length; i < len; i++) {
4846                     var r = rs.records[i];
4847                     errors[i] = r.data;
4848                 }
4849             }
4850             if(errors.length < 1){
4851                 errors = null;
4852             }
4853             return {
4854                 success : rs.success,
4855                 errors : errors
4856             };
4857         }
4858         var ret = false;
4859         try {
4860             ret = Roo.decode(response.responseText);
4861         } catch (e) {
4862             ret = {
4863                 success: false,
4864                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4865                 errors : []
4866             };
4867         }
4868         return ret;
4869         
4870     }
4871 });
4872
4873
4874 Roo.form.Action.Load = function(form, options){
4875     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4876     this.reader = this.form.reader;
4877 };
4878
4879 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4880     type : 'load',
4881
4882     run : function(){
4883         
4884         Roo.Ajax.request(Roo.apply(
4885                 this.createCallback(), {
4886                     method:this.getMethod(),
4887                     url:this.getUrl(false),
4888                     params:this.getParams()
4889         }));
4890     },
4891
4892     success : function(response){
4893         
4894         var result = this.processResponse(response);
4895         if(result === true || !result.success || !result.data){
4896             this.failureType = Roo.form.Action.LOAD_FAILURE;
4897             this.form.afterAction(this, false);
4898             return;
4899         }
4900         this.form.clearInvalid();
4901         this.form.setValues(result.data);
4902         this.form.afterAction(this, true);
4903     },
4904
4905     handleResponse : function(response){
4906         if(this.form.reader){
4907             var rs = this.form.reader.read(response);
4908             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4909             return {
4910                 success : rs.success,
4911                 data : data
4912             };
4913         }
4914         return Roo.decode(response.responseText);
4915     }
4916 });
4917
4918 Roo.form.Action.ACTION_TYPES = {
4919     'load' : Roo.form.Action.Load,
4920     'submit' : Roo.form.Action.Submit
4921 };/*
4922  * - LGPL
4923  *
4924  * form
4925  * 
4926  */
4927
4928 /**
4929  * @class Roo.bootstrap.Form
4930  * @extends Roo.bootstrap.Component
4931  * Bootstrap Form class
4932  * @cfg {String} method  GET | POST (default POST)
4933  * @cfg {String} labelAlign top | left (default top)
4934   * @cfg {String} align left  | right - for navbars
4935
4936  * 
4937  * @constructor
4938  * Create a new Form
4939  * @param {Object} config The config object
4940  */
4941
4942
4943 Roo.bootstrap.Form = function(config){
4944     Roo.bootstrap.Form.superclass.constructor.call(this, config);
4945     this.addEvents({
4946         /**
4947          * @event clientvalidation
4948          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4949          * @param {Form} this
4950          * @param {Boolean} valid true if the form has passed client-side validation
4951          */
4952         clientvalidation: true,
4953         /**
4954          * @event beforeaction
4955          * Fires before any action is performed. Return false to cancel the action.
4956          * @param {Form} this
4957          * @param {Action} action The action to be performed
4958          */
4959         beforeaction: true,
4960         /**
4961          * @event actionfailed
4962          * Fires when an action fails.
4963          * @param {Form} this
4964          * @param {Action} action The action that failed
4965          */
4966         actionfailed : true,
4967         /**
4968          * @event actioncomplete
4969          * Fires when an action is completed.
4970          * @param {Form} this
4971          * @param {Action} action The action that completed
4972          */
4973         actioncomplete : true
4974     });
4975     
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
4979       
4980      /**
4981      * @cfg {String} method
4982      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4983      */
4984     method : 'POST',
4985     /**
4986      * @cfg {String} url
4987      * The URL to use for form actions if one isn't supplied in the action options.
4988      */
4989     /**
4990      * @cfg {Boolean} fileUpload
4991      * Set to true if this form is a file upload.
4992      */
4993      
4994     /**
4995      * @cfg {Object} baseParams
4996      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4997      */
4998       
4999     /**
5000      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5001      */
5002     timeout: 30,
5003     /**
5004      * @cfg {Sting} align (left|right) for navbar forms
5005      */
5006     align : 'left',
5007
5008     // private
5009     activeAction : null,
5010  
5011     /**
5012      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5013      * element by passing it or its id or mask the form itself by passing in true.
5014      * @type Mixed
5015      */
5016     waitMsgTarget : false,
5017     
5018      
5019     
5020     /**
5021      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5022      * element by passing it or its id or mask the form itself by passing in true.
5023      * @type Mixed
5024      */
5025     
5026     getAutoCreate : function(){
5027         
5028         var cfg = {
5029             tag: 'form',
5030             method : this.method || 'POST',
5031             id : this.id || Roo.id(),
5032             cls : ''
5033         }
5034         if (this.parent().xtype.match(/^Nav/)) {
5035             cfg.cls = 'navbar-form navbar-' + this.align;
5036             
5037         }
5038         
5039         if (this.labelAlign == 'left' ) {
5040             cfg.cls += ' form-horizontal';
5041         }
5042         
5043         
5044         return cfg;
5045     },
5046     initEvents : function()
5047     {
5048         this.el.on('submit', this.onSubmit, this);
5049         
5050         
5051     },
5052     // private
5053     onSubmit : function(e){
5054         e.stopEvent();
5055     },
5056     
5057      /**
5058      * Returns true if client-side validation on the form is successful.
5059      * @return Boolean
5060      */
5061     isValid : function(){
5062         var items = this.getItems();
5063         var valid = true;
5064         items.each(function(f){
5065            if(!f.validate()){
5066                valid = false;
5067                
5068            }
5069         });
5070         return valid;
5071     },
5072     /**
5073      * Returns true if any fields in this form have changed since their original load.
5074      * @return Boolean
5075      */
5076     isDirty : function(){
5077         var dirty = false;
5078         var items = this.getItems();
5079         items.each(function(f){
5080            if(f.isDirty()){
5081                dirty = true;
5082                return false;
5083            }
5084            return true;
5085         });
5086         return dirty;
5087     },
5088      /**
5089      * Performs a predefined action (submit or load) or custom actions you define on this form.
5090      * @param {String} actionName The name of the action type
5091      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5092      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5093      * accept other config options):
5094      * <pre>
5095 Property          Type             Description
5096 ----------------  ---------------  ----------------------------------------------------------------------------------
5097 url               String           The url for the action (defaults to the form's url)
5098 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5099 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5100 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5101                                    validate the form on the client (defaults to false)
5102      * </pre>
5103      * @return {BasicForm} this
5104      */
5105     doAction : function(action, options){
5106         if(typeof action == 'string'){
5107             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5108         }
5109         if(this.fireEvent('beforeaction', this, action) !== false){
5110             this.beforeAction(action);
5111             action.run.defer(100, action);
5112         }
5113         return this;
5114     },
5115     
5116     // private
5117     beforeAction : function(action){
5118         var o = action.options;
5119         
5120         // not really supported yet.. ??
5121         
5122         //if(this.waitMsgTarget === true){
5123             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5124         //}else if(this.waitMsgTarget){
5125         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5126         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5127         //}else {
5128         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5129        // }
5130          
5131     },
5132
5133     // private
5134     afterAction : function(action, success){
5135         this.activeAction = null;
5136         var o = action.options;
5137         
5138         //if(this.waitMsgTarget === true){
5139             this.el.unmask();
5140         //}else if(this.waitMsgTarget){
5141         //    this.waitMsgTarget.unmask();
5142         //}else{
5143         //    Roo.MessageBox.updateProgress(1);
5144         //    Roo.MessageBox.hide();
5145        // }
5146         // 
5147         if(success){
5148             if(o.reset){
5149                 this.reset();
5150             }
5151             Roo.callback(o.success, o.scope, [this, action]);
5152             this.fireEvent('actioncomplete', this, action);
5153             
5154         }else{
5155             
5156             // failure condition..
5157             // we have a scenario where updates need confirming.
5158             // eg. if a locking scenario exists..
5159             // we look for { errors : { needs_confirm : true }} in the response.
5160             if (
5161                 (typeof(action.result) != 'undefined')  &&
5162                 (typeof(action.result.errors) != 'undefined')  &&
5163                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5164            ){
5165                 var _t = this;
5166                 Roo.log("not supported yet");
5167                  /*
5168                 
5169                 Roo.MessageBox.confirm(
5170                     "Change requires confirmation",
5171                     action.result.errorMsg,
5172                     function(r) {
5173                         if (r != 'yes') {
5174                             return;
5175                         }
5176                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5177                     }
5178                     
5179                 );
5180                 */
5181                 
5182                 
5183                 return;
5184             }
5185             
5186             Roo.callback(o.failure, o.scope, [this, action]);
5187             // show an error message if no failed handler is set..
5188             if (!this.hasListener('actionfailed')) {
5189                 Roo.log("need to add dialog support");
5190                 /*
5191                 Roo.MessageBox.alert("Error",
5192                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5193                         action.result.errorMsg :
5194                         "Saving Failed, please check your entries or try again"
5195                 );
5196                 */
5197             }
5198             
5199             this.fireEvent('actionfailed', this, action);
5200         }
5201         
5202     },
5203     /**
5204      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5205      * @param {String} id The value to search for
5206      * @return Field
5207      */
5208     findField : function(id){
5209         var items = this.getItems();
5210         var field = items.get(id);
5211         if(!field){
5212              items.each(function(f){
5213                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5214                     field = f;
5215                     return false;
5216                 }
5217                 return true;
5218             });
5219         }
5220         return field || null;
5221     },
5222      /**
5223      * Mark fields in this form invalid in bulk.
5224      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5225      * @return {BasicForm} this
5226      */
5227     markInvalid : function(errors){
5228         if(errors instanceof Array){
5229             for(var i = 0, len = errors.length; i < len; i++){
5230                 var fieldError = errors[i];
5231                 var f = this.findField(fieldError.id);
5232                 if(f){
5233                     f.markInvalid(fieldError.msg);
5234                 }
5235             }
5236         }else{
5237             var field, id;
5238             for(id in errors){
5239                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5240                     field.markInvalid(errors[id]);
5241                 }
5242             }
5243         }
5244         //Roo.each(this.childForms || [], function (f) {
5245         //    f.markInvalid(errors);
5246         //});
5247         
5248         return this;
5249     },
5250
5251     /**
5252      * Set values for fields in this form in bulk.
5253      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5254      * @return {BasicForm} this
5255      */
5256     setValues : function(values){
5257         if(values instanceof Array){ // array of objects
5258             for(var i = 0, len = values.length; i < len; i++){
5259                 var v = values[i];
5260                 var f = this.findField(v.id);
5261                 if(f){
5262                     f.setValue(v.value);
5263                     if(this.trackResetOnLoad){
5264                         f.originalValue = f.getValue();
5265                     }
5266                 }
5267             }
5268         }else{ // object hash
5269             var field, id;
5270             for(id in values){
5271                 if(typeof values[id] != 'function' && (field = this.findField(id))){
5272                     
5273                     if (field.setFromData && 
5274                         field.valueField && 
5275                         field.displayField &&
5276                         // combos' with local stores can 
5277                         // be queried via setValue()
5278                         // to set their value..
5279                         (field.store && !field.store.isLocal)
5280                         ) {
5281                         // it's a combo
5282                         var sd = { };
5283                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5284                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5285                         field.setFromData(sd);
5286                         
5287                     } else {
5288                         field.setValue(values[id]);
5289                     }
5290                     
5291                     
5292                     if(this.trackResetOnLoad){
5293                         field.originalValue = field.getValue();
5294                     }
5295                 }
5296             }
5297         }
5298          
5299         //Roo.each(this.childForms || [], function (f) {
5300         //    f.setValues(values);
5301         //});
5302                 
5303         return this;
5304     },
5305
5306     /**
5307      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5308      * they are returned as an array.
5309      * @param {Boolean} asString
5310      * @return {Object}
5311      */
5312     getValues : function(asString){
5313         //if (this.childForms) {
5314             // copy values from the child forms
5315         //    Roo.each(this.childForms, function (f) {
5316         //        this.setValues(f.getValues());
5317         //    }, this);
5318         //}
5319         
5320         
5321         
5322         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5323         if(asString === true){
5324             return fs;
5325         }
5326         return Roo.urlDecode(fs);
5327     },
5328     
5329     /**
5330      * Returns the fields in this form as an object with key/value pairs. 
5331      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5332      * @return {Object}
5333      */
5334     getFieldValues : function(with_hidden)
5335     {
5336         var items = this.getItems();
5337         var ret = {};
5338         items.each(function(f){
5339             if (!f.getName()) {
5340                 return;
5341             }
5342             var v = f.getValue();
5343             if (f.inputType =='radio') {
5344                 if (typeof(ret[f.getName()]) == 'undefined') {
5345                     ret[f.getName()] = ''; // empty..
5346                 }
5347                 
5348                 if (!f.el.dom.checked) {
5349                     return;
5350                     
5351                 }
5352                 v = f.el.dom.value;
5353                 
5354             }
5355             
5356             // not sure if this supported any more..
5357             if ((typeof(v) == 'object') && f.getRawValue) {
5358                 v = f.getRawValue() ; // dates..
5359             }
5360             // combo boxes where name != hiddenName...
5361             if (f.name != f.getName()) {
5362                 ret[f.name] = f.getRawValue();
5363             }
5364             ret[f.getName()] = v;
5365         });
5366         
5367         return ret;
5368     },
5369
5370     /**
5371      * Clears all invalid messages in this form.
5372      * @return {BasicForm} this
5373      */
5374     clearInvalid : function(){
5375         var items = this.getItems();
5376         
5377         items.each(function(f){
5378            f.clearInvalid();
5379         });
5380         
5381         
5382         
5383         return this;
5384     },
5385
5386     /**
5387      * Resets this form.
5388      * @return {BasicForm} this
5389      */
5390     reset : function(){
5391         var items = this.getItems();
5392         items.each(function(f){
5393             f.reset();
5394         });
5395         
5396         Roo.each(this.childForms || [], function (f) {
5397             f.reset();
5398         });
5399        
5400         
5401         return this;
5402     },
5403     getItems : function()
5404     {
5405         var r=new Roo.util.MixedCollection(false, function(o){
5406             return o.id || (o.id = Roo.id());
5407         });
5408         var iter = function(el) {
5409             if (el.inputEl) {
5410                 r.add(el);
5411             }
5412             if (!el.items) {
5413                 return;
5414             }
5415             Roo.each(el.items,function(e) {
5416                 iter(e);
5417             });
5418             
5419             
5420         };
5421         iter(this);
5422         return r;
5423         
5424         
5425         
5426         
5427     }
5428     
5429 });
5430
5431  
5432 /*
5433  * Based on:
5434  * Ext JS Library 1.1.1
5435  * Copyright(c) 2006-2007, Ext JS, LLC.
5436  *
5437  * Originally Released Under LGPL - original licence link has changed is not relivant.
5438  *
5439  * Fork - LGPL
5440  * <script type="text/javascript">
5441  */
5442 /**
5443  * @class Roo.form.VTypes
5444  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5445  * @singleton
5446  */
5447 Roo.form.VTypes = function(){
5448     // closure these in so they are only created once.
5449     var alpha = /^[a-zA-Z_]+$/;
5450     var alphanum = /^[a-zA-Z0-9_]+$/;
5451     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5452     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5453
5454     // All these messages and functions are configurable
5455     return {
5456         /**
5457          * The function used to validate email addresses
5458          * @param {String} value The email address
5459          */
5460         'email' : function(v){
5461             return email.test(v);
5462         },
5463         /**
5464          * The error text to display when the email validation function returns false
5465          * @type String
5466          */
5467         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5468         /**
5469          * The keystroke filter mask to be applied on email input
5470          * @type RegExp
5471          */
5472         'emailMask' : /[a-z0-9_\.\-@]/i,
5473
5474         /**
5475          * The function used to validate URLs
5476          * @param {String} value The URL
5477          */
5478         'url' : function(v){
5479             return url.test(v);
5480         },
5481         /**
5482          * The error text to display when the url validation function returns false
5483          * @type String
5484          */
5485         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5486         
5487         /**
5488          * The function used to validate alpha values
5489          * @param {String} value The value
5490          */
5491         'alpha' : function(v){
5492             return alpha.test(v);
5493         },
5494         /**
5495          * The error text to display when the alpha validation function returns false
5496          * @type String
5497          */
5498         'alphaText' : 'This field should only contain letters and _',
5499         /**
5500          * The keystroke filter mask to be applied on alpha input
5501          * @type RegExp
5502          */
5503         'alphaMask' : /[a-z_]/i,
5504
5505         /**
5506          * The function used to validate alphanumeric values
5507          * @param {String} value The value
5508          */
5509         'alphanum' : function(v){
5510             return alphanum.test(v);
5511         },
5512         /**
5513          * The error text to display when the alphanumeric validation function returns false
5514          * @type String
5515          */
5516         'alphanumText' : 'This field should only contain letters, numbers and _',
5517         /**
5518          * The keystroke filter mask to be applied on alphanumeric input
5519          * @type RegExp
5520          */
5521         'alphanumMask' : /[a-z0-9_]/i
5522     };
5523 }();/*
5524  * - LGPL
5525  *
5526  * Input
5527  * 
5528  */
5529
5530 /**
5531  * @class Roo.bootstrap.Input
5532  * @extends Roo.bootstrap.Component
5533  * Bootstrap Input class
5534  * @cfg {Boolean} disabled is it disabled
5535  * @cfg {String} fieldLabel - the label associated
5536  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5537  * @cfg {String} name name of the input
5538  * @cfg {string} fieldLabel - the label associated
5539  * @cfg {string}  inputType - input / file submit ...
5540  * @cfg {string} placeholder - placeholder to put in text.
5541  * @cfg {string}  before - input group add on before
5542  * @cfg {string} after - input group add on after
5543  * @cfg {string} size - (lg|sm) or leave empty..
5544  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5545  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5546  * @cfg {Number} md colspan out of 12 for computer-sized screens
5547  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5548  * @cfg {string} value default value of the input
5549  * @cfg {Number} labelWidth set the width of label (0-12)
5550  * @cfg {String} labelAlign (top|left)
5551  * @cfg {Boolean} readOnly Specifies that the field should be read-only
5552  * 
5553  * 
5554  * @constructor
5555  * Create a new Input
5556  * @param {Object} config The config object
5557  */
5558
5559 Roo.bootstrap.Input = function(config){
5560     Roo.bootstrap.Input.superclass.constructor.call(this, config);
5561    
5562         this.addEvents({
5563             /**
5564              * @event focus
5565              * Fires when this field receives input focus.
5566              * @param {Roo.form.Field} this
5567              */
5568             focus : true,
5569             /**
5570              * @event blur
5571              * Fires when this field loses input focus.
5572              * @param {Roo.form.Field} this
5573              */
5574             blur : true,
5575             /**
5576              * @event specialkey
5577              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
5578              * {@link Roo.EventObject#getKey} to determine which key was pressed.
5579              * @param {Roo.form.Field} this
5580              * @param {Roo.EventObject} e The event object
5581              */
5582             specialkey : true,
5583             /**
5584              * @event change
5585              * Fires just before the field blurs if the field value has changed.
5586              * @param {Roo.form.Field} this
5587              * @param {Mixed} newValue The new value
5588              * @param {Mixed} oldValue The original value
5589              */
5590             change : true,
5591             /**
5592              * @event invalid
5593              * Fires after the field has been marked as invalid.
5594              * @param {Roo.form.Field} this
5595              * @param {String} msg The validation message
5596              */
5597             invalid : true,
5598             /**
5599              * @event valid
5600              * Fires after the field has been validated with no errors.
5601              * @param {Roo.form.Field} this
5602              */
5603             valid : true,
5604              /**
5605              * @event keyup
5606              * Fires after the key up
5607              * @param {Roo.form.Field} this
5608              * @param {Roo.EventObject}  e The event Object
5609              */
5610             keyup : true
5611         });
5612 };
5613
5614 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
5615      /**
5616      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5617       automatic validation (defaults to "keyup").
5618      */
5619     validationEvent : "keyup",
5620      /**
5621      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5622      */
5623     validateOnBlur : true,
5624     /**
5625      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5626      */
5627     validationDelay : 250,
5628      /**
5629      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5630      */
5631     focusClass : "x-form-focus",  // not needed???
5632     
5633        
5634     /**
5635      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5636      */
5637     invalidClass : "has-error",
5638     
5639     /**
5640      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5641      */
5642     selectOnFocus : false,
5643     
5644      /**
5645      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5646      */
5647     maskRe : null,
5648        /**
5649      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5650      */
5651     vtype : null,
5652     
5653       /**
5654      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5655      */
5656     disableKeyFilter : false,
5657     
5658        /**
5659      * @cfg {Boolean} disabled True to disable the field (defaults to false).
5660      */
5661     disabled : false,
5662      /**
5663      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5664      */
5665     allowBlank : true,
5666     /**
5667      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5668      */
5669     blankText : "This field is required",
5670     
5671      /**
5672      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5673      */
5674     minLength : 0,
5675     /**
5676      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5677      */
5678     maxLength : Number.MAX_VALUE,
5679     /**
5680      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5681      */
5682     minLengthText : "The minimum length for this field is {0}",
5683     /**
5684      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5685      */
5686     maxLengthText : "The maximum length for this field is {0}",
5687   
5688     
5689     /**
5690      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5691      * If available, this function will be called only after the basic validators all return true, and will be passed the
5692      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5693      */
5694     validator : null,
5695     /**
5696      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5697      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5698      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
5699      */
5700     regex : null,
5701     /**
5702      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5703      */
5704     regexText : "",
5705     
5706     
5707     
5708     fieldLabel : '',
5709     inputType : 'text',
5710     
5711     name : false,
5712     placeholder: false,
5713     before : false,
5714     after : false,
5715     size : false,
5716     // private
5717     hasFocus : false,
5718     preventMark: false,
5719     isFormField : true,
5720     value : '',
5721     labelWidth : 2,
5722     labelAlign : false,
5723     readOnly : false,
5724     
5725     parentLabelAlign : function()
5726     {
5727         var parent = this;
5728         while (parent.parent()) {
5729             parent = parent.parent();
5730             if (typeof(parent.labelAlign) !='undefined') {
5731                 return parent.labelAlign;
5732             }
5733         }
5734         return 'left';
5735         
5736     },
5737     
5738     getAutoCreate : function(){
5739         
5740         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5741         
5742         var id = Roo.id();
5743         
5744         var cfg = {};
5745         
5746         if(this.inputType != 'hidden'){
5747             cfg.cls = 'form-group' //input-group
5748         }
5749         
5750         var input =  {
5751             tag: 'input',
5752             id : id,
5753             type : this.inputType,
5754             value : this.value,
5755             cls : 'form-control',
5756             placeholder : this.placeholder || ''
5757             
5758         };
5759         
5760         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5761             input.maxLength = this.maxLength;
5762         }
5763         
5764         if (this.disabled) {
5765             input.disabled=true;
5766         }
5767         
5768         if (this.readOnly) {
5769             input.readonly=true;
5770         }
5771         
5772         if (this.name) {
5773             input.name = this.name;
5774         }
5775         if (this.size) {
5776             input.cls += ' input-' + this.size;
5777         }
5778         var settings=this;
5779         ['xs','sm','md','lg'].map(function(size){
5780             if (settings[size]) {
5781                 cfg.cls += ' col-' + size + '-' + settings[size];
5782             }
5783         });
5784         
5785         var inputblock = input;
5786         
5787         if (this.before || this.after) {
5788             
5789             inputblock = {
5790                 cls : 'input-group',
5791                 cn :  [] 
5792             };
5793             if (this.before && typeof(this.before) == 'string') {
5794                 
5795                 inputblock.cn.push({
5796                     tag :'span',
5797                     cls : 'roo-input-before input-group-addon',
5798                     html : this.before
5799                 });
5800             }
5801             if (this.before && typeof(this.before) == 'object') {
5802                 this.before = Roo.factory(this.before);
5803                 Roo.log(this.before);
5804                 inputblock.cn.push({
5805                     tag :'span',
5806                     cls : 'roo-input-before input-group-' +
5807                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5808                 });
5809             }
5810             
5811             inputblock.cn.push(input);
5812             
5813             if (this.after && typeof(this.after) == 'string') {
5814                 inputblock.cn.push({
5815                     tag :'span',
5816                     cls : 'roo-input-after input-group-addon',
5817                     html : this.after
5818                 });
5819             }
5820             if (this.after && typeof(this.after) == 'object') {
5821                 this.after = Roo.factory(this.after);
5822                 Roo.log(this.after);
5823                 inputblock.cn.push({
5824                     tag :'span',
5825                     cls : 'roo-input-after input-group-' +
5826                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
5827                 });
5828             }
5829         };
5830         
5831         if (align ==='left' && this.fieldLabel.length) {
5832                 Roo.log("left and has label");
5833                 cfg.cn = [
5834                     
5835                     {
5836                         tag: 'label',
5837                         'for' :  id,
5838                         cls : 'control-label col-sm-' + this.labelWidth,
5839                         html : this.fieldLabel
5840                         
5841                     },
5842                     {
5843                         cls : "col-sm-" + (12 - this.labelWidth), 
5844                         cn: [
5845                             inputblock
5846                         ]
5847                     }
5848                     
5849                 ];
5850         } else if ( this.fieldLabel.length) {
5851                 Roo.log(" label");
5852                  cfg.cn = [
5853                    
5854                     {
5855                         tag: 'label',
5856                         //cls : 'input-group-addon',
5857                         html : this.fieldLabel
5858                         
5859                     },
5860                     
5861                     inputblock
5862                     
5863                 ];
5864
5865         } else {
5866             
5867                 Roo.log(" no label && no align");
5868                 cfg.cn = [
5869                     
5870                         inputblock
5871                     
5872                 ];
5873                 
5874                 
5875         };
5876         Roo.log('input-parentType: ' + this.parentType);
5877         
5878         if (this.parentType === 'Navbar' &&  this.parent().bar) {
5879            cfg.cls += ' navbar-form';
5880            Roo.log(cfg);
5881         }
5882         
5883         return cfg;
5884         
5885     },
5886     /**
5887      * return the real input element.
5888      */
5889     inputEl: function ()
5890     {
5891         return this.el.select('input.form-control',true).first();
5892     },
5893     setDisabled : function(v)
5894     {
5895         var i  = this.inputEl().dom;
5896         if (!v) {
5897             i.removeAttribute('disabled');
5898             return;
5899             
5900         }
5901         i.setAttribute('disabled','true');
5902     },
5903     initEvents : function()
5904     {
5905         
5906         this.inputEl().on("keydown" , this.fireKey,  this);
5907         this.inputEl().on("focus", this.onFocus,  this);
5908         this.inputEl().on("blur", this.onBlur,  this);
5909         
5910         this.inputEl().relayEvent('keyup', this);
5911
5912         // reference to original value for reset
5913         this.originalValue = this.getValue();
5914         //Roo.form.TextField.superclass.initEvents.call(this);
5915         if(this.validationEvent == 'keyup'){
5916             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5917             this.inputEl().on('keyup', this.filterValidation, this);
5918         }
5919         else if(this.validationEvent !== false){
5920             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5921         }
5922         
5923         if(this.selectOnFocus){
5924             this.on("focus", this.preFocus, this);
5925             
5926         }
5927         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5928             this.inputEl().on("keypress", this.filterKeys, this);
5929         }
5930        /* if(this.grow){
5931             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
5932             this.el.on("click", this.autoSize,  this);
5933         }
5934         */
5935         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5936             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5937         }
5938         
5939         if (typeof(this.before) == 'object') {
5940             this.before.render(this.el.select('.roo-input-before',true).first());
5941         }
5942         if (typeof(this.after) == 'object') {
5943             this.after.render(this.el.select('.roo-input-after',true).first());
5944         }
5945         
5946         
5947     },
5948     filterValidation : function(e){
5949         if(!e.isNavKeyPress()){
5950             this.validationTask.delay(this.validationDelay);
5951         }
5952     },
5953      /**
5954      * Validates the field value
5955      * @return {Boolean} True if the value is valid, else false
5956      */
5957     validate : function(){
5958         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5959         if(this.disabled || this.validateValue(this.getRawValue())){
5960             this.clearInvalid();
5961             return true;
5962         }
5963         return false;
5964     },
5965     
5966     
5967     /**
5968      * Validates a value according to the field's validation rules and marks the field as invalid
5969      * if the validation fails
5970      * @param {Mixed} value The value to validate
5971      * @return {Boolean} True if the value is valid, else false
5972      */
5973     validateValue : function(value){
5974         if(value.length < 1)  { // if it's blank
5975              if(this.allowBlank){
5976                 this.clearInvalid();
5977                 return true;
5978              }else{
5979                 this.markInvalid(this.blankText);
5980                 return false;
5981              }
5982         }
5983         if(value.length < this.minLength){
5984             this.markInvalid(String.format(this.minLengthText, this.minLength));
5985             return false;
5986         }
5987         if(value.length > this.maxLength){
5988             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5989             return false;
5990         }
5991         if(this.vtype){
5992             var vt = Roo.form.VTypes;
5993             if(!vt[this.vtype](value, this)){
5994                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5995                 return false;
5996             }
5997         }
5998         if(typeof this.validator == "function"){
5999             var msg = this.validator(value);
6000             if(msg !== true){
6001                 this.markInvalid(msg);
6002                 return false;
6003             }
6004         }
6005         if(this.regex && !this.regex.test(value)){
6006             this.markInvalid(this.regexText);
6007             return false;
6008         }
6009         return true;
6010     },
6011
6012     
6013     
6014      // private
6015     fireKey : function(e){
6016         //Roo.log('field ' + e.getKey());
6017         if(e.isNavKeyPress()){
6018             this.fireEvent("specialkey", this, e);
6019         }
6020     },
6021     focus : function (selectText){
6022         if(this.rendered){
6023             this.inputEl().focus();
6024             if(selectText === true){
6025                 this.inputEl().dom.select();
6026             }
6027         }
6028         return this;
6029     } ,
6030     
6031     onFocus : function(){
6032         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6033            // this.el.addClass(this.focusClass);
6034         }
6035         if(!this.hasFocus){
6036             this.hasFocus = true;
6037             this.startValue = this.getValue();
6038             this.fireEvent("focus", this);
6039         }
6040     },
6041     
6042     beforeBlur : Roo.emptyFn,
6043
6044     
6045     // private
6046     onBlur : function(){
6047         this.beforeBlur();
6048         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6049             //this.el.removeClass(this.focusClass);
6050         }
6051         this.hasFocus = false;
6052         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6053             this.validate();
6054         }
6055         var v = this.getValue();
6056         if(String(v) !== String(this.startValue)){
6057             this.fireEvent('change', this, v, this.startValue);
6058         }
6059         this.fireEvent("blur", this);
6060     },
6061     
6062     /**
6063      * Resets the current field value to the originally loaded value and clears any validation messages
6064      */
6065     reset : function(){
6066         this.setValue(this.originalValue);
6067         this.clearInvalid();
6068     },
6069      /**
6070      * Returns the name of the field
6071      * @return {Mixed} name The name field
6072      */
6073     getName: function(){
6074         return this.name;
6075     },
6076      /**
6077      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6078      * @return {Mixed} value The field value
6079      */
6080     getValue : function(){
6081         return this.inputEl().getValue();
6082     },
6083     /**
6084      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6085      * @return {Mixed} value The field value
6086      */
6087     getRawValue : function(){
6088         var v = this.inputEl().getValue();
6089         
6090         return v;
6091     },
6092     
6093     /**
6094      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6095      * @param {Mixed} value The value to set
6096      */
6097     setRawValue : function(v){
6098         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6099     },
6100     
6101     selectText : function(start, end){
6102         var v = this.getRawValue();
6103         if(v.length > 0){
6104             start = start === undefined ? 0 : start;
6105             end = end === undefined ? v.length : end;
6106             var d = this.inputEl().dom;
6107             if(d.setSelectionRange){
6108                 d.setSelectionRange(start, end);
6109             }else if(d.createTextRange){
6110                 var range = d.createTextRange();
6111                 range.moveStart("character", start);
6112                 range.moveEnd("character", v.length-end);
6113                 range.select();
6114             }
6115         }
6116     },
6117     
6118     /**
6119      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6120      * @param {Mixed} value The value to set
6121      */
6122     setValue : function(v){
6123         this.value = v;
6124         if(this.rendered){
6125             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6126             this.validate();
6127         }
6128     },
6129     
6130     /*
6131     processValue : function(value){
6132         if(this.stripCharsRe){
6133             var newValue = value.replace(this.stripCharsRe, '');
6134             if(newValue !== value){
6135                 this.setRawValue(newValue);
6136                 return newValue;
6137             }
6138         }
6139         return value;
6140     },
6141   */
6142     preFocus : function(){
6143         
6144         if(this.selectOnFocus){
6145             this.inputEl().dom.select();
6146         }
6147     },
6148     filterKeys : function(e){
6149         var k = e.getKey();
6150         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6151             return;
6152         }
6153         var c = e.getCharCode(), cc = String.fromCharCode(c);
6154         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6155             return;
6156         }
6157         if(!this.maskRe.test(cc)){
6158             e.stopEvent();
6159         }
6160     },
6161      /**
6162      * Clear any invalid styles/messages for this field
6163      */
6164     clearInvalid : function(){
6165         
6166         if(!this.el || this.preventMark){ // not rendered
6167             return;
6168         }
6169         this.el.removeClass(this.invalidClass);
6170         /*
6171         switch(this.msgTarget){
6172             case 'qtip':
6173                 this.el.dom.qtip = '';
6174                 break;
6175             case 'title':
6176                 this.el.dom.title = '';
6177                 break;
6178             case 'under':
6179                 if(this.errorEl){
6180                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6181                 }
6182                 break;
6183             case 'side':
6184                 if(this.errorIcon){
6185                     this.errorIcon.dom.qtip = '';
6186                     this.errorIcon.hide();
6187                     this.un('resize', this.alignErrorIcon, this);
6188                 }
6189                 break;
6190             default:
6191                 var t = Roo.getDom(this.msgTarget);
6192                 t.innerHTML = '';
6193                 t.style.display = 'none';
6194                 break;
6195         }
6196         */
6197         this.fireEvent('valid', this);
6198     },
6199      /**
6200      * Mark this field as invalid
6201      * @param {String} msg The validation message
6202      */
6203     markInvalid : function(msg){
6204         if(!this.el  || this.preventMark){ // not rendered
6205             return;
6206         }
6207         this.el.addClass(this.invalidClass);
6208         /*
6209         msg = msg || this.invalidText;
6210         switch(this.msgTarget){
6211             case 'qtip':
6212                 this.el.dom.qtip = msg;
6213                 this.el.dom.qclass = 'x-form-invalid-tip';
6214                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6215                     Roo.QuickTips.enable();
6216                 }
6217                 break;
6218             case 'title':
6219                 this.el.dom.title = msg;
6220                 break;
6221             case 'under':
6222                 if(!this.errorEl){
6223                     var elp = this.el.findParent('.x-form-element', 5, true);
6224                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6225                     this.errorEl.setWidth(elp.getWidth(true)-20);
6226                 }
6227                 this.errorEl.update(msg);
6228                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6229                 break;
6230             case 'side':
6231                 if(!this.errorIcon){
6232                     var elp = this.el.findParent('.x-form-element', 5, true);
6233                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6234                 }
6235                 this.alignErrorIcon();
6236                 this.errorIcon.dom.qtip = msg;
6237                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6238                 this.errorIcon.show();
6239                 this.on('resize', this.alignErrorIcon, this);
6240                 break;
6241             default:
6242                 var t = Roo.getDom(this.msgTarget);
6243                 t.innerHTML = msg;
6244                 t.style.display = this.msgDisplay;
6245                 break;
6246         }
6247         */
6248         this.fireEvent('invalid', this, msg);
6249     },
6250     // private
6251     SafariOnKeyDown : function(event)
6252     {
6253         // this is a workaround for a password hang bug on chrome/ webkit.
6254         
6255         var isSelectAll = false;
6256         
6257         if(this.inputEl().dom.selectionEnd > 0){
6258             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6259         }
6260         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6261             event.preventDefault();
6262             this.setValue('');
6263             return;
6264         }
6265         
6266         if(isSelectAll){ // backspace and delete key
6267             
6268             event.preventDefault();
6269             // this is very hacky as keydown always get's upper case.
6270             //
6271             var cc = String.fromCharCode(event.getCharCode());
6272             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
6273             
6274         }
6275     },
6276     adjustWidth : function(tag, w){
6277         tag = tag.toLowerCase();
6278         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6279             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6280                 if(tag == 'input'){
6281                     return w + 2;
6282                 }
6283                 if(tag == 'textarea'){
6284                     return w-2;
6285                 }
6286             }else if(Roo.isOpera){
6287                 if(tag == 'input'){
6288                     return w + 2;
6289                 }
6290                 if(tag == 'textarea'){
6291                     return w-2;
6292                 }
6293             }
6294         }
6295         return w;
6296     }
6297     
6298 });
6299
6300  
6301 /*
6302  * - LGPL
6303  *
6304  * Input
6305  * 
6306  */
6307
6308 /**
6309  * @class Roo.bootstrap.TextArea
6310  * @extends Roo.bootstrap.Input
6311  * Bootstrap TextArea class
6312  * @cfg {Number} cols Specifies the visible width of a text area
6313  * @cfg {Number} rows Specifies the visible number of lines in a text area
6314  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6315  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6316  * @cfg {string} html text
6317  * 
6318  * @constructor
6319  * Create a new TextArea
6320  * @param {Object} config The config object
6321  */
6322
6323 Roo.bootstrap.TextArea = function(config){
6324     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6325    
6326 };
6327
6328 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
6329      
6330     cols : false,
6331     rows : 5,
6332     readOnly : false,
6333     warp : 'soft',
6334     resize : false,
6335     value: false,
6336     html: false,
6337     
6338     getAutoCreate : function(){
6339         
6340         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6341         
6342         var id = Roo.id();
6343         
6344         var cfg = {};
6345         
6346         var input =  {
6347             tag: 'textarea',
6348             id : id,
6349             warp : this.warp,
6350             rows : this.rows,
6351             value : this.value || '',
6352             html: this.html || '',
6353             cls : 'form-control',
6354             placeholder : this.placeholder || '' 
6355             
6356         };
6357         
6358         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6359             input.maxLength = this.maxLength;
6360         }
6361         
6362         if(this.resize){
6363             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6364         }
6365         
6366         if(this.cols){
6367             input.cols = this.cols;
6368         }
6369         
6370         if (this.readOnly) {
6371             input.readonly = true;
6372         }
6373         
6374         if (this.name) {
6375             input.name = this.name;
6376         }
6377         
6378         if (this.size) {
6379             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6380         }
6381         
6382         var settings=this;
6383         ['xs','sm','md','lg'].map(function(size){
6384             if (settings[size]) {
6385                 cfg.cls += ' col-' + size + '-' + settings[size];
6386             }
6387         });
6388         
6389         var inputblock = input;
6390         
6391         if (this.before || this.after) {
6392             
6393             inputblock = {
6394                 cls : 'input-group',
6395                 cn :  [] 
6396             };
6397             if (this.before) {
6398                 inputblock.cn.push({
6399                     tag :'span',
6400                     cls : 'input-group-addon',
6401                     html : this.before
6402                 });
6403             }
6404             inputblock.cn.push(input);
6405             if (this.after) {
6406                 inputblock.cn.push({
6407                     tag :'span',
6408                     cls : 'input-group-addon',
6409                     html : this.after
6410                 });
6411             }
6412             
6413         }
6414         
6415         if (align ==='left' && this.fieldLabel.length) {
6416                 Roo.log("left and has label");
6417                 cfg.cn = [
6418                     
6419                     {
6420                         tag: 'label',
6421                         'for' :  id,
6422                         cls : 'control-label col-sm-' + this.labelWidth,
6423                         html : this.fieldLabel
6424                         
6425                     },
6426                     {
6427                         cls : "col-sm-" + (12 - this.labelWidth), 
6428                         cn: [
6429                             inputblock
6430                         ]
6431                     }
6432                     
6433                 ];
6434         } else if ( this.fieldLabel.length) {
6435                 Roo.log(" label");
6436                  cfg.cn = [
6437                    
6438                     {
6439                         tag: 'label',
6440                         //cls : 'input-group-addon',
6441                         html : this.fieldLabel
6442                         
6443                     },
6444                     
6445                     inputblock
6446                     
6447                 ];
6448
6449         } else {
6450             
6451                    Roo.log(" no label && no align");
6452                 cfg.cn = [
6453                     
6454                         inputblock
6455                     
6456                 ];
6457                 
6458                 
6459         }
6460         
6461         if (this.disabled) {
6462             input.disabled=true;
6463         }
6464         
6465         return cfg;
6466         
6467     },
6468     /**
6469      * return the real textarea element.
6470      */
6471     inputEl: function ()
6472     {
6473         return this.el.select('textarea.form-control',true).first();
6474     }
6475 });
6476
6477  
6478 /*
6479  * - LGPL
6480  *
6481  * trigger field - base class for combo..
6482  * 
6483  */
6484  
6485 /**
6486  * @class Roo.bootstrap.TriggerField
6487  * @extends Roo.bootstrap.Input
6488  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6489  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6490  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6491  * for which you can provide a custom implementation.  For example:
6492  * <pre><code>
6493 var trigger = new Roo.bootstrap.TriggerField();
6494 trigger.onTriggerClick = myTriggerFn;
6495 trigger.applyTo('my-field');
6496 </code></pre>
6497  *
6498  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6499  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6500  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
6501  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6502  * @constructor
6503  * Create a new TriggerField.
6504  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6505  * to the base TextField)
6506  */
6507 Roo.bootstrap.TriggerField = function(config){
6508     this.mimicing = false;
6509     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6510 };
6511
6512 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
6513     /**
6514      * @cfg {String} triggerClass A CSS class to apply to the trigger
6515      */
6516      /**
6517      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6518      */
6519     hideTrigger:false,
6520
6521     /** @cfg {Boolean} grow @hide */
6522     /** @cfg {Number} growMin @hide */
6523     /** @cfg {Number} growMax @hide */
6524
6525     /**
6526      * @hide 
6527      * @method
6528      */
6529     autoSize: Roo.emptyFn,
6530     // private
6531     monitorTab : true,
6532     // private
6533     deferHeight : true,
6534
6535     
6536     actionMode : 'wrap',
6537     
6538     
6539     
6540     getAutoCreate : function(){
6541        
6542         var parent = this.parent();
6543         
6544         var align = this.labelAlign || this.parentLabelAlign();
6545         
6546         var id = Roo.id();
6547         
6548         var cfg = {
6549             cls: 'form-group' //input-group
6550         };
6551         
6552         
6553         var input =  {
6554             tag: 'input',
6555             id : id,
6556             type : this.inputType,
6557             cls : 'form-control',
6558             autocomplete: 'off',
6559             placeholder : this.placeholder || '' 
6560             
6561         };
6562         if (this.name) {
6563             input.name = this.name;
6564         }
6565         if (this.size) {
6566             input.cls += ' input-' + this.size;
6567         }
6568         
6569         if (this.disabled) {
6570             input.disabled=true;
6571         }
6572         
6573         var inputblock = input;
6574         
6575         if (this.before || this.after) {
6576             
6577             inputblock = {
6578                 cls : 'input-group',
6579                 cn :  [] 
6580             };
6581             if (this.before) {
6582                 inputblock.cn.push({
6583                     tag :'span',
6584                     cls : 'input-group-addon',
6585                     html : this.before
6586                 });
6587             }
6588             inputblock.cn.push(input);
6589             if (this.after) {
6590                 inputblock.cn.push({
6591                     tag :'span',
6592                     cls : 'input-group-addon',
6593                     html : this.after
6594                 });
6595             }
6596             
6597         };
6598         
6599         var box = {
6600             tag: 'div',
6601             cn: [
6602                 {
6603                     tag: 'input',
6604                     type : 'hidden',
6605                     cls: 'form-hidden-field'
6606                 },
6607                 inputblock
6608             ]
6609             
6610         };
6611         
6612         if(this.multiple){
6613             Roo.log('multiple');
6614             
6615             box = {
6616                 tag: 'div',
6617                 cn: [
6618                     {
6619                         tag: 'input',
6620                         type : 'hidden',
6621                         cls: 'form-hidden-field'
6622                     },
6623                     {
6624                         tag: 'ul',
6625                         cls: 'select2-choices',
6626                         cn:[
6627                             {
6628                                 tag: 'li',
6629                                 cls: 'select2-search-field',
6630                                 cn: [
6631
6632                                     inputblock
6633                                 ]
6634                             }
6635                         ]
6636                     }
6637                 ]
6638             }
6639         };
6640         
6641         var combobox = {
6642             cls: 'select2-container input-group',
6643             cn: [
6644                 box,
6645                 {
6646                     tag: 'ul',
6647                     cls: 'typeahead typeahead-long dropdown-menu',
6648                     style: 'display:none'
6649                 }
6650             ]
6651         };
6652         
6653         if(!this.multiple){
6654             combobox.cn.push({
6655                 tag :'span',
6656                 cls : 'input-group-addon btn dropdown-toggle',
6657                 cn : [
6658                     {
6659                         tag: 'span',
6660                         cls: 'caret'
6661                     },
6662                     {
6663                         tag: 'span',
6664                         cls: 'combobox-clear',
6665                         cn  : [
6666                             {
6667                                 tag : 'i',
6668                                 cls: 'icon-remove'
6669                             }
6670                         ]
6671                     }
6672                 ]
6673
6674             })
6675         }
6676         
6677         if(this.multiple){
6678             combobox.cls += ' select2-container-multi';
6679         }
6680         
6681         if (align ==='left' && this.fieldLabel.length) {
6682             
6683                 Roo.log("left and has label");
6684                 cfg.cn = [
6685                     
6686                     {
6687                         tag: 'label',
6688                         'for' :  id,
6689                         cls : 'control-label col-sm-' + this.labelWidth,
6690                         html : this.fieldLabel
6691                         
6692                     },
6693                     {
6694                         cls : "col-sm-" + (12 - this.labelWidth), 
6695                         cn: [
6696                             combobox
6697                         ]
6698                     }
6699                     
6700                 ];
6701         } else if ( this.fieldLabel.length) {
6702                 Roo.log(" label");
6703                  cfg.cn = [
6704                    
6705                     {
6706                         tag: 'label',
6707                         //cls : 'input-group-addon',
6708                         html : this.fieldLabel
6709                         
6710                     },
6711                     
6712                     combobox
6713                     
6714                 ];
6715
6716         } else {
6717             
6718                 Roo.log(" no label && no align");
6719                 cfg = combobox
6720                      
6721                 
6722         }
6723          
6724         var settings=this;
6725         ['xs','sm','md','lg'].map(function(size){
6726             if (settings[size]) {
6727                 cfg.cls += ' col-' + size + '-' + settings[size];
6728             }
6729         });
6730         
6731         return cfg;
6732         
6733     },
6734     
6735     
6736     
6737     // private
6738     onResize : function(w, h){
6739 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6740 //        if(typeof w == 'number'){
6741 //            var x = w - this.trigger.getWidth();
6742 //            this.inputEl().setWidth(this.adjustWidth('input', x));
6743 //            this.trigger.setStyle('left', x+'px');
6744 //        }
6745     },
6746
6747     // private
6748     adjustSize : Roo.BoxComponent.prototype.adjustSize,
6749
6750     // private
6751     getResizeEl : function(){
6752         return this.inputEl();
6753     },
6754
6755     // private
6756     getPositionEl : function(){
6757         return this.inputEl();
6758     },
6759
6760     // private
6761     alignErrorIcon : function(){
6762         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6763     },
6764
6765     // private
6766     initEvents : function(){
6767         
6768         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6769         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6770         if(!this.multiple){
6771             this.trigger = this.el.select('span.dropdown-toggle',true).first();
6772             if(this.hideTrigger){
6773                 this.trigger.setDisplayed(false);
6774             }
6775             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6776         }
6777         
6778         if(this.multiple){
6779             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6780         }
6781         
6782         //this.trigger.addClassOnOver('x-form-trigger-over');
6783         //this.trigger.addClassOnClick('x-form-trigger-click');
6784         
6785         //if(!this.width){
6786         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6787         //}
6788     },
6789
6790     // private
6791     initTrigger : function(){
6792        
6793     },
6794
6795     // private
6796     onDestroy : function(){
6797         if(this.trigger){
6798             this.trigger.removeAllListeners();
6799           //  this.trigger.remove();
6800         }
6801         //if(this.wrap){
6802         //    this.wrap.remove();
6803         //}
6804         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6805     },
6806
6807     // private
6808     onFocus : function(){
6809         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6810         /*
6811         if(!this.mimicing){
6812             this.wrap.addClass('x-trigger-wrap-focus');
6813             this.mimicing = true;
6814             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6815             if(this.monitorTab){
6816                 this.el.on("keydown", this.checkTab, this);
6817             }
6818         }
6819         */
6820     },
6821
6822     // private
6823     checkTab : function(e){
6824         if(e.getKey() == e.TAB){
6825             this.triggerBlur();
6826         }
6827     },
6828
6829     // private
6830     onBlur : function(){
6831         // do nothing
6832     },
6833
6834     // private
6835     mimicBlur : function(e, t){
6836         /*
6837         if(!this.wrap.contains(t) && this.validateBlur()){
6838             this.triggerBlur();
6839         }
6840         */
6841     },
6842
6843     // private
6844     triggerBlur : function(){
6845         this.mimicing = false;
6846         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6847         if(this.monitorTab){
6848             this.el.un("keydown", this.checkTab, this);
6849         }
6850         //this.wrap.removeClass('x-trigger-wrap-focus');
6851         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6852     },
6853
6854     // private
6855     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6856     validateBlur : function(e, t){
6857         return true;
6858     },
6859
6860     // private
6861     onDisable : function(){
6862         this.inputEl().dom.disabled = true;
6863         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6864         //if(this.wrap){
6865         //    this.wrap.addClass('x-item-disabled');
6866         //}
6867     },
6868
6869     // private
6870     onEnable : function(){
6871         this.inputEl().dom.disabled = false;
6872         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6873         //if(this.wrap){
6874         //    this.el.removeClass('x-item-disabled');
6875         //}
6876     },
6877
6878     // private
6879     onShow : function(){
6880         var ae = this.getActionEl();
6881         
6882         if(ae){
6883             ae.dom.style.display = '';
6884             ae.dom.style.visibility = 'visible';
6885         }
6886     },
6887
6888     // private
6889     
6890     onHide : function(){
6891         var ae = this.getActionEl();
6892         ae.dom.style.display = 'none';
6893     },
6894
6895     /**
6896      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
6897      * by an implementing function.
6898      * @method
6899      * @param {EventObject} e
6900      */
6901     onTriggerClick : Roo.emptyFn
6902 });
6903  /*
6904  * Based on:
6905  * Ext JS Library 1.1.1
6906  * Copyright(c) 2006-2007, Ext JS, LLC.
6907  *
6908  * Originally Released Under LGPL - original licence link has changed is not relivant.
6909  *
6910  * Fork - LGPL
6911  * <script type="text/javascript">
6912  */
6913
6914
6915 /**
6916  * @class Roo.data.SortTypes
6917  * @singleton
6918  * Defines the default sorting (casting?) comparison functions used when sorting data.
6919  */
6920 Roo.data.SortTypes = {
6921     /**
6922      * Default sort that does nothing
6923      * @param {Mixed} s The value being converted
6924      * @return {Mixed} The comparison value
6925      */
6926     none : function(s){
6927         return s;
6928     },
6929     
6930     /**
6931      * The regular expression used to strip tags
6932      * @type {RegExp}
6933      * @property
6934      */
6935     stripTagsRE : /<\/?[^>]+>/gi,
6936     
6937     /**
6938      * Strips all HTML tags to sort on text only
6939      * @param {Mixed} s The value being converted
6940      * @return {String} The comparison value
6941      */
6942     asText : function(s){
6943         return String(s).replace(this.stripTagsRE, "");
6944     },
6945     
6946     /**
6947      * Strips all HTML tags to sort on text only - Case insensitive
6948      * @param {Mixed} s The value being converted
6949      * @return {String} The comparison value
6950      */
6951     asUCText : function(s){
6952         return String(s).toUpperCase().replace(this.stripTagsRE, "");
6953     },
6954     
6955     /**
6956      * Case insensitive string
6957      * @param {Mixed} s The value being converted
6958      * @return {String} The comparison value
6959      */
6960     asUCString : function(s) {
6961         return String(s).toUpperCase();
6962     },
6963     
6964     /**
6965      * Date sorting
6966      * @param {Mixed} s The value being converted
6967      * @return {Number} The comparison value
6968      */
6969     asDate : function(s) {
6970         if(!s){
6971             return 0;
6972         }
6973         if(s instanceof Date){
6974             return s.getTime();
6975         }
6976         return Date.parse(String(s));
6977     },
6978     
6979     /**
6980      * Float sorting
6981      * @param {Mixed} s The value being converted
6982      * @return {Float} The comparison value
6983      */
6984     asFloat : function(s) {
6985         var val = parseFloat(String(s).replace(/,/g, ""));
6986         if(isNaN(val)) val = 0;
6987         return val;
6988     },
6989     
6990     /**
6991      * Integer sorting
6992      * @param {Mixed} s The value being converted
6993      * @return {Number} The comparison value
6994      */
6995     asInt : function(s) {
6996         var val = parseInt(String(s).replace(/,/g, ""));
6997         if(isNaN(val)) val = 0;
6998         return val;
6999     }
7000 };/*
7001  * Based on:
7002  * Ext JS Library 1.1.1
7003  * Copyright(c) 2006-2007, Ext JS, LLC.
7004  *
7005  * Originally Released Under LGPL - original licence link has changed is not relivant.
7006  *
7007  * Fork - LGPL
7008  * <script type="text/javascript">
7009  */
7010
7011 /**
7012 * @class Roo.data.Record
7013  * Instances of this class encapsulate both record <em>definition</em> information, and record
7014  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7015  * to access Records cached in an {@link Roo.data.Store} object.<br>
7016  * <p>
7017  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7018  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7019  * objects.<br>
7020  * <p>
7021  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7022  * @constructor
7023  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7024  * {@link #create}. The parameters are the same.
7025  * @param {Array} data An associative Array of data values keyed by the field name.
7026  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7027  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7028  * not specified an integer id is generated.
7029  */
7030 Roo.data.Record = function(data, id){
7031     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7032     this.data = data;
7033 };
7034
7035 /**
7036  * Generate a constructor for a specific record layout.
7037  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7038  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7039  * Each field definition object may contain the following properties: <ul>
7040  * <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,
7041  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7042  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7043  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7044  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7045  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7046  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7047  * this may be omitted.</p></li>
7048  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7049  * <ul><li>auto (Default, implies no conversion)</li>
7050  * <li>string</li>
7051  * <li>int</li>
7052  * <li>float</li>
7053  * <li>boolean</li>
7054  * <li>date</li></ul></p></li>
7055  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7056  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7057  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7058  * by the Reader into an object that will be stored in the Record. It is passed the
7059  * following parameters:<ul>
7060  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7061  * </ul></p></li>
7062  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7063  * </ul>
7064  * <br>usage:<br><pre><code>
7065 var TopicRecord = Roo.data.Record.create(
7066     {name: 'title', mapping: 'topic_title'},
7067     {name: 'author', mapping: 'username'},
7068     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7069     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7070     {name: 'lastPoster', mapping: 'user2'},
7071     {name: 'excerpt', mapping: 'post_text'}
7072 );
7073
7074 var myNewRecord = new TopicRecord({
7075     title: 'Do my job please',
7076     author: 'noobie',
7077     totalPosts: 1,
7078     lastPost: new Date(),
7079     lastPoster: 'Animal',
7080     excerpt: 'No way dude!'
7081 });
7082 myStore.add(myNewRecord);
7083 </code></pre>
7084  * @method create
7085  * @static
7086  */
7087 Roo.data.Record.create = function(o){
7088     var f = function(){
7089         f.superclass.constructor.apply(this, arguments);
7090     };
7091     Roo.extend(f, Roo.data.Record);
7092     var p = f.prototype;
7093     p.fields = new Roo.util.MixedCollection(false, function(field){
7094         return field.name;
7095     });
7096     for(var i = 0, len = o.length; i < len; i++){
7097         p.fields.add(new Roo.data.Field(o[i]));
7098     }
7099     f.getField = function(name){
7100         return p.fields.get(name);  
7101     };
7102     return f;
7103 };
7104
7105 Roo.data.Record.AUTO_ID = 1000;
7106 Roo.data.Record.EDIT = 'edit';
7107 Roo.data.Record.REJECT = 'reject';
7108 Roo.data.Record.COMMIT = 'commit';
7109
7110 Roo.data.Record.prototype = {
7111     /**
7112      * Readonly flag - true if this record has been modified.
7113      * @type Boolean
7114      */
7115     dirty : false,
7116     editing : false,
7117     error: null,
7118     modified: null,
7119
7120     // private
7121     join : function(store){
7122         this.store = store;
7123     },
7124
7125     /**
7126      * Set the named field to the specified value.
7127      * @param {String} name The name of the field to set.
7128      * @param {Object} value The value to set the field to.
7129      */
7130     set : function(name, value){
7131         if(this.data[name] == value){
7132             return;
7133         }
7134         this.dirty = true;
7135         if(!this.modified){
7136             this.modified = {};
7137         }
7138         if(typeof this.modified[name] == 'undefined'){
7139             this.modified[name] = this.data[name];
7140         }
7141         this.data[name] = value;
7142         if(!this.editing && this.store){
7143             this.store.afterEdit(this);
7144         }       
7145     },
7146
7147     /**
7148      * Get the value of the named field.
7149      * @param {String} name The name of the field to get the value of.
7150      * @return {Object} The value of the field.
7151      */
7152     get : function(name){
7153         return this.data[name]; 
7154     },
7155
7156     // private
7157     beginEdit : function(){
7158         this.editing = true;
7159         this.modified = {}; 
7160     },
7161
7162     // private
7163     cancelEdit : function(){
7164         this.editing = false;
7165         delete this.modified;
7166     },
7167
7168     // private
7169     endEdit : function(){
7170         this.editing = false;
7171         if(this.dirty && this.store){
7172             this.store.afterEdit(this);
7173         }
7174     },
7175
7176     /**
7177      * Usually called by the {@link Roo.data.Store} which owns the Record.
7178      * Rejects all changes made to the Record since either creation, or the last commit operation.
7179      * Modified fields are reverted to their original values.
7180      * <p>
7181      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7182      * of reject operations.
7183      */
7184     reject : function(){
7185         var m = this.modified;
7186         for(var n in m){
7187             if(typeof m[n] != "function"){
7188                 this.data[n] = m[n];
7189             }
7190         }
7191         this.dirty = false;
7192         delete this.modified;
7193         this.editing = false;
7194         if(this.store){
7195             this.store.afterReject(this);
7196         }
7197     },
7198
7199     /**
7200      * Usually called by the {@link Roo.data.Store} which owns the Record.
7201      * Commits all changes made to the Record since either creation, or the last commit operation.
7202      * <p>
7203      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7204      * of commit operations.
7205      */
7206     commit : function(){
7207         this.dirty = false;
7208         delete this.modified;
7209         this.editing = false;
7210         if(this.store){
7211             this.store.afterCommit(this);
7212         }
7213     },
7214
7215     // private
7216     hasError : function(){
7217         return this.error != null;
7218     },
7219
7220     // private
7221     clearError : function(){
7222         this.error = null;
7223     },
7224
7225     /**
7226      * Creates a copy of this record.
7227      * @param {String} id (optional) A new record id if you don't want to use this record's id
7228      * @return {Record}
7229      */
7230     copy : function(newId) {
7231         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7232     }
7233 };/*
7234  * Based on:
7235  * Ext JS Library 1.1.1
7236  * Copyright(c) 2006-2007, Ext JS, LLC.
7237  *
7238  * Originally Released Under LGPL - original licence link has changed is not relivant.
7239  *
7240  * Fork - LGPL
7241  * <script type="text/javascript">
7242  */
7243
7244
7245
7246 /**
7247  * @class Roo.data.Store
7248  * @extends Roo.util.Observable
7249  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7250  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7251  * <p>
7252  * 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
7253  * has no knowledge of the format of the data returned by the Proxy.<br>
7254  * <p>
7255  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7256  * instances from the data object. These records are cached and made available through accessor functions.
7257  * @constructor
7258  * Creates a new Store.
7259  * @param {Object} config A config object containing the objects needed for the Store to access data,
7260  * and read the data into Records.
7261  */
7262 Roo.data.Store = function(config){
7263     this.data = new Roo.util.MixedCollection(false);
7264     this.data.getKey = function(o){
7265         return o.id;
7266     };
7267     this.baseParams = {};
7268     // private
7269     this.paramNames = {
7270         "start" : "start",
7271         "limit" : "limit",
7272         "sort" : "sort",
7273         "dir" : "dir",
7274         "multisort" : "_multisort"
7275     };
7276
7277     if(config && config.data){
7278         this.inlineData = config.data;
7279         delete config.data;
7280     }
7281
7282     Roo.apply(this, config);
7283     
7284     if(this.reader){ // reader passed
7285         this.reader = Roo.factory(this.reader, Roo.data);
7286         this.reader.xmodule = this.xmodule || false;
7287         if(!this.recordType){
7288             this.recordType = this.reader.recordType;
7289         }
7290         if(this.reader.onMetaChange){
7291             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7292         }
7293     }
7294
7295     if(this.recordType){
7296         this.fields = this.recordType.prototype.fields;
7297     }
7298     this.modified = [];
7299
7300     this.addEvents({
7301         /**
7302          * @event datachanged
7303          * Fires when the data cache has changed, and a widget which is using this Store
7304          * as a Record cache should refresh its view.
7305          * @param {Store} this
7306          */
7307         datachanged : true,
7308         /**
7309          * @event metachange
7310          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7311          * @param {Store} this
7312          * @param {Object} meta The JSON metadata
7313          */
7314         metachange : true,
7315         /**
7316          * @event add
7317          * Fires when Records have been added to the Store
7318          * @param {Store} this
7319          * @param {Roo.data.Record[]} records The array of Records added
7320          * @param {Number} index The index at which the record(s) were added
7321          */
7322         add : true,
7323         /**
7324          * @event remove
7325          * Fires when a Record has been removed from the Store
7326          * @param {Store} this
7327          * @param {Roo.data.Record} record The Record that was removed
7328          * @param {Number} index The index at which the record was removed
7329          */
7330         remove : true,
7331         /**
7332          * @event update
7333          * Fires when a Record has been updated
7334          * @param {Store} this
7335          * @param {Roo.data.Record} record The Record that was updated
7336          * @param {String} operation The update operation being performed.  Value may be one of:
7337          * <pre><code>
7338  Roo.data.Record.EDIT
7339  Roo.data.Record.REJECT
7340  Roo.data.Record.COMMIT
7341          * </code></pre>
7342          */
7343         update : true,
7344         /**
7345          * @event clear
7346          * Fires when the data cache has been cleared.
7347          * @param {Store} this
7348          */
7349         clear : true,
7350         /**
7351          * @event beforeload
7352          * Fires before a request is made for a new data object.  If the beforeload handler returns false
7353          * the load action will be canceled.
7354          * @param {Store} this
7355          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7356          */
7357         beforeload : true,
7358         /**
7359          * @event beforeloadadd
7360          * Fires after a new set of Records has been loaded.
7361          * @param {Store} this
7362          * @param {Roo.data.Record[]} records The Records that were loaded
7363          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7364          */
7365         beforeloadadd : true,
7366         /**
7367          * @event load
7368          * Fires after a new set of Records has been loaded, before they are added to the store.
7369          * @param {Store} this
7370          * @param {Roo.data.Record[]} records The Records that were loaded
7371          * @param {Object} options The loading options that were specified (see {@link #load} for details)
7372          * @params {Object} return from reader
7373          */
7374         load : true,
7375         /**
7376          * @event loadexception
7377          * Fires if an exception occurs in the Proxy during loading.
7378          * Called with the signature of the Proxy's "loadexception" event.
7379          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7380          * 
7381          * @param {Proxy} 
7382          * @param {Object} return from JsonData.reader() - success, totalRecords, records
7383          * @param {Object} load options 
7384          * @param {Object} jsonData from your request (normally this contains the Exception)
7385          */
7386         loadexception : true
7387     });
7388     
7389     if(this.proxy){
7390         this.proxy = Roo.factory(this.proxy, Roo.data);
7391         this.proxy.xmodule = this.xmodule || false;
7392         this.relayEvents(this.proxy,  ["loadexception"]);
7393     }
7394     this.sortToggle = {};
7395     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7396
7397     Roo.data.Store.superclass.constructor.call(this);
7398
7399     if(this.inlineData){
7400         this.loadData(this.inlineData);
7401         delete this.inlineData;
7402     }
7403 };
7404
7405 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7406      /**
7407     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
7408     * without a remote query - used by combo/forms at present.
7409     */
7410     
7411     /**
7412     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7413     */
7414     /**
7415     * @cfg {Array} data Inline data to be loaded when the store is initialized.
7416     */
7417     /**
7418     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7419     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7420     */
7421     /**
7422     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7423     * on any HTTP request
7424     */
7425     /**
7426     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7427     */
7428     /**
7429     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7430     */
7431     multiSort: false,
7432     /**
7433     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7434     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7435     */
7436     remoteSort : false,
7437
7438     /**
7439     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7440      * loaded or when a record is removed. (defaults to false).
7441     */
7442     pruneModifiedRecords : false,
7443
7444     // private
7445     lastOptions : null,
7446
7447     /**
7448      * Add Records to the Store and fires the add event.
7449      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7450      */
7451     add : function(records){
7452         records = [].concat(records);
7453         for(var i = 0, len = records.length; i < len; i++){
7454             records[i].join(this);
7455         }
7456         var index = this.data.length;
7457         this.data.addAll(records);
7458         this.fireEvent("add", this, records, index);
7459     },
7460
7461     /**
7462      * Remove a Record from the Store and fires the remove event.
7463      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7464      */
7465     remove : function(record){
7466         var index = this.data.indexOf(record);
7467         this.data.removeAt(index);
7468         if(this.pruneModifiedRecords){
7469             this.modified.remove(record);
7470         }
7471         this.fireEvent("remove", this, record, index);
7472     },
7473
7474     /**
7475      * Remove all Records from the Store and fires the clear event.
7476      */
7477     removeAll : function(){
7478         this.data.clear();
7479         if(this.pruneModifiedRecords){
7480             this.modified = [];
7481         }
7482         this.fireEvent("clear", this);
7483     },
7484
7485     /**
7486      * Inserts Records to the Store at the given index and fires the add event.
7487      * @param {Number} index The start index at which to insert the passed Records.
7488      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7489      */
7490     insert : function(index, records){
7491         records = [].concat(records);
7492         for(var i = 0, len = records.length; i < len; i++){
7493             this.data.insert(index, records[i]);
7494             records[i].join(this);
7495         }
7496         this.fireEvent("add", this, records, index);
7497     },
7498
7499     /**
7500      * Get the index within the cache of the passed Record.
7501      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7502      * @return {Number} The index of the passed Record. Returns -1 if not found.
7503      */
7504     indexOf : function(record){
7505         return this.data.indexOf(record);
7506     },
7507
7508     /**
7509      * Get the index within the cache of the Record with the passed id.
7510      * @param {String} id The id of the Record to find.
7511      * @return {Number} The index of the Record. Returns -1 if not found.
7512      */
7513     indexOfId : function(id){
7514         return this.data.indexOfKey(id);
7515     },
7516
7517     /**
7518      * Get the Record with the specified id.
7519      * @param {String} id The id of the Record to find.
7520      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7521      */
7522     getById : function(id){
7523         return this.data.key(id);
7524     },
7525
7526     /**
7527      * Get the Record at the specified index.
7528      * @param {Number} index The index of the Record to find.
7529      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7530      */
7531     getAt : function(index){
7532         return this.data.itemAt(index);
7533     },
7534
7535     /**
7536      * Returns a range of Records between specified indices.
7537      * @param {Number} startIndex (optional) The starting index (defaults to 0)
7538      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7539      * @return {Roo.data.Record[]} An array of Records
7540      */
7541     getRange : function(start, end){
7542         return this.data.getRange(start, end);
7543     },
7544
7545     // private
7546     storeOptions : function(o){
7547         o = Roo.apply({}, o);
7548         delete o.callback;
7549         delete o.scope;
7550         this.lastOptions = o;
7551     },
7552
7553     /**
7554      * Loads the Record cache from the configured Proxy using the configured Reader.
7555      * <p>
7556      * If using remote paging, then the first load call must specify the <em>start</em>
7557      * and <em>limit</em> properties in the options.params property to establish the initial
7558      * position within the dataset, and the number of Records to cache on each read from the Proxy.
7559      * <p>
7560      * <strong>It is important to note that for remote data sources, loading is asynchronous,
7561      * and this call will return before the new data has been loaded. Perform any post-processing
7562      * in a callback function, or in a "load" event handler.</strong>
7563      * <p>
7564      * @param {Object} options An object containing properties which control loading options:<ul>
7565      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7566      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7567      * passed the following arguments:<ul>
7568      * <li>r : Roo.data.Record[]</li>
7569      * <li>options: Options object from the load call</li>
7570      * <li>success: Boolean success indicator</li></ul></li>
7571      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7572      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7573      * </ul>
7574      */
7575     load : function(options){
7576         options = options || {};
7577         if(this.fireEvent("beforeload", this, options) !== false){
7578             this.storeOptions(options);
7579             var p = Roo.apply(options.params || {}, this.baseParams);
7580             // if meta was not loaded from remote source.. try requesting it.
7581             if (!this.reader.metaFromRemote) {
7582                 p._requestMeta = 1;
7583             }
7584             if(this.sortInfo && this.remoteSort){
7585                 var pn = this.paramNames;
7586                 p[pn["sort"]] = this.sortInfo.field;
7587                 p[pn["dir"]] = this.sortInfo.direction;
7588             }
7589             if (this.multiSort) {
7590                 var pn = this.paramNames;
7591                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7592             }
7593             
7594             this.proxy.load(p, this.reader, this.loadRecords, this, options);
7595         }
7596     },
7597
7598     /**
7599      * Reloads the Record cache from the configured Proxy using the configured Reader and
7600      * the options from the last load operation performed.
7601      * @param {Object} options (optional) An object containing properties which may override the options
7602      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7603      * the most recently used options are reused).
7604      */
7605     reload : function(options){
7606         this.load(Roo.applyIf(options||{}, this.lastOptions));
7607     },
7608
7609     // private
7610     // Called as a callback by the Reader during a load operation.
7611     loadRecords : function(o, options, success){
7612         if(!o || success === false){
7613             if(success !== false){
7614                 this.fireEvent("load", this, [], options, o);
7615             }
7616             if(options.callback){
7617                 options.callback.call(options.scope || this, [], options, false);
7618             }
7619             return;
7620         }
7621         // if data returned failure - throw an exception.
7622         if (o.success === false) {
7623             // show a message if no listener is registered.
7624             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7625                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7626             }
7627             // loadmask wil be hooked into this..
7628             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7629             return;
7630         }
7631         var r = o.records, t = o.totalRecords || r.length;
7632         
7633         this.fireEvent("beforeloadadd", this, r, options, o);
7634         
7635         if(!options || options.add !== true){
7636             if(this.pruneModifiedRecords){
7637                 this.modified = [];
7638             }
7639             for(var i = 0, len = r.length; i < len; i++){
7640                 r[i].join(this);
7641             }
7642             if(this.snapshot){
7643                 this.data = this.snapshot;
7644                 delete this.snapshot;
7645             }
7646             this.data.clear();
7647             this.data.addAll(r);
7648             this.totalLength = t;
7649             this.applySort();
7650             this.fireEvent("datachanged", this);
7651         }else{
7652             this.totalLength = Math.max(t, this.data.length+r.length);
7653             this.add(r);
7654         }
7655         this.fireEvent("load", this, r, options, o);
7656         if(options.callback){
7657             options.callback.call(options.scope || this, r, options, true);
7658         }
7659     },
7660
7661
7662     /**
7663      * Loads data from a passed data block. A Reader which understands the format of the data
7664      * must have been configured in the constructor.
7665      * @param {Object} data The data block from which to read the Records.  The format of the data expected
7666      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7667      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7668      */
7669     loadData : function(o, append){
7670         var r = this.reader.readRecords(o);
7671         this.loadRecords(r, {add: append}, true);
7672     },
7673
7674     /**
7675      * Gets the number of cached records.
7676      * <p>
7677      * <em>If using paging, this may not be the total size of the dataset. If the data object
7678      * used by the Reader contains the dataset size, then the getTotalCount() function returns
7679      * the data set size</em>
7680      */
7681     getCount : function(){
7682         return this.data.length || 0;
7683     },
7684
7685     /**
7686      * Gets the total number of records in the dataset as returned by the server.
7687      * <p>
7688      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7689      * the dataset size</em>
7690      */
7691     getTotalCount : function(){
7692         return this.totalLength || 0;
7693     },
7694
7695     /**
7696      * Returns the sort state of the Store as an object with two properties:
7697      * <pre><code>
7698  field {String} The name of the field by which the Records are sorted
7699  direction {String} The sort order, "ASC" or "DESC"
7700      * </code></pre>
7701      */
7702     getSortState : function(){
7703         return this.sortInfo;
7704     },
7705
7706     // private
7707     applySort : function(){
7708         if(this.sortInfo && !this.remoteSort){
7709             var s = this.sortInfo, f = s.field;
7710             var st = this.fields.get(f).sortType;
7711             var fn = function(r1, r2){
7712                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7713                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7714             };
7715             this.data.sort(s.direction, fn);
7716             if(this.snapshot && this.snapshot != this.data){
7717                 this.snapshot.sort(s.direction, fn);
7718             }
7719         }
7720     },
7721
7722     /**
7723      * Sets the default sort column and order to be used by the next load operation.
7724      * @param {String} fieldName The name of the field to sort by.
7725      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7726      */
7727     setDefaultSort : function(field, dir){
7728         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7729     },
7730
7731     /**
7732      * Sort the Records.
7733      * If remote sorting is used, the sort is performed on the server, and the cache is
7734      * reloaded. If local sorting is used, the cache is sorted internally.
7735      * @param {String} fieldName The name of the field to sort by.
7736      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7737      */
7738     sort : function(fieldName, dir){
7739         var f = this.fields.get(fieldName);
7740         if(!dir){
7741             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7742             
7743             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7744                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7745             }else{
7746                 dir = f.sortDir;
7747             }
7748         }
7749         this.sortToggle[f.name] = dir;
7750         this.sortInfo = {field: f.name, direction: dir};
7751         if(!this.remoteSort){
7752             this.applySort();
7753             this.fireEvent("datachanged", this);
7754         }else{
7755             this.load(this.lastOptions);
7756         }
7757     },
7758
7759     /**
7760      * Calls the specified function for each of the Records in the cache.
7761      * @param {Function} fn The function to call. The Record is passed as the first parameter.
7762      * Returning <em>false</em> aborts and exits the iteration.
7763      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7764      */
7765     each : function(fn, scope){
7766         this.data.each(fn, scope);
7767     },
7768
7769     /**
7770      * Gets all records modified since the last commit.  Modified records are persisted across load operations
7771      * (e.g., during paging).
7772      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7773      */
7774     getModifiedRecords : function(){
7775         return this.modified;
7776     },
7777
7778     // private
7779     createFilterFn : function(property, value, anyMatch){
7780         if(!value.exec){ // not a regex
7781             value = String(value);
7782             if(value.length == 0){
7783                 return false;
7784             }
7785             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7786         }
7787         return function(r){
7788             return value.test(r.data[property]);
7789         };
7790     },
7791
7792     /**
7793      * Sums the value of <i>property</i> for each record between start and end and returns the result.
7794      * @param {String} property A field on your records
7795      * @param {Number} start The record index to start at (defaults to 0)
7796      * @param {Number} end The last record index to include (defaults to length - 1)
7797      * @return {Number} The sum
7798      */
7799     sum : function(property, start, end){
7800         var rs = this.data.items, v = 0;
7801         start = start || 0;
7802         end = (end || end === 0) ? end : rs.length-1;
7803
7804         for(var i = start; i <= end; i++){
7805             v += (rs[i].data[property] || 0);
7806         }
7807         return v;
7808     },
7809
7810     /**
7811      * Filter the records by a specified property.
7812      * @param {String} field A field on your records
7813      * @param {String/RegExp} value Either a string that the field
7814      * should start with or a RegExp to test against the field
7815      * @param {Boolean} anyMatch True to match any part not just the beginning
7816      */
7817     filter : function(property, value, anyMatch){
7818         var fn = this.createFilterFn(property, value, anyMatch);
7819         return fn ? this.filterBy(fn) : this.clearFilter();
7820     },
7821
7822     /**
7823      * Filter by a function. The specified function will be called with each
7824      * record in this data source. If the function returns true the record is included,
7825      * otherwise it is filtered.
7826      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7827      * @param {Object} scope (optional) The scope of the function (defaults to this)
7828      */
7829     filterBy : function(fn, scope){
7830         this.snapshot = this.snapshot || this.data;
7831         this.data = this.queryBy(fn, scope||this);
7832         this.fireEvent("datachanged", this);
7833     },
7834
7835     /**
7836      * Query the records by a specified property.
7837      * @param {String} field A field on your records
7838      * @param {String/RegExp} value Either a string that the field
7839      * should start with or a RegExp to test against the field
7840      * @param {Boolean} anyMatch True to match any part not just the beginning
7841      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7842      */
7843     query : function(property, value, anyMatch){
7844         var fn = this.createFilterFn(property, value, anyMatch);
7845         return fn ? this.queryBy(fn) : this.data.clone();
7846     },
7847
7848     /**
7849      * Query by a function. The specified function will be called with each
7850      * record in this data source. If the function returns true the record is included
7851      * in the results.
7852      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7853      * @param {Object} scope (optional) The scope of the function (defaults to this)
7854       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7855      **/
7856     queryBy : function(fn, scope){
7857         var data = this.snapshot || this.data;
7858         return data.filterBy(fn, scope||this);
7859     },
7860
7861     /**
7862      * Collects unique values for a particular dataIndex from this store.
7863      * @param {String} dataIndex The property to collect
7864      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7865      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7866      * @return {Array} An array of the unique values
7867      **/
7868     collect : function(dataIndex, allowNull, bypassFilter){
7869         var d = (bypassFilter === true && this.snapshot) ?
7870                 this.snapshot.items : this.data.items;
7871         var v, sv, r = [], l = {};
7872         for(var i = 0, len = d.length; i < len; i++){
7873             v = d[i].data[dataIndex];
7874             sv = String(v);
7875             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7876                 l[sv] = true;
7877                 r[r.length] = v;
7878             }
7879         }
7880         return r;
7881     },
7882
7883     /**
7884      * Revert to a view of the Record cache with no filtering applied.
7885      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7886      */
7887     clearFilter : function(suppressEvent){
7888         if(this.snapshot && this.snapshot != this.data){
7889             this.data = this.snapshot;
7890             delete this.snapshot;
7891             if(suppressEvent !== true){
7892                 this.fireEvent("datachanged", this);
7893             }
7894         }
7895     },
7896
7897     // private
7898     afterEdit : function(record){
7899         if(this.modified.indexOf(record) == -1){
7900             this.modified.push(record);
7901         }
7902         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7903     },
7904     
7905     // private
7906     afterReject : function(record){
7907         this.modified.remove(record);
7908         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7909     },
7910
7911     // private
7912     afterCommit : function(record){
7913         this.modified.remove(record);
7914         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7915     },
7916
7917     /**
7918      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7919      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7920      */
7921     commitChanges : function(){
7922         var m = this.modified.slice(0);
7923         this.modified = [];
7924         for(var i = 0, len = m.length; i < len; i++){
7925             m[i].commit();
7926         }
7927     },
7928
7929     /**
7930      * Cancel outstanding changes on all changed records.
7931      */
7932     rejectChanges : function(){
7933         var m = this.modified.slice(0);
7934         this.modified = [];
7935         for(var i = 0, len = m.length; i < len; i++){
7936             m[i].reject();
7937         }
7938     },
7939
7940     onMetaChange : function(meta, rtype, o){
7941         this.recordType = rtype;
7942         this.fields = rtype.prototype.fields;
7943         delete this.snapshot;
7944         this.sortInfo = meta.sortInfo || this.sortInfo;
7945         this.modified = [];
7946         this.fireEvent('metachange', this, this.reader.meta);
7947     },
7948     
7949     moveIndex : function(data, type)
7950     {
7951         var index = this.indexOf(data);
7952         
7953         var newIndex = index + type;
7954         
7955         this.remove(data);
7956         
7957         this.insert(newIndex, data);
7958         
7959     }
7960 });/*
7961  * Based on:
7962  * Ext JS Library 1.1.1
7963  * Copyright(c) 2006-2007, Ext JS, LLC.
7964  *
7965  * Originally Released Under LGPL - original licence link has changed is not relivant.
7966  *
7967  * Fork - LGPL
7968  * <script type="text/javascript">
7969  */
7970
7971 /**
7972  * @class Roo.data.SimpleStore
7973  * @extends Roo.data.Store
7974  * Small helper class to make creating Stores from Array data easier.
7975  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7976  * @cfg {Array} fields An array of field definition objects, or field name strings.
7977  * @cfg {Array} data The multi-dimensional array of data
7978  * @constructor
7979  * @param {Object} config
7980  */
7981 Roo.data.SimpleStore = function(config){
7982     Roo.data.SimpleStore.superclass.constructor.call(this, {
7983         isLocal : true,
7984         reader: new Roo.data.ArrayReader({
7985                 id: config.id
7986             },
7987             Roo.data.Record.create(config.fields)
7988         ),
7989         proxy : new Roo.data.MemoryProxy(config.data)
7990     });
7991     this.load();
7992 };
7993 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7994  * Based on:
7995  * Ext JS Library 1.1.1
7996  * Copyright(c) 2006-2007, Ext JS, LLC.
7997  *
7998  * Originally Released Under LGPL - original licence link has changed is not relivant.
7999  *
8000  * Fork - LGPL
8001  * <script type="text/javascript">
8002  */
8003
8004 /**
8005 /**
8006  * @extends Roo.data.Store
8007  * @class Roo.data.JsonStore
8008  * Small helper class to make creating Stores for JSON data easier. <br/>
8009 <pre><code>
8010 var store = new Roo.data.JsonStore({
8011     url: 'get-images.php',
8012     root: 'images',
8013     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8014 });
8015 </code></pre>
8016  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8017  * JsonReader and HttpProxy (unless inline data is provided).</b>
8018  * @cfg {Array} fields An array of field definition objects, or field name strings.
8019  * @constructor
8020  * @param {Object} config
8021  */
8022 Roo.data.JsonStore = function(c){
8023     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8024         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8025         reader: new Roo.data.JsonReader(c, c.fields)
8026     }));
8027 };
8028 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8029  * Based on:
8030  * Ext JS Library 1.1.1
8031  * Copyright(c) 2006-2007, Ext JS, LLC.
8032  *
8033  * Originally Released Under LGPL - original licence link has changed is not relivant.
8034  *
8035  * Fork - LGPL
8036  * <script type="text/javascript">
8037  */
8038
8039  
8040 Roo.data.Field = function(config){
8041     if(typeof config == "string"){
8042         config = {name: config};
8043     }
8044     Roo.apply(this, config);
8045     
8046     if(!this.type){
8047         this.type = "auto";
8048     }
8049     
8050     var st = Roo.data.SortTypes;
8051     // named sortTypes are supported, here we look them up
8052     if(typeof this.sortType == "string"){
8053         this.sortType = st[this.sortType];
8054     }
8055     
8056     // set default sortType for strings and dates
8057     if(!this.sortType){
8058         switch(this.type){
8059             case "string":
8060                 this.sortType = st.asUCString;
8061                 break;
8062             case "date":
8063                 this.sortType = st.asDate;
8064                 break;
8065             default:
8066                 this.sortType = st.none;
8067         }
8068     }
8069
8070     // define once
8071     var stripRe = /[\$,%]/g;
8072
8073     // prebuilt conversion function for this field, instead of
8074     // switching every time we're reading a value
8075     if(!this.convert){
8076         var cv, dateFormat = this.dateFormat;
8077         switch(this.type){
8078             case "":
8079             case "auto":
8080             case undefined:
8081                 cv = function(v){ return v; };
8082                 break;
8083             case "string":
8084                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8085                 break;
8086             case "int":
8087                 cv = function(v){
8088                     return v !== undefined && v !== null && v !== '' ?
8089                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8090                     };
8091                 break;
8092             case "float":
8093                 cv = function(v){
8094                     return v !== undefined && v !== null && v !== '' ?
8095                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8096                     };
8097                 break;
8098             case "bool":
8099             case "boolean":
8100                 cv = function(v){ return v === true || v === "true" || v == 1; };
8101                 break;
8102             case "date":
8103                 cv = function(v){
8104                     if(!v){
8105                         return '';
8106                     }
8107                     if(v instanceof Date){
8108                         return v;
8109                     }
8110                     if(dateFormat){
8111                         if(dateFormat == "timestamp"){
8112                             return new Date(v*1000);
8113                         }
8114                         return Date.parseDate(v, dateFormat);
8115                     }
8116                     var parsed = Date.parse(v);
8117                     return parsed ? new Date(parsed) : null;
8118                 };
8119              break;
8120             
8121         }
8122         this.convert = cv;
8123     }
8124 };
8125
8126 Roo.data.Field.prototype = {
8127     dateFormat: null,
8128     defaultValue: "",
8129     mapping: null,
8130     sortType : null,
8131     sortDir : "ASC"
8132 };/*
8133  * Based on:
8134  * Ext JS Library 1.1.1
8135  * Copyright(c) 2006-2007, Ext JS, LLC.
8136  *
8137  * Originally Released Under LGPL - original licence link has changed is not relivant.
8138  *
8139  * Fork - LGPL
8140  * <script type="text/javascript">
8141  */
8142  
8143 // Base class for reading structured data from a data source.  This class is intended to be
8144 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8145
8146 /**
8147  * @class Roo.data.DataReader
8148  * Base class for reading structured data from a data source.  This class is intended to be
8149  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8150  */
8151
8152 Roo.data.DataReader = function(meta, recordType){
8153     
8154     this.meta = meta;
8155     
8156     this.recordType = recordType instanceof Array ? 
8157         Roo.data.Record.create(recordType) : recordType;
8158 };
8159
8160 Roo.data.DataReader.prototype = {
8161      /**
8162      * Create an empty record
8163      * @param {Object} data (optional) - overlay some values
8164      * @return {Roo.data.Record} record created.
8165      */
8166     newRow :  function(d) {
8167         var da =  {};
8168         this.recordType.prototype.fields.each(function(c) {
8169             switch( c.type) {
8170                 case 'int' : da[c.name] = 0; break;
8171                 case 'date' : da[c.name] = new Date(); break;
8172                 case 'float' : da[c.name] = 0.0; break;
8173                 case 'boolean' : da[c.name] = false; break;
8174                 default : da[c.name] = ""; break;
8175             }
8176             
8177         });
8178         return new this.recordType(Roo.apply(da, d));
8179     }
8180     
8181 };/*
8182  * Based on:
8183  * Ext JS Library 1.1.1
8184  * Copyright(c) 2006-2007, Ext JS, LLC.
8185  *
8186  * Originally Released Under LGPL - original licence link has changed is not relivant.
8187  *
8188  * Fork - LGPL
8189  * <script type="text/javascript">
8190  */
8191
8192 /**
8193  * @class Roo.data.DataProxy
8194  * @extends Roo.data.Observable
8195  * This class is an abstract base class for implementations which provide retrieval of
8196  * unformatted data objects.<br>
8197  * <p>
8198  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8199  * (of the appropriate type which knows how to parse the data object) to provide a block of
8200  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8201  * <p>
8202  * Custom implementations must implement the load method as described in
8203  * {@link Roo.data.HttpProxy#load}.
8204  */
8205 Roo.data.DataProxy = function(){
8206     this.addEvents({
8207         /**
8208          * @event beforeload
8209          * Fires before a network request is made to retrieve a data object.
8210          * @param {Object} This DataProxy object.
8211          * @param {Object} params The params parameter to the load function.
8212          */
8213         beforeload : true,
8214         /**
8215          * @event load
8216          * Fires before the load method's callback is called.
8217          * @param {Object} This DataProxy object.
8218          * @param {Object} o The data object.
8219          * @param {Object} arg The callback argument object passed to the load function.
8220          */
8221         load : true,
8222         /**
8223          * @event loadexception
8224          * Fires if an Exception occurs during data retrieval.
8225          * @param {Object} This DataProxy object.
8226          * @param {Object} o The data object.
8227          * @param {Object} arg The callback argument object passed to the load function.
8228          * @param {Object} e The Exception.
8229          */
8230         loadexception : true
8231     });
8232     Roo.data.DataProxy.superclass.constructor.call(this);
8233 };
8234
8235 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8236
8237     /**
8238      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8239      */
8240 /*
8241  * Based on:
8242  * Ext JS Library 1.1.1
8243  * Copyright(c) 2006-2007, Ext JS, LLC.
8244  *
8245  * Originally Released Under LGPL - original licence link has changed is not relivant.
8246  *
8247  * Fork - LGPL
8248  * <script type="text/javascript">
8249  */
8250 /**
8251  * @class Roo.data.MemoryProxy
8252  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8253  * to the Reader when its load method is called.
8254  * @constructor
8255  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8256  */
8257 Roo.data.MemoryProxy = function(data){
8258     if (data.data) {
8259         data = data.data;
8260     }
8261     Roo.data.MemoryProxy.superclass.constructor.call(this);
8262     this.data = data;
8263 };
8264
8265 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8266     /**
8267      * Load data from the requested source (in this case an in-memory
8268      * data object passed to the constructor), read the data object into
8269      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8270      * process that block using the passed callback.
8271      * @param {Object} params This parameter is not used by the MemoryProxy class.
8272      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8273      * object into a block of Roo.data.Records.
8274      * @param {Function} callback The function into which to pass the block of Roo.data.records.
8275      * The function must be passed <ul>
8276      * <li>The Record block object</li>
8277      * <li>The "arg" argument from the load function</li>
8278      * <li>A boolean success indicator</li>
8279      * </ul>
8280      * @param {Object} scope The scope in which to call the callback
8281      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8282      */
8283     load : function(params, reader, callback, scope, arg){
8284         params = params || {};
8285         var result;
8286         try {
8287             result = reader.readRecords(this.data);
8288         }catch(e){
8289             this.fireEvent("loadexception", this, arg, null, e);
8290             callback.call(scope, null, arg, false);
8291             return;
8292         }
8293         callback.call(scope, result, arg, true);
8294     },
8295     
8296     // private
8297     update : function(params, records){
8298         
8299     }
8300 });/*
8301  * Based on:
8302  * Ext JS Library 1.1.1
8303  * Copyright(c) 2006-2007, Ext JS, LLC.
8304  *
8305  * Originally Released Under LGPL - original licence link has changed is not relivant.
8306  *
8307  * Fork - LGPL
8308  * <script type="text/javascript">
8309  */
8310 /**
8311  * @class Roo.data.HttpProxy
8312  * @extends Roo.data.DataProxy
8313  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8314  * configured to reference a certain URL.<br><br>
8315  * <p>
8316  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8317  * from which the running page was served.<br><br>
8318  * <p>
8319  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8320  * <p>
8321  * Be aware that to enable the browser to parse an XML document, the server must set
8322  * the Content-Type header in the HTTP response to "text/xml".
8323  * @constructor
8324  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8325  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
8326  * will be used to make the request.
8327  */
8328 Roo.data.HttpProxy = function(conn){
8329     Roo.data.HttpProxy.superclass.constructor.call(this);
8330     // is conn a conn config or a real conn?
8331     this.conn = conn;
8332     this.useAjax = !conn || !conn.events;
8333   
8334 };
8335
8336 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8337     // thse are take from connection...
8338     
8339     /**
8340      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8341      */
8342     /**
8343      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8344      * extra parameters to each request made by this object. (defaults to undefined)
8345      */
8346     /**
8347      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8348      *  to each request made by this object. (defaults to undefined)
8349      */
8350     /**
8351      * @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)
8352      */
8353     /**
8354      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8355      */
8356      /**
8357      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8358      * @type Boolean
8359      */
8360   
8361
8362     /**
8363      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8364      * @type Boolean
8365      */
8366     /**
8367      * Return the {@link Roo.data.Connection} object being used by this Proxy.
8368      * @return {Connection} The Connection object. This object may be used to subscribe to events on
8369      * a finer-grained basis than the DataProxy events.
8370      */
8371     getConnection : function(){
8372         return this.useAjax ? Roo.Ajax : this.conn;
8373     },
8374
8375     /**
8376      * Load data from the configured {@link Roo.data.Connection}, read the data object into
8377      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8378      * process that block using the passed callback.
8379      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8380      * for the request to the remote server.
8381      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8382      * object into a block of Roo.data.Records.
8383      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8384      * The function must be passed <ul>
8385      * <li>The Record block object</li>
8386      * <li>The "arg" argument from the load function</li>
8387      * <li>A boolean success indicator</li>
8388      * </ul>
8389      * @param {Object} scope The scope in which to call the callback
8390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8391      */
8392     load : function(params, reader, callback, scope, arg){
8393         if(this.fireEvent("beforeload", this, params) !== false){
8394             var  o = {
8395                 params : params || {},
8396                 request: {
8397                     callback : callback,
8398                     scope : scope,
8399                     arg : arg
8400                 },
8401                 reader: reader,
8402                 callback : this.loadResponse,
8403                 scope: this
8404             };
8405             if(this.useAjax){
8406                 Roo.applyIf(o, this.conn);
8407                 if(this.activeRequest){
8408                     Roo.Ajax.abort(this.activeRequest);
8409                 }
8410                 this.activeRequest = Roo.Ajax.request(o);
8411             }else{
8412                 this.conn.request(o);
8413             }
8414         }else{
8415             callback.call(scope||this, null, arg, false);
8416         }
8417     },
8418
8419     // private
8420     loadResponse : function(o, success, response){
8421         delete this.activeRequest;
8422         if(!success){
8423             this.fireEvent("loadexception", this, o, response);
8424             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8425             return;
8426         }
8427         var result;
8428         try {
8429             result = o.reader.read(response);
8430         }catch(e){
8431             this.fireEvent("loadexception", this, o, response, e);
8432             o.request.callback.call(o.request.scope, null, o.request.arg, false);
8433             return;
8434         }
8435         
8436         this.fireEvent("load", this, o, o.request.arg);
8437         o.request.callback.call(o.request.scope, result, o.request.arg, true);
8438     },
8439
8440     // private
8441     update : function(dataSet){
8442
8443     },
8444
8445     // private
8446     updateResponse : function(dataSet){
8447
8448     }
8449 });/*
8450  * Based on:
8451  * Ext JS Library 1.1.1
8452  * Copyright(c) 2006-2007, Ext JS, LLC.
8453  *
8454  * Originally Released Under LGPL - original licence link has changed is not relivant.
8455  *
8456  * Fork - LGPL
8457  * <script type="text/javascript">
8458  */
8459
8460 /**
8461  * @class Roo.data.ScriptTagProxy
8462  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8463  * other than the originating domain of the running page.<br><br>
8464  * <p>
8465  * <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
8466  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8467  * <p>
8468  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8469  * source code that is used as the source inside a &lt;script> tag.<br><br>
8470  * <p>
8471  * In order for the browser to process the returned data, the server must wrap the data object
8472  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8473  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8474  * depending on whether the callback name was passed:
8475  * <p>
8476  * <pre><code>
8477 boolean scriptTag = false;
8478 String cb = request.getParameter("callback");
8479 if (cb != null) {
8480     scriptTag = true;
8481     response.setContentType("text/javascript");
8482 } else {
8483     response.setContentType("application/x-json");
8484 }
8485 Writer out = response.getWriter();
8486 if (scriptTag) {
8487     out.write(cb + "(");
8488 }
8489 out.print(dataBlock.toJsonString());
8490 if (scriptTag) {
8491     out.write(");");
8492 }
8493 </pre></code>
8494  *
8495  * @constructor
8496  * @param {Object} config A configuration object.
8497  */
8498 Roo.data.ScriptTagProxy = function(config){
8499     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8500     Roo.apply(this, config);
8501     this.head = document.getElementsByTagName("head")[0];
8502 };
8503
8504 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8505
8506 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8507     /**
8508      * @cfg {String} url The URL from which to request the data object.
8509      */
8510     /**
8511      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8512      */
8513     timeout : 30000,
8514     /**
8515      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8516      * the server the name of the callback function set up by the load call to process the returned data object.
8517      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8518      * javascript output which calls this named function passing the data object as its only parameter.
8519      */
8520     callbackParam : "callback",
8521     /**
8522      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8523      * name to the request.
8524      */
8525     nocache : true,
8526
8527     /**
8528      * Load data from the configured URL, read the data object into
8529      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8530      * process that block using the passed callback.
8531      * @param {Object} params An object containing properties which are to be used as HTTP parameters
8532      * for the request to the remote server.
8533      * @param {Roo.data.DataReader} reader The Reader object which converts the data
8534      * object into a block of Roo.data.Records.
8535      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8536      * The function must be passed <ul>
8537      * <li>The Record block object</li>
8538      * <li>The "arg" argument from the load function</li>
8539      * <li>A boolean success indicator</li>
8540      * </ul>
8541      * @param {Object} scope The scope in which to call the callback
8542      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8543      */
8544     load : function(params, reader, callback, scope, arg){
8545         if(this.fireEvent("beforeload", this, params) !== false){
8546
8547             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8548
8549             var url = this.url;
8550             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8551             if(this.nocache){
8552                 url += "&_dc=" + (new Date().getTime());
8553             }
8554             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8555             var trans = {
8556                 id : transId,
8557                 cb : "stcCallback"+transId,
8558                 scriptId : "stcScript"+transId,
8559                 params : params,
8560                 arg : arg,
8561                 url : url,
8562                 callback : callback,
8563                 scope : scope,
8564                 reader : reader
8565             };
8566             var conn = this;
8567
8568             window[trans.cb] = function(o){
8569                 conn.handleResponse(o, trans);
8570             };
8571
8572             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8573
8574             if(this.autoAbort !== false){
8575                 this.abort();
8576             }
8577
8578             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8579
8580             var script = document.createElement("script");
8581             script.setAttribute("src", url);
8582             script.setAttribute("type", "text/javascript");
8583             script.setAttribute("id", trans.scriptId);
8584             this.head.appendChild(script);
8585
8586             this.trans = trans;
8587         }else{
8588             callback.call(scope||this, null, arg, false);
8589         }
8590     },
8591
8592     // private
8593     isLoading : function(){
8594         return this.trans ? true : false;
8595     },
8596
8597     /**
8598      * Abort the current server request.
8599      */
8600     abort : function(){
8601         if(this.isLoading()){
8602             this.destroyTrans(this.trans);
8603         }
8604     },
8605
8606     // private
8607     destroyTrans : function(trans, isLoaded){
8608         this.head.removeChild(document.getElementById(trans.scriptId));
8609         clearTimeout(trans.timeoutId);
8610         if(isLoaded){
8611             window[trans.cb] = undefined;
8612             try{
8613                 delete window[trans.cb];
8614             }catch(e){}
8615         }else{
8616             // if hasn't been loaded, wait for load to remove it to prevent script error
8617             window[trans.cb] = function(){
8618                 window[trans.cb] = undefined;
8619                 try{
8620                     delete window[trans.cb];
8621                 }catch(e){}
8622             };
8623         }
8624     },
8625
8626     // private
8627     handleResponse : function(o, trans){
8628         this.trans = false;
8629         this.destroyTrans(trans, true);
8630         var result;
8631         try {
8632             result = trans.reader.readRecords(o);
8633         }catch(e){
8634             this.fireEvent("loadexception", this, o, trans.arg, e);
8635             trans.callback.call(trans.scope||window, null, trans.arg, false);
8636             return;
8637         }
8638         this.fireEvent("load", this, o, trans.arg);
8639         trans.callback.call(trans.scope||window, result, trans.arg, true);
8640     },
8641
8642     // private
8643     handleFailure : function(trans){
8644         this.trans = false;
8645         this.destroyTrans(trans, false);
8646         this.fireEvent("loadexception", this, null, trans.arg);
8647         trans.callback.call(trans.scope||window, null, trans.arg, false);
8648     }
8649 });/*
8650  * Based on:
8651  * Ext JS Library 1.1.1
8652  * Copyright(c) 2006-2007, Ext JS, LLC.
8653  *
8654  * Originally Released Under LGPL - original licence link has changed is not relivant.
8655  *
8656  * Fork - LGPL
8657  * <script type="text/javascript">
8658  */
8659
8660 /**
8661  * @class Roo.data.JsonReader
8662  * @extends Roo.data.DataReader
8663  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8664  * based on mappings in a provided Roo.data.Record constructor.
8665  * 
8666  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8667  * in the reply previously. 
8668  * 
8669  * <p>
8670  * Example code:
8671  * <pre><code>
8672 var RecordDef = Roo.data.Record.create([
8673     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
8674     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
8675 ]);
8676 var myReader = new Roo.data.JsonReader({
8677     totalProperty: "results",    // The property which contains the total dataset size (optional)
8678     root: "rows",                // The property which contains an Array of row objects
8679     id: "id"                     // The property within each row object that provides an ID for the record (optional)
8680 }, RecordDef);
8681 </code></pre>
8682  * <p>
8683  * This would consume a JSON file like this:
8684  * <pre><code>
8685 { 'results': 2, 'rows': [
8686     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8687     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8688 }
8689 </code></pre>
8690  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8691  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8692  * paged from the remote server.
8693  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8694  * @cfg {String} root name of the property which contains the Array of row objects.
8695  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8696  * @constructor
8697  * Create a new JsonReader
8698  * @param {Object} meta Metadata configuration options
8699  * @param {Object} recordType Either an Array of field definition objects,
8700  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8701  */
8702 Roo.data.JsonReader = function(meta, recordType){
8703     
8704     meta = meta || {};
8705     // set some defaults:
8706     Roo.applyIf(meta, {
8707         totalProperty: 'total',
8708         successProperty : 'success',
8709         root : 'data',
8710         id : 'id'
8711     });
8712     
8713     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8714 };
8715 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8716     
8717     /**
8718      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
8719      * Used by Store query builder to append _requestMeta to params.
8720      * 
8721      */
8722     metaFromRemote : false,
8723     /**
8724      * This method is only used by a DataProxy which has retrieved data from a remote server.
8725      * @param {Object} response The XHR object which contains the JSON data in its responseText.
8726      * @return {Object} data A data block which is used by an Roo.data.Store object as
8727      * a cache of Roo.data.Records.
8728      */
8729     read : function(response){
8730         var json = response.responseText;
8731        
8732         var o = /* eval:var:o */ eval("("+json+")");
8733         if(!o) {
8734             throw {message: "JsonReader.read: Json object not found"};
8735         }
8736         
8737         if(o.metaData){
8738             
8739             delete this.ef;
8740             this.metaFromRemote = true;
8741             this.meta = o.metaData;
8742             this.recordType = Roo.data.Record.create(o.metaData.fields);
8743             this.onMetaChange(this.meta, this.recordType, o);
8744         }
8745         return this.readRecords(o);
8746     },
8747
8748     // private function a store will implement
8749     onMetaChange : function(meta, recordType, o){
8750
8751     },
8752
8753     /**
8754          * @ignore
8755          */
8756     simpleAccess: function(obj, subsc) {
8757         return obj[subsc];
8758     },
8759
8760         /**
8761          * @ignore
8762          */
8763     getJsonAccessor: function(){
8764         var re = /[\[\.]/;
8765         return function(expr) {
8766             try {
8767                 return(re.test(expr))
8768                     ? new Function("obj", "return obj." + expr)
8769                     : function(obj){
8770                         return obj[expr];
8771                     };
8772             } catch(e){}
8773             return Roo.emptyFn;
8774         };
8775     }(),
8776
8777     /**
8778      * Create a data block containing Roo.data.Records from an XML document.
8779      * @param {Object} o An object which contains an Array of row objects in the property specified
8780      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8781      * which contains the total size of the dataset.
8782      * @return {Object} data A data block which is used by an Roo.data.Store object as
8783      * a cache of Roo.data.Records.
8784      */
8785     readRecords : function(o){
8786         /**
8787          * After any data loads, the raw JSON data is available for further custom processing.
8788          * @type Object
8789          */
8790         this.o = o;
8791         var s = this.meta, Record = this.recordType,
8792             f = Record.prototype.fields, fi = f.items, fl = f.length;
8793
8794 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
8795         if (!this.ef) {
8796             if(s.totalProperty) {
8797                     this.getTotal = this.getJsonAccessor(s.totalProperty);
8798                 }
8799                 if(s.successProperty) {
8800                     this.getSuccess = this.getJsonAccessor(s.successProperty);
8801                 }
8802                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8803                 if (s.id) {
8804                         var g = this.getJsonAccessor(s.id);
8805                         this.getId = function(rec) {
8806                                 var r = g(rec);
8807                                 return (r === undefined || r === "") ? null : r;
8808                         };
8809                 } else {
8810                         this.getId = function(){return null;};
8811                 }
8812             this.ef = [];
8813             for(var jj = 0; jj < fl; jj++){
8814                 f = fi[jj];
8815                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8816                 this.ef[jj] = this.getJsonAccessor(map);
8817             }
8818         }
8819
8820         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8821         if(s.totalProperty){
8822             var vt = parseInt(this.getTotal(o), 10);
8823             if(!isNaN(vt)){
8824                 totalRecords = vt;
8825             }
8826         }
8827         if(s.successProperty){
8828             var vs = this.getSuccess(o);
8829             if(vs === false || vs === 'false'){
8830                 success = false;
8831             }
8832         }
8833         var records = [];
8834             for(var i = 0; i < c; i++){
8835                     var n = root[i];
8836                 var values = {};
8837                 var id = this.getId(n);
8838                 for(var j = 0; j < fl; j++){
8839                     f = fi[j];
8840                 var v = this.ef[j](n);
8841                 if (!f.convert) {
8842                     Roo.log('missing convert for ' + f.name);
8843                     Roo.log(f);
8844                     continue;
8845                 }
8846                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8847                 }
8848                 var record = new Record(values, id);
8849                 record.json = n;
8850                 records[i] = record;
8851             }
8852             return {
8853             raw : o,
8854                 success : success,
8855                 records : records,
8856                 totalRecords : totalRecords
8857             };
8858     }
8859 });/*
8860  * Based on:
8861  * Ext JS Library 1.1.1
8862  * Copyright(c) 2006-2007, Ext JS, LLC.
8863  *
8864  * Originally Released Under LGPL - original licence link has changed is not relivant.
8865  *
8866  * Fork - LGPL
8867  * <script type="text/javascript">
8868  */
8869
8870 /**
8871  * @class Roo.data.ArrayReader
8872  * @extends Roo.data.DataReader
8873  * Data reader class to create an Array of Roo.data.Record objects from an Array.
8874  * Each element of that Array represents a row of data fields. The
8875  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8876  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8877  * <p>
8878  * Example code:.
8879  * <pre><code>
8880 var RecordDef = Roo.data.Record.create([
8881     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
8882     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
8883 ]);
8884 var myReader = new Roo.data.ArrayReader({
8885     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
8886 }, RecordDef);
8887 </code></pre>
8888  * <p>
8889  * This would consume an Array like this:
8890  * <pre><code>
8891 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8892   </code></pre>
8893  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8894  * @constructor
8895  * Create a new JsonReader
8896  * @param {Object} meta Metadata configuration options.
8897  * @param {Object} recordType Either an Array of field definition objects
8898  * as specified to {@link Roo.data.Record#create},
8899  * or an {@link Roo.data.Record} object
8900  * created using {@link Roo.data.Record#create}.
8901  */
8902 Roo.data.ArrayReader = function(meta, recordType){
8903     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8904 };
8905
8906 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8907     /**
8908      * Create a data block containing Roo.data.Records from an XML document.
8909      * @param {Object} o An Array of row objects which represents the dataset.
8910      * @return {Object} data A data block which is used by an Roo.data.Store object as
8911      * a cache of Roo.data.Records.
8912      */
8913     readRecords : function(o){
8914         var sid = this.meta ? this.meta.id : null;
8915         var recordType = this.recordType, fields = recordType.prototype.fields;
8916         var records = [];
8917         var root = o;
8918             for(var i = 0; i < root.length; i++){
8919                     var n = root[i];
8920                 var values = {};
8921                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8922                 for(var j = 0, jlen = fields.length; j < jlen; j++){
8923                 var f = fields.items[j];
8924                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8925                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8926                 v = f.convert(v);
8927                 values[f.name] = v;
8928             }
8929                 var record = new recordType(values, id);
8930                 record.json = n;
8931                 records[records.length] = record;
8932             }
8933             return {
8934                 records : records,
8935                 totalRecords : records.length
8936             };
8937     }
8938 });/*
8939  * - LGPL
8940  * * 
8941  */
8942
8943 /**
8944  * @class Roo.bootstrap.ComboBox
8945  * @extends Roo.bootstrap.TriggerField
8946  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8947  * @cfg {Boolean} append (true|false) default false
8948  * @constructor
8949  * Create a new ComboBox.
8950  * @param {Object} config Configuration options
8951  */
8952 Roo.bootstrap.ComboBox = function(config){
8953     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8954     this.addEvents({
8955         /**
8956          * @event expand
8957          * Fires when the dropdown list is expanded
8958              * @param {Roo.bootstrap.ComboBox} combo This combo box
8959              */
8960         'expand' : true,
8961         /**
8962          * @event collapse
8963          * Fires when the dropdown list is collapsed
8964              * @param {Roo.bootstrap.ComboBox} combo This combo box
8965              */
8966         'collapse' : true,
8967         /**
8968          * @event beforeselect
8969          * Fires before a list item is selected. Return false to cancel the selection.
8970              * @param {Roo.bootstrap.ComboBox} combo This combo box
8971              * @param {Roo.data.Record} record The data record returned from the underlying store
8972              * @param {Number} index The index of the selected item in the dropdown list
8973              */
8974         'beforeselect' : true,
8975         /**
8976          * @event select
8977          * Fires when a list item is selected
8978              * @param {Roo.bootstrap.ComboBox} combo This combo box
8979              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8980              * @param {Number} index The index of the selected item in the dropdown list
8981              */
8982         'select' : true,
8983         /**
8984          * @event beforequery
8985          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8986          * The event object passed has these properties:
8987              * @param {Roo.bootstrap.ComboBox} combo This combo box
8988              * @param {String} query The query
8989              * @param {Boolean} forceAll true to force "all" query
8990              * @param {Boolean} cancel true to cancel the query
8991              * @param {Object} e The query event object
8992              */
8993         'beforequery': true,
8994          /**
8995          * @event add
8996          * Fires when the 'add' icon is pressed (add a listener to enable add button)
8997              * @param {Roo.bootstrap.ComboBox} combo This combo box
8998              */
8999         'add' : true,
9000         /**
9001          * @event edit
9002          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9003              * @param {Roo.bootstrap.ComboBox} combo This combo box
9004              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9005              */
9006         'edit' : true,
9007         /**
9008          * @event remove
9009          * Fires when the remove value from the combobox array
9010              * @param {Roo.bootstrap.ComboBox} combo This combo box
9011              */
9012         'remove' : true
9013         
9014     });
9015     
9016     
9017     this.selectedIndex = -1;
9018     if(this.mode == 'local'){
9019         if(config.queryDelay === undefined){
9020             this.queryDelay = 10;
9021         }
9022         if(config.minChars === undefined){
9023             this.minChars = 0;
9024         }
9025     }
9026 };
9027
9028 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9029      
9030     /**
9031      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9032      * rendering into an Roo.Editor, defaults to false)
9033      */
9034     /**
9035      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9036      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9037      */
9038     /**
9039      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9040      */
9041     /**
9042      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9043      * the dropdown list (defaults to undefined, with no header element)
9044      */
9045
9046      /**
9047      * @cfg {String/Roo.Template} tpl The template to use to render the output
9048      */
9049      
9050      /**
9051      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9052      */
9053     listWidth: undefined,
9054     /**
9055      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9056      * mode = 'remote' or 'text' if mode = 'local')
9057      */
9058     displayField: undefined,
9059     /**
9060      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9061      * mode = 'remote' or 'value' if mode = 'local'). 
9062      * Note: use of a valueField requires the user make a selection
9063      * in order for a value to be mapped.
9064      */
9065     valueField: undefined,
9066     
9067     
9068     /**
9069      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9070      * field's data value (defaults to the underlying DOM element's name)
9071      */
9072     hiddenName: undefined,
9073     /**
9074      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9075      */
9076     listClass: '',
9077     /**
9078      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9079      */
9080     selectedClass: 'active',
9081     
9082     /**
9083      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9084      */
9085     shadow:'sides',
9086     /**
9087      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9088      * anchor positions (defaults to 'tl-bl')
9089      */
9090     listAlign: 'tl-bl?',
9091     /**
9092      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9093      */
9094     maxHeight: 300,
9095     /**
9096      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9097      * query specified by the allQuery config option (defaults to 'query')
9098      */
9099     triggerAction: 'query',
9100     /**
9101      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9102      * (defaults to 4, does not apply if editable = false)
9103      */
9104     minChars : 4,
9105     /**
9106      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9107      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9108      */
9109     typeAhead: false,
9110     /**
9111      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9112      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9113      */
9114     queryDelay: 500,
9115     /**
9116      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9117      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9118      */
9119     pageSize: 0,
9120     /**
9121      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9122      * when editable = true (defaults to false)
9123      */
9124     selectOnFocus:false,
9125     /**
9126      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9127      */
9128     queryParam: 'query',
9129     /**
9130      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9131      * when mode = 'remote' (defaults to 'Loading...')
9132      */
9133     loadingText: 'Loading...',
9134     /**
9135      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9136      */
9137     resizable: false,
9138     /**
9139      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9140      */
9141     handleHeight : 8,
9142     /**
9143      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9144      * traditional select (defaults to true)
9145      */
9146     editable: true,
9147     /**
9148      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9149      */
9150     allQuery: '',
9151     /**
9152      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9153      */
9154     mode: 'remote',
9155     /**
9156      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9157      * listWidth has a higher value)
9158      */
9159     minListWidth : 70,
9160     /**
9161      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9162      * allow the user to set arbitrary text into the field (defaults to false)
9163      */
9164     forceSelection:false,
9165     /**
9166      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9167      * if typeAhead = true (defaults to 250)
9168      */
9169     typeAheadDelay : 250,
9170     /**
9171      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9172      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9173      */
9174     valueNotFoundText : undefined,
9175     /**
9176      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9177      */
9178     blockFocus : false,
9179     
9180     /**
9181      * @cfg {Boolean} disableClear Disable showing of clear button.
9182      */
9183     disableClear : false,
9184     /**
9185      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9186      */
9187     alwaysQuery : false,
9188     
9189     /**
9190      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9191      */
9192     multiple : false,
9193     
9194     //private
9195     addicon : false,
9196     editicon: false,
9197     
9198     page: 0,
9199     hasQuery: false,
9200     append: false,
9201     loadNext: false,
9202     item: [],
9203     
9204     // element that contains real text value.. (when hidden is used..)
9205      
9206     // private
9207     initEvents: function(){
9208         
9209         if (!this.store) {
9210             throw "can not find store for combo";
9211         }
9212         this.store = Roo.factory(this.store, Roo.data);
9213         
9214         
9215         
9216         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9217         
9218         
9219         if(this.hiddenName){
9220             
9221             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9222             
9223             this.hiddenField.dom.value =
9224                 this.hiddenValue !== undefined ? this.hiddenValue :
9225                 this.value !== undefined ? this.value : '';
9226
9227             // prevent input submission
9228             this.el.dom.removeAttribute('name');
9229             this.hiddenField.dom.setAttribute('name', this.hiddenName);
9230              
9231              
9232         }
9233         //if(Roo.isGecko){
9234         //    this.el.dom.setAttribute('autocomplete', 'off');
9235         //}
9236
9237         var cls = 'x-combo-list';
9238         this.list = this.el.select('ul.dropdown-menu',true).first();
9239
9240         //this.list = new Roo.Layer({
9241         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9242         //});
9243         
9244         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9245         this.list.setWidth(lw);
9246         
9247         this.list.on('mouseover', this.onViewOver, this);
9248         this.list.on('mousemove', this.onViewMove, this);
9249         
9250         this.list.on('scroll', this.onViewScroll, this);
9251         
9252         /*
9253         this.list.swallowEvent('mousewheel');
9254         this.assetHeight = 0;
9255
9256         if(this.title){
9257             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9258             this.assetHeight += this.header.getHeight();
9259         }
9260
9261         this.innerList = this.list.createChild({cls:cls+'-inner'});
9262         this.innerList.on('mouseover', this.onViewOver, this);
9263         this.innerList.on('mousemove', this.onViewMove, this);
9264         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9265         
9266         if(this.allowBlank && !this.pageSize && !this.disableClear){
9267             this.footer = this.list.createChild({cls:cls+'-ft'});
9268             this.pageTb = new Roo.Toolbar(this.footer);
9269            
9270         }
9271         if(this.pageSize){
9272             this.footer = this.list.createChild({cls:cls+'-ft'});
9273             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9274                     {pageSize: this.pageSize});
9275             
9276         }
9277         
9278         if (this.pageTb && this.allowBlank && !this.disableClear) {
9279             var _this = this;
9280             this.pageTb.add(new Roo.Toolbar.Fill(), {
9281                 cls: 'x-btn-icon x-btn-clear',
9282                 text: '&#160;',
9283                 handler: function()
9284                 {
9285                     _this.collapse();
9286                     _this.clearValue();
9287                     _this.onSelect(false, -1);
9288                 }
9289             });
9290         }
9291         if (this.footer) {
9292             this.assetHeight += this.footer.getHeight();
9293         }
9294         */
9295             
9296         if(!this.tpl){
9297             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9298         }
9299
9300         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9301             singleSelect:true, store: this.store, selectedClass: this.selectedClass
9302         });
9303         //this.view.wrapEl.setDisplayed(false);
9304         this.view.on('click', this.onViewClick, this);
9305         
9306         
9307         
9308         this.store.on('beforeload', this.onBeforeLoad, this);
9309         this.store.on('load', this.onLoad, this);
9310         this.store.on('loadexception', this.onLoadException, this);
9311         /*
9312         if(this.resizable){
9313             this.resizer = new Roo.Resizable(this.list,  {
9314                pinned:true, handles:'se'
9315             });
9316             this.resizer.on('resize', function(r, w, h){
9317                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9318                 this.listWidth = w;
9319                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9320                 this.restrictHeight();
9321             }, this);
9322             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9323         }
9324         */
9325         if(!this.editable){
9326             this.editable = true;
9327             this.setEditable(false);
9328         }
9329         
9330         /*
9331         
9332         if (typeof(this.events.add.listeners) != 'undefined') {
9333             
9334             this.addicon = this.wrap.createChild(
9335                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
9336        
9337             this.addicon.on('click', function(e) {
9338                 this.fireEvent('add', this);
9339             }, this);
9340         }
9341         if (typeof(this.events.edit.listeners) != 'undefined') {
9342             
9343             this.editicon = this.wrap.createChild(
9344                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
9345             if (this.addicon) {
9346                 this.editicon.setStyle('margin-left', '40px');
9347             }
9348             this.editicon.on('click', function(e) {
9349                 
9350                 // we fire even  if inothing is selected..
9351                 this.fireEvent('edit', this, this.lastData );
9352                 
9353             }, this);
9354         }
9355         */
9356         
9357         this.keyNav = new Roo.KeyNav(this.inputEl(), {
9358             "up" : function(e){
9359                 this.inKeyMode = true;
9360                 this.selectPrev();
9361             },
9362
9363             "down" : function(e){
9364                 if(!this.isExpanded()){
9365                     this.onTriggerClick();
9366                 }else{
9367                     this.inKeyMode = true;
9368                     this.selectNext();
9369                 }
9370             },
9371
9372             "enter" : function(e){
9373                 this.onViewClick();
9374                 //return true;
9375             },
9376
9377             "esc" : function(e){
9378                 this.collapse();
9379             },
9380
9381             "tab" : function(e){
9382                 this.collapse();
9383                 
9384                 if(this.fireEvent("specialkey", this, e)){
9385                     this.onViewClick(false);
9386                 }
9387                 
9388                 return true;
9389             },
9390
9391             scope : this,
9392
9393             doRelay : function(foo, bar, hname){
9394                 if(hname == 'down' || this.scope.isExpanded()){
9395                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9396                 }
9397                 return true;
9398             },
9399
9400             forceKeyDown: true
9401         });
9402         
9403         
9404         this.queryDelay = Math.max(this.queryDelay || 10,
9405                 this.mode == 'local' ? 10 : 250);
9406         
9407         
9408         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9409         
9410         if(this.typeAhead){
9411             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9412         }
9413         if(this.editable !== false){
9414             this.inputEl().on("keyup", this.onKeyUp, this);
9415         }
9416         if(this.forceSelection){
9417             this.inputEl().on('blur', this.doForce, this);
9418         }
9419         
9420         if(this.multiple){
9421             this.choices = this.el.select('ul.select2-choices', true).first();
9422             this.searchField = this.el.select('ul li.select2-search-field', true).first();
9423         }
9424     },
9425
9426     onDestroy : function(){
9427         if(this.view){
9428             this.view.setStore(null);
9429             this.view.el.removeAllListeners();
9430             this.view.el.remove();
9431             this.view.purgeListeners();
9432         }
9433         if(this.list){
9434             this.list.dom.innerHTML  = '';
9435         }
9436         if(this.store){
9437             this.store.un('beforeload', this.onBeforeLoad, this);
9438             this.store.un('load', this.onLoad, this);
9439             this.store.un('loadexception', this.onLoadException, this);
9440         }
9441         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9442     },
9443
9444     // private
9445     fireKey : function(e){
9446         if(e.isNavKeyPress() && !this.list.isVisible()){
9447             this.fireEvent("specialkey", this, e);
9448         }
9449     },
9450
9451     // private
9452     onResize: function(w, h){
9453 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9454 //        
9455 //        if(typeof w != 'number'){
9456 //            // we do not handle it!?!?
9457 //            return;
9458 //        }
9459 //        var tw = this.trigger.getWidth();
9460 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
9461 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
9462 //        var x = w - tw;
9463 //        this.inputEl().setWidth( this.adjustWidth('input', x));
9464 //            
9465 //        //this.trigger.setStyle('left', x+'px');
9466 //        
9467 //        if(this.list && this.listWidth === undefined){
9468 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9469 //            this.list.setWidth(lw);
9470 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9471 //        }
9472         
9473     
9474         
9475     },
9476
9477     /**
9478      * Allow or prevent the user from directly editing the field text.  If false is passed,
9479      * the user will only be able to select from the items defined in the dropdown list.  This method
9480      * is the runtime equivalent of setting the 'editable' config option at config time.
9481      * @param {Boolean} value True to allow the user to directly edit the field text
9482      */
9483     setEditable : function(value){
9484         if(value == this.editable){
9485             return;
9486         }
9487         this.editable = value;
9488         if(!value){
9489             this.inputEl().dom.setAttribute('readOnly', true);
9490             this.inputEl().on('mousedown', this.onTriggerClick,  this);
9491             this.inputEl().addClass('x-combo-noedit');
9492         }else{
9493             this.inputEl().dom.setAttribute('readOnly', false);
9494             this.inputEl().un('mousedown', this.onTriggerClick,  this);
9495             this.inputEl().removeClass('x-combo-noedit');
9496         }
9497     },
9498
9499     // private
9500     
9501     onBeforeLoad : function(combo,opts){
9502         if(!this.hasFocus){
9503             return;
9504         }
9505          if (!opts.add) {
9506             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9507          }
9508         this.restrictHeight();
9509         this.selectedIndex = -1;
9510     },
9511
9512     // private
9513     onLoad : function(){
9514         
9515         this.hasQuery = false;
9516         
9517         if(!this.hasFocus){
9518             return;
9519         }
9520         
9521         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9522             this.loading.hide();
9523         }
9524         
9525         if(this.store.getCount() > 0){
9526             this.expand();
9527             this.restrictHeight();
9528             if(this.lastQuery == this.allQuery){
9529                 if(this.editable){
9530                     this.inputEl().dom.select();
9531                 }
9532                 if(!this.selectByValue(this.value, true)){
9533                     this.select(0, true);
9534                 }
9535             }else{
9536                 this.selectNext();
9537                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9538                     this.taTask.delay(this.typeAheadDelay);
9539                 }
9540             }
9541         }else{
9542             this.onEmptyResults();
9543         }
9544         
9545         //this.el.focus();
9546     },
9547     // private
9548     onLoadException : function()
9549     {
9550         this.hasQuery = false;
9551         
9552         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9553             this.loading.hide();
9554         }
9555         
9556         this.collapse();
9557         Roo.log(this.store.reader.jsonData);
9558         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9559             // fixme
9560             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9561         }
9562         
9563         
9564     },
9565     // private
9566     onTypeAhead : function(){
9567         if(this.store.getCount() > 0){
9568             var r = this.store.getAt(0);
9569             var newValue = r.data[this.displayField];
9570             var len = newValue.length;
9571             var selStart = this.getRawValue().length;
9572             
9573             if(selStart != len){
9574                 this.setRawValue(newValue);
9575                 this.selectText(selStart, newValue.length);
9576             }
9577         }
9578     },
9579
9580     // private
9581     onSelect : function(record, index){
9582         
9583         if(this.fireEvent('beforeselect', this, record, index) !== false){
9584         
9585             this.setFromData(index > -1 ? record.data : false);
9586             
9587             this.collapse();
9588             this.fireEvent('select', this, record, index);
9589         }
9590     },
9591
9592     /**
9593      * Returns the currently selected field value or empty string if no value is set.
9594      * @return {String} value The selected value
9595      */
9596     getValue : function(){
9597         
9598         if(this.multiple){
9599             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9600         }
9601         
9602         if(this.valueField){
9603             return typeof this.value != 'undefined' ? this.value : '';
9604         }else{
9605             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9606         }
9607     },
9608
9609     /**
9610      * Clears any text/value currently set in the field
9611      */
9612     clearValue : function(){
9613         if(this.hiddenField){
9614             this.hiddenField.dom.value = '';
9615         }
9616         this.value = '';
9617         this.setRawValue('');
9618         this.lastSelectionText = '';
9619         
9620     },
9621
9622     /**
9623      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
9624      * will be displayed in the field.  If the value does not match the data value of an existing item,
9625      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9626      * Otherwise the field will be blank (although the value will still be set).
9627      * @param {String} value The value to match
9628      */
9629     setValue : function(v){
9630         if(this.multiple){
9631             this.syncValue();
9632             return;
9633         }
9634         
9635         var text = v;
9636         if(this.valueField){
9637             var r = this.findRecord(this.valueField, v);
9638             if(r){
9639                 text = r.data[this.displayField];
9640             }else if(this.valueNotFoundText !== undefined){
9641                 text = this.valueNotFoundText;
9642             }
9643         }
9644         this.lastSelectionText = text;
9645         if(this.hiddenField){
9646             this.hiddenField.dom.value = v;
9647         }
9648         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9649         this.value = v;
9650     },
9651     /**
9652      * @property {Object} the last set data for the element
9653      */
9654     
9655     lastData : false,
9656     /**
9657      * Sets the value of the field based on a object which is related to the record format for the store.
9658      * @param {Object} value the value to set as. or false on reset?
9659      */
9660     setFromData : function(o){
9661         
9662         if(this.multiple){
9663             this.addItem(o);
9664             return;
9665         }
9666             
9667         var dv = ''; // display value
9668         var vv = ''; // value value..
9669         this.lastData = o;
9670         if (this.displayField) {
9671             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9672         } else {
9673             // this is an error condition!!!
9674             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
9675         }
9676         
9677         if(this.valueField){
9678             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9679         }
9680         
9681         if(this.hiddenField){
9682             this.hiddenField.dom.value = vv;
9683             
9684             this.lastSelectionText = dv;
9685             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9686             this.value = vv;
9687             return;
9688         }
9689         // no hidden field.. - we store the value in 'value', but still display
9690         // display field!!!!
9691         this.lastSelectionText = dv;
9692         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9693         this.value = vv;
9694         
9695         
9696     },
9697     // private
9698     reset : function(){
9699         // overridden so that last data is reset..
9700         this.setValue(this.originalValue);
9701         this.clearInvalid();
9702         this.lastData = false;
9703         if (this.view) {
9704             this.view.clearSelections();
9705         }
9706     },
9707     // private
9708     findRecord : function(prop, value){
9709         var record;
9710         if(this.store.getCount() > 0){
9711             this.store.each(function(r){
9712                 if(r.data[prop] == value){
9713                     record = r;
9714                     return false;
9715                 }
9716                 return true;
9717             });
9718         }
9719         return record;
9720     },
9721     
9722     getName: function()
9723     {
9724         // returns hidden if it's set..
9725         if (!this.rendered) {return ''};
9726         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
9727         
9728     },
9729     // private
9730     onViewMove : function(e, t){
9731         this.inKeyMode = false;
9732     },
9733
9734     // private
9735     onViewOver : function(e, t){
9736         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9737             return;
9738         }
9739         var item = this.view.findItemFromChild(t);
9740         if(item){
9741             var index = this.view.indexOf(item);
9742             this.select(index, false);
9743         }
9744     },
9745
9746     // private
9747     onViewClick : function(doFocus)
9748     {
9749         var index = this.view.getSelectedIndexes()[0];
9750         var r = this.store.getAt(index);
9751         if(r){
9752             this.onSelect(r, index);
9753         }
9754         if(doFocus !== false && !this.blockFocus){
9755             this.inputEl().focus();
9756         }
9757     },
9758
9759     // private
9760     restrictHeight : function(){
9761         //this.innerList.dom.style.height = '';
9762         //var inner = this.innerList.dom;
9763         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9764         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9765         //this.list.beginUpdate();
9766         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9767         this.list.alignTo(this.inputEl(), this.listAlign);
9768         //this.list.endUpdate();
9769     },
9770
9771     // private
9772     onEmptyResults : function(){
9773         this.collapse();
9774     },
9775
9776     /**
9777      * Returns true if the dropdown list is expanded, else false.
9778      */
9779     isExpanded : function(){
9780         return this.list.isVisible();
9781     },
9782
9783     /**
9784      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9785      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9786      * @param {String} value The data value of the item to select
9787      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9788      * selected item if it is not currently in view (defaults to true)
9789      * @return {Boolean} True if the value matched an item in the list, else false
9790      */
9791     selectByValue : function(v, scrollIntoView){
9792         if(v !== undefined && v !== null){
9793             var r = this.findRecord(this.valueField || this.displayField, v);
9794             if(r){
9795                 this.select(this.store.indexOf(r), scrollIntoView);
9796                 return true;
9797             }
9798         }
9799         return false;
9800     },
9801
9802     /**
9803      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9804      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9805      * @param {Number} index The zero-based index of the list item to select
9806      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9807      * selected item if it is not currently in view (defaults to true)
9808      */
9809     select : function(index, scrollIntoView){
9810         this.selectedIndex = index;
9811         this.view.select(index);
9812         if(scrollIntoView !== false){
9813             var el = this.view.getNode(index);
9814             if(el){
9815                 //this.innerList.scrollChildIntoView(el, false);
9816                 
9817             }
9818         }
9819     },
9820
9821     // private
9822     selectNext : function(){
9823         var ct = this.store.getCount();
9824         if(ct > 0){
9825             if(this.selectedIndex == -1){
9826                 this.select(0);
9827             }else if(this.selectedIndex < ct-1){
9828                 this.select(this.selectedIndex+1);
9829             }
9830         }
9831     },
9832
9833     // private
9834     selectPrev : function(){
9835         var ct = this.store.getCount();
9836         if(ct > 0){
9837             if(this.selectedIndex == -1){
9838                 this.select(0);
9839             }else if(this.selectedIndex != 0){
9840                 this.select(this.selectedIndex-1);
9841             }
9842         }
9843     },
9844
9845     // private
9846     onKeyUp : function(e){
9847         if(this.editable !== false && !e.isSpecialKey()){
9848             this.lastKey = e.getKey();
9849             this.dqTask.delay(this.queryDelay);
9850         }
9851     },
9852
9853     // private
9854     validateBlur : function(){
9855         return !this.list || !this.list.isVisible();   
9856     },
9857
9858     // private
9859     initQuery : function(){
9860         this.doQuery(this.getRawValue());
9861     },
9862
9863     // private
9864     doForce : function(){
9865         if(this.inputEl().dom.value.length > 0){
9866             this.inputEl().dom.value =
9867                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9868              
9869         }
9870     },
9871
9872     /**
9873      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
9874      * query allowing the query action to be canceled if needed.
9875      * @param {String} query The SQL query to execute
9876      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9877      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
9878      * saved in the current store (defaults to false)
9879      */
9880     doQuery : function(q, forceAll){
9881         
9882         if(q === undefined || q === null){
9883             q = '';
9884         }
9885         var qe = {
9886             query: q,
9887             forceAll: forceAll,
9888             combo: this,
9889             cancel:false
9890         };
9891         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9892             return false;
9893         }
9894         q = qe.query;
9895         
9896         forceAll = qe.forceAll;
9897         if(forceAll === true || (q.length >= this.minChars)){
9898             
9899             this.hasQuery = true;
9900             
9901             if(this.lastQuery != q || this.alwaysQuery){
9902                 this.lastQuery = q;
9903                 if(this.mode == 'local'){
9904                     this.selectedIndex = -1;
9905                     if(forceAll){
9906                         this.store.clearFilter();
9907                     }else{
9908                         this.store.filter(this.displayField, q);
9909                     }
9910                     this.onLoad();
9911                 }else{
9912                     this.store.baseParams[this.queryParam] = q;
9913                     
9914                     var options = {params : this.getParams(q)};
9915                     
9916                     if(this.loadNext){
9917                         options.add = true;
9918                         options.params.start = this.page * this.pageSize;
9919                     }
9920                     
9921                     this.store.load(options);
9922                     this.expand();
9923                 }
9924             }else{
9925                 this.selectedIndex = -1;
9926                 this.onLoad();   
9927             }
9928         }
9929         
9930         this.loadNext = false;
9931     },
9932
9933     // private
9934     getParams : function(q){
9935         var p = {};
9936         //p[this.queryParam] = q;
9937         
9938         if(this.pageSize){
9939             p.start = 0;
9940             p.limit = this.pageSize;
9941         }
9942         return p;
9943     },
9944
9945     /**
9946      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9947      */
9948     collapse : function(){
9949         if(!this.isExpanded()){
9950             return;
9951         }
9952         
9953         this.list.hide();
9954         Roo.get(document).un('mousedown', this.collapseIf, this);
9955         Roo.get(document).un('mousewheel', this.collapseIf, this);
9956         if (!this.editable) {
9957             Roo.get(document).un('keydown', this.listKeyPress, this);
9958         }
9959         this.fireEvent('collapse', this);
9960     },
9961
9962     // private
9963     collapseIf : function(e){
9964         var in_combo  = e.within(this.el);
9965         var in_list =  e.within(this.list);
9966         
9967         if (in_combo || in_list) {
9968             //e.stopPropagation();
9969             return;
9970         }
9971
9972         this.collapse();
9973         
9974     },
9975
9976     /**
9977      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9978      */
9979     expand : function(){
9980        
9981         if(this.isExpanded() || !this.hasFocus){
9982             return;
9983         }
9984          Roo.log('expand');
9985         this.list.alignTo(this.inputEl(), this.listAlign);
9986         this.list.show();
9987         Roo.get(document).on('mousedown', this.collapseIf, this);
9988         Roo.get(document).on('mousewheel', this.collapseIf, this);
9989         if (!this.editable) {
9990             Roo.get(document).on('keydown', this.listKeyPress, this);
9991         }
9992         
9993         this.fireEvent('expand', this);
9994     },
9995
9996     // private
9997     // Implements the default empty TriggerField.onTriggerClick function
9998     onTriggerClick : function()
9999     {
10000         Roo.log('trigger click');
10001         
10002         if(this.disabled){
10003             return;
10004         }
10005         
10006         this.page = 0;
10007         this.loadNext = false;
10008         
10009         if(this.isExpanded()){
10010             this.collapse();
10011             if (!this.blockFocus) {
10012                 this.inputEl().focus();
10013             }
10014             
10015         }else {
10016             this.hasFocus = true;
10017             if(this.triggerAction == 'all') {
10018                 this.doQuery(this.allQuery, true);
10019             } else {
10020                 this.doQuery(this.getRawValue());
10021             }
10022             if (!this.blockFocus) {
10023                 this.inputEl().focus();
10024             }
10025         }
10026     },
10027     listKeyPress : function(e)
10028     {
10029         //Roo.log('listkeypress');
10030         // scroll to first matching element based on key pres..
10031         if (e.isSpecialKey()) {
10032             return false;
10033         }
10034         var k = String.fromCharCode(e.getKey()).toUpperCase();
10035         //Roo.log(k);
10036         var match  = false;
10037         var csel = this.view.getSelectedNodes();
10038         var cselitem = false;
10039         if (csel.length) {
10040             var ix = this.view.indexOf(csel[0]);
10041             cselitem  = this.store.getAt(ix);
10042             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10043                 cselitem = false;
10044             }
10045             
10046         }
10047         
10048         this.store.each(function(v) { 
10049             if (cselitem) {
10050                 // start at existing selection.
10051                 if (cselitem.id == v.id) {
10052                     cselitem = false;
10053                 }
10054                 return true;
10055             }
10056                 
10057             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10058                 match = this.store.indexOf(v);
10059                 return false;
10060             }
10061             return true;
10062         }, this);
10063         
10064         if (match === false) {
10065             return true; // no more action?
10066         }
10067         // scroll to?
10068         this.view.select(match);
10069         var sn = Roo.get(this.view.getSelectedNodes()[0])
10070         //sn.scrollIntoView(sn.dom.parentNode, false);
10071     },
10072     
10073     onViewScroll : function(e, t){
10074         
10075         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10076             return;
10077         }
10078         
10079         this.hasQuery = true;
10080         
10081         this.loading = this.list.select('.loading', true).first();
10082         
10083         if(this.loading === null){
10084             this.list.createChild({
10085                 tag: 'div',
10086                 cls: 'loading select2-more-results select2-active',
10087                 html: 'Loading more results...'
10088             })
10089             
10090             this.loading = this.list.select('.loading', true).first();
10091             
10092             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10093             
10094             this.loading.hide();
10095         }
10096         
10097         this.loading.show();
10098         
10099         var _combo = this;
10100         
10101         this.page++;
10102         this.loadNext = true;
10103         
10104         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10105         
10106         return;
10107     },
10108     
10109     addItem : function(o)
10110     {   
10111         var dv = ''; // display value
10112         
10113         if (this.displayField) {
10114             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10115         } else {
10116             // this is an error condition!!!
10117             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10118         }
10119         
10120         if(!dv.length){
10121             return;
10122         }
10123         
10124         var choice = this.choices.createChild({
10125             tag: 'li',
10126             cls: 'select2-search-choice',
10127             cn: [
10128                 {
10129                     tag: 'div',
10130                     html: dv
10131                 },
10132                 {
10133                     tag: 'a',
10134                     href: '#',
10135                     cls: 'select2-search-choice-close',
10136                     tabindex: '-1'
10137                 }
10138             ]
10139             
10140         }, this.searchField);
10141         
10142         var close = choice.select('a.select2-search-choice-close', true).first()
10143         
10144         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10145         
10146         this.item.push(o);
10147         this.lastData = o;
10148         
10149         this.syncValue();
10150         
10151         this.inputEl().dom.value = '';
10152         
10153     },
10154     
10155     onRemoveItem : function(e, _self, o)
10156     {
10157         e.preventDefault();
10158         var index = this.item.indexOf(o.data) * 1;
10159         
10160         if( index < 0){
10161             Roo.log('not this item?!');
10162             return;
10163         }
10164         
10165         this.item.splice(index, 1);
10166         o.item.remove();
10167         
10168         this.syncValue();
10169         
10170         this.fireEvent('remove', this, e);
10171         
10172     },
10173     
10174     syncValue : function()
10175     {
10176         if(!this.item.length){
10177             this.clearValue();
10178             return;
10179         }
10180             
10181         var value = [];
10182         var _this = this;
10183         Roo.each(this.item, function(i){
10184             if(_this.valueField){
10185                 value.push(i[_this.valueField]);
10186                 return;
10187             }
10188
10189             value.push(i);
10190         });
10191
10192         this.value = value.join(',');
10193
10194         if(this.hiddenField){
10195             this.hiddenField.dom.value = this.value;
10196         }
10197     },
10198     
10199     clearItem : function()
10200     {
10201         if(!this.multiple){
10202             return;
10203         }
10204         
10205         this.item = [];
10206         
10207         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10208            c.remove();
10209         });
10210         
10211         this.syncValue();
10212     }
10213     
10214     
10215
10216     /** 
10217     * @cfg {Boolean} grow 
10218     * @hide 
10219     */
10220     /** 
10221     * @cfg {Number} growMin 
10222     * @hide 
10223     */
10224     /** 
10225     * @cfg {Number} growMax 
10226     * @hide 
10227     */
10228     /**
10229      * @hide
10230      * @method autoSize
10231      */
10232 });
10233 /*
10234  * Based on:
10235  * Ext JS Library 1.1.1
10236  * Copyright(c) 2006-2007, Ext JS, LLC.
10237  *
10238  * Originally Released Under LGPL - original licence link has changed is not relivant.
10239  *
10240  * Fork - LGPL
10241  * <script type="text/javascript">
10242  */
10243
10244 /**
10245  * @class Roo.View
10246  * @extends Roo.util.Observable
10247  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
10248  * This class also supports single and multi selection modes. <br>
10249  * Create a data model bound view:
10250  <pre><code>
10251  var store = new Roo.data.Store(...);
10252
10253  var view = new Roo.View({
10254     el : "my-element",
10255     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
10256  
10257     singleSelect: true,
10258     selectedClass: "ydataview-selected",
10259     store: store
10260  });
10261
10262  // listen for node click?
10263  view.on("click", function(vw, index, node, e){
10264  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10265  });
10266
10267  // load XML data
10268  dataModel.load("foobar.xml");
10269  </code></pre>
10270  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10271  * <br><br>
10272  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10273  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10274  * 
10275  * Note: old style constructor is still suported (container, template, config)
10276  * 
10277  * @constructor
10278  * Create a new View
10279  * @param {Object} config The config object
10280  * 
10281  */
10282 Roo.View = function(config, depreciated_tpl, depreciated_config){
10283     
10284     if (typeof(depreciated_tpl) == 'undefined') {
10285         // new way.. - universal constructor.
10286         Roo.apply(this, config);
10287         this.el  = Roo.get(this.el);
10288     } else {
10289         // old format..
10290         this.el  = Roo.get(config);
10291         this.tpl = depreciated_tpl;
10292         Roo.apply(this, depreciated_config);
10293     }
10294     this.wrapEl  = this.el.wrap().wrap();
10295     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10296     
10297     
10298     if(typeof(this.tpl) == "string"){
10299         this.tpl = new Roo.Template(this.tpl);
10300     } else {
10301         // support xtype ctors..
10302         this.tpl = new Roo.factory(this.tpl, Roo);
10303     }
10304     
10305     
10306     this.tpl.compile();
10307    
10308   
10309     
10310      
10311     /** @private */
10312     this.addEvents({
10313         /**
10314          * @event beforeclick
10315          * Fires before a click is processed. Returns false to cancel the default action.
10316          * @param {Roo.View} this
10317          * @param {Number} index The index of the target node
10318          * @param {HTMLElement} node The target node
10319          * @param {Roo.EventObject} e The raw event object
10320          */
10321             "beforeclick" : true,
10322         /**
10323          * @event click
10324          * Fires when a template node is clicked.
10325          * @param {Roo.View} this
10326          * @param {Number} index The index of the target node
10327          * @param {HTMLElement} node The target node
10328          * @param {Roo.EventObject} e The raw event object
10329          */
10330             "click" : true,
10331         /**
10332          * @event dblclick
10333          * Fires when a template node is double clicked.
10334          * @param {Roo.View} this
10335          * @param {Number} index The index of the target node
10336          * @param {HTMLElement} node The target node
10337          * @param {Roo.EventObject} e The raw event object
10338          */
10339             "dblclick" : true,
10340         /**
10341          * @event contextmenu
10342          * Fires when a template node is right clicked.
10343          * @param {Roo.View} this
10344          * @param {Number} index The index of the target node
10345          * @param {HTMLElement} node The target node
10346          * @param {Roo.EventObject} e The raw event object
10347          */
10348             "contextmenu" : true,
10349         /**
10350          * @event selectionchange
10351          * Fires when the selected nodes change.
10352          * @param {Roo.View} this
10353          * @param {Array} selections Array of the selected nodes
10354          */
10355             "selectionchange" : true,
10356     
10357         /**
10358          * @event beforeselect
10359          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10360          * @param {Roo.View} this
10361          * @param {HTMLElement} node The node to be selected
10362          * @param {Array} selections Array of currently selected nodes
10363          */
10364             "beforeselect" : true,
10365         /**
10366          * @event preparedata
10367          * Fires on every row to render, to allow you to change the data.
10368          * @param {Roo.View} this
10369          * @param {Object} data to be rendered (change this)
10370          */
10371           "preparedata" : true
10372           
10373           
10374         });
10375
10376
10377
10378     this.el.on({
10379         "click": this.onClick,
10380         "dblclick": this.onDblClick,
10381         "contextmenu": this.onContextMenu,
10382         scope:this
10383     });
10384
10385     this.selections = [];
10386     this.nodes = [];
10387     this.cmp = new Roo.CompositeElementLite([]);
10388     if(this.store){
10389         this.store = Roo.factory(this.store, Roo.data);
10390         this.setStore(this.store, true);
10391     }
10392     
10393     if ( this.footer && this.footer.xtype) {
10394            
10395          var fctr = this.wrapEl.appendChild(document.createElement("div"));
10396         
10397         this.footer.dataSource = this.store
10398         this.footer.container = fctr;
10399         this.footer = Roo.factory(this.footer, Roo);
10400         fctr.insertFirst(this.el);
10401         
10402         // this is a bit insane - as the paging toolbar seems to detach the el..
10403 //        dom.parentNode.parentNode.parentNode
10404          // they get detached?
10405     }
10406     
10407     
10408     Roo.View.superclass.constructor.call(this);
10409     
10410     
10411 };
10412
10413 Roo.extend(Roo.View, Roo.util.Observable, {
10414     
10415      /**
10416      * @cfg {Roo.data.Store} store Data store to load data from.
10417      */
10418     store : false,
10419     
10420     /**
10421      * @cfg {String|Roo.Element} el The container element.
10422      */
10423     el : '',
10424     
10425     /**
10426      * @cfg {String|Roo.Template} tpl The template used by this View 
10427      */
10428     tpl : false,
10429     /**
10430      * @cfg {String} dataName the named area of the template to use as the data area
10431      *                          Works with domtemplates roo-name="name"
10432      */
10433     dataName: false,
10434     /**
10435      * @cfg {String} selectedClass The css class to add to selected nodes
10436      */
10437     selectedClass : "x-view-selected",
10438      /**
10439      * @cfg {String} emptyText The empty text to show when nothing is loaded.
10440      */
10441     emptyText : "",
10442     
10443     /**
10444      * @cfg {String} text to display on mask (default Loading)
10445      */
10446     mask : false,
10447     /**
10448      * @cfg {Boolean} multiSelect Allow multiple selection
10449      */
10450     multiSelect : false,
10451     /**
10452      * @cfg {Boolean} singleSelect Allow single selection
10453      */
10454     singleSelect:  false,
10455     
10456     /**
10457      * @cfg {Boolean} toggleSelect - selecting 
10458      */
10459     toggleSelect : false,
10460     
10461     /**
10462      * Returns the element this view is bound to.
10463      * @return {Roo.Element}
10464      */
10465     getEl : function(){
10466         return this.wrapEl;
10467     },
10468     
10469     
10470
10471     /**
10472      * Refreshes the view. - called by datachanged on the store. - do not call directly.
10473      */
10474     refresh : function(){
10475         Roo.log('refresh');
10476         var t = this.tpl;
10477         
10478         // if we are using something like 'domtemplate', then
10479         // the what gets used is:
10480         // t.applySubtemplate(NAME, data, wrapping data..)
10481         // the outer template then get' applied with
10482         //     the store 'extra data'
10483         // and the body get's added to the
10484         //      roo-name="data" node?
10485         //      <span class='roo-tpl-{name}'></span> ?????
10486         
10487         
10488         
10489         this.clearSelections();
10490         this.el.update("");
10491         var html = [];
10492         var records = this.store.getRange();
10493         if(records.length < 1) {
10494             
10495             // is this valid??  = should it render a template??
10496             
10497             this.el.update(this.emptyText);
10498             return;
10499         }
10500         var el = this.el;
10501         if (this.dataName) {
10502             this.el.update(t.apply(this.store.meta)); //????
10503             el = this.el.child('.roo-tpl-' + this.dataName);
10504         }
10505         
10506         for(var i = 0, len = records.length; i < len; i++){
10507             var data = this.prepareData(records[i].data, i, records[i]);
10508             this.fireEvent("preparedata", this, data, i, records[i]);
10509             html[html.length] = Roo.util.Format.trim(
10510                 this.dataName ?
10511                     t.applySubtemplate(this.dataName, data, this.store.meta) :
10512                     t.apply(data)
10513             );
10514         }
10515         
10516         
10517         
10518         el.update(html.join(""));
10519         this.nodes = el.dom.childNodes;
10520         this.updateIndexes(0);
10521     },
10522     
10523
10524     /**
10525      * Function to override to reformat the data that is sent to
10526      * the template for each node.
10527      * DEPRICATED - use the preparedata event handler.
10528      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10529      * a JSON object for an UpdateManager bound view).
10530      */
10531     prepareData : function(data, index, record)
10532     {
10533         this.fireEvent("preparedata", this, data, index, record);
10534         return data;
10535     },
10536
10537     onUpdate : function(ds, record){
10538          Roo.log('on update');   
10539         this.clearSelections();
10540         var index = this.store.indexOf(record);
10541         var n = this.nodes[index];
10542         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10543         n.parentNode.removeChild(n);
10544         this.updateIndexes(index, index);
10545     },
10546
10547     
10548     
10549 // --------- FIXME     
10550     onAdd : function(ds, records, index)
10551     {
10552         Roo.log(['on Add', ds, records, index] );        
10553         this.clearSelections();
10554         if(this.nodes.length == 0){
10555             this.refresh();
10556             return;
10557         }
10558         var n = this.nodes[index];
10559         for(var i = 0, len = records.length; i < len; i++){
10560             var d = this.prepareData(records[i].data, i, records[i]);
10561             if(n){
10562                 this.tpl.insertBefore(n, d);
10563             }else{
10564                 
10565                 this.tpl.append(this.el, d);
10566             }
10567         }
10568         this.updateIndexes(index);
10569     },
10570
10571     onRemove : function(ds, record, index){
10572         Roo.log('onRemove');
10573         this.clearSelections();
10574         var el = this.dataName  ?
10575             this.el.child('.roo-tpl-' + this.dataName) :
10576             this.el; 
10577         
10578         el.dom.removeChild(this.nodes[index]);
10579         this.updateIndexes(index);
10580     },
10581
10582     /**
10583      * Refresh an individual node.
10584      * @param {Number} index
10585      */
10586     refreshNode : function(index){
10587         this.onUpdate(this.store, this.store.getAt(index));
10588     },
10589
10590     updateIndexes : function(startIndex, endIndex){
10591         var ns = this.nodes;
10592         startIndex = startIndex || 0;
10593         endIndex = endIndex || ns.length - 1;
10594         for(var i = startIndex; i <= endIndex; i++){
10595             ns[i].nodeIndex = i;
10596         }
10597     },
10598
10599     /**
10600      * Changes the data store this view uses and refresh the view.
10601      * @param {Store} store
10602      */
10603     setStore : function(store, initial){
10604         if(!initial && this.store){
10605             this.store.un("datachanged", this.refresh);
10606             this.store.un("add", this.onAdd);
10607             this.store.un("remove", this.onRemove);
10608             this.store.un("update", this.onUpdate);
10609             this.store.un("clear", this.refresh);
10610             this.store.un("beforeload", this.onBeforeLoad);
10611             this.store.un("load", this.onLoad);
10612             this.store.un("loadexception", this.onLoad);
10613         }
10614         if(store){
10615           
10616             store.on("datachanged", this.refresh, this);
10617             store.on("add", this.onAdd, this);
10618             store.on("remove", this.onRemove, this);
10619             store.on("update", this.onUpdate, this);
10620             store.on("clear", this.refresh, this);
10621             store.on("beforeload", this.onBeforeLoad, this);
10622             store.on("load", this.onLoad, this);
10623             store.on("loadexception", this.onLoad, this);
10624         }
10625         
10626         if(store){
10627             this.refresh();
10628         }
10629     },
10630     /**
10631      * onbeforeLoad - masks the loading area.
10632      *
10633      */
10634     onBeforeLoad : function(store,opts)
10635     {
10636          Roo.log('onBeforeLoad');   
10637         if (!opts.add) {
10638             this.el.update("");
10639         }
10640         this.el.mask(this.mask ? this.mask : "Loading" ); 
10641     },
10642     onLoad : function ()
10643     {
10644         this.el.unmask();
10645     },
10646     
10647
10648     /**
10649      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10650      * @param {HTMLElement} node
10651      * @return {HTMLElement} The template node
10652      */
10653     findItemFromChild : function(node){
10654         var el = this.dataName  ?
10655             this.el.child('.roo-tpl-' + this.dataName,true) :
10656             this.el.dom; 
10657         
10658         if(!node || node.parentNode == el){
10659                     return node;
10660             }
10661             var p = node.parentNode;
10662             while(p && p != el){
10663             if(p.parentNode == el){
10664                 return p;
10665             }
10666             p = p.parentNode;
10667         }
10668             return null;
10669     },
10670
10671     /** @ignore */
10672     onClick : function(e){
10673         var item = this.findItemFromChild(e.getTarget());
10674         if(item){
10675             var index = this.indexOf(item);
10676             if(this.onItemClick(item, index, e) !== false){
10677                 this.fireEvent("click", this, index, item, e);
10678             }
10679         }else{
10680             this.clearSelections();
10681         }
10682     },
10683
10684     /** @ignore */
10685     onContextMenu : function(e){
10686         var item = this.findItemFromChild(e.getTarget());
10687         if(item){
10688             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10689         }
10690     },
10691
10692     /** @ignore */
10693     onDblClick : function(e){
10694         var item = this.findItemFromChild(e.getTarget());
10695         if(item){
10696             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10697         }
10698     },
10699
10700     onItemClick : function(item, index, e)
10701     {
10702         if(this.fireEvent("beforeclick", this, index, item, e) === false){
10703             return false;
10704         }
10705         if (this.toggleSelect) {
10706             var m = this.isSelected(item) ? 'unselect' : 'select';
10707             Roo.log(m);
10708             var _t = this;
10709             _t[m](item, true, false);
10710             return true;
10711         }
10712         if(this.multiSelect || this.singleSelect){
10713             if(this.multiSelect && e.shiftKey && this.lastSelection){
10714                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10715             }else{
10716                 this.select(item, this.multiSelect && e.ctrlKey);
10717                 this.lastSelection = item;
10718             }
10719             e.preventDefault();
10720         }
10721         return true;
10722     },
10723
10724     /**
10725      * Get the number of selected nodes.
10726      * @return {Number}
10727      */
10728     getSelectionCount : function(){
10729         return this.selections.length;
10730     },
10731
10732     /**
10733      * Get the currently selected nodes.
10734      * @return {Array} An array of HTMLElements
10735      */
10736     getSelectedNodes : function(){
10737         return this.selections;
10738     },
10739
10740     /**
10741      * Get the indexes of the selected nodes.
10742      * @return {Array}
10743      */
10744     getSelectedIndexes : function(){
10745         var indexes = [], s = this.selections;
10746         for(var i = 0, len = s.length; i < len; i++){
10747             indexes.push(s[i].nodeIndex);
10748         }
10749         return indexes;
10750     },
10751
10752     /**
10753      * Clear all selections
10754      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10755      */
10756     clearSelections : function(suppressEvent){
10757         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10758             this.cmp.elements = this.selections;
10759             this.cmp.removeClass(this.selectedClass);
10760             this.selections = [];
10761             if(!suppressEvent){
10762                 this.fireEvent("selectionchange", this, this.selections);
10763             }
10764         }
10765     },
10766
10767     /**
10768      * Returns true if the passed node is selected
10769      * @param {HTMLElement/Number} node The node or node index
10770      * @return {Boolean}
10771      */
10772     isSelected : function(node){
10773         var s = this.selections;
10774         if(s.length < 1){
10775             return false;
10776         }
10777         node = this.getNode(node);
10778         return s.indexOf(node) !== -1;
10779     },
10780
10781     /**
10782      * Selects nodes.
10783      * @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
10784      * @param {Boolean} keepExisting (optional) true to keep existing selections
10785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10786      */
10787     select : function(nodeInfo, keepExisting, suppressEvent){
10788         if(nodeInfo instanceof Array){
10789             if(!keepExisting){
10790                 this.clearSelections(true);
10791             }
10792             for(var i = 0, len = nodeInfo.length; i < len; i++){
10793                 this.select(nodeInfo[i], true, true);
10794             }
10795             return;
10796         } 
10797         var node = this.getNode(nodeInfo);
10798         if(!node || this.isSelected(node)){
10799             return; // already selected.
10800         }
10801         if(!keepExisting){
10802             this.clearSelections(true);
10803         }
10804         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10805             Roo.fly(node).addClass(this.selectedClass);
10806             this.selections.push(node);
10807             if(!suppressEvent){
10808                 this.fireEvent("selectionchange", this, this.selections);
10809             }
10810         }
10811         
10812         
10813     },
10814       /**
10815      * Unselects nodes.
10816      * @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
10817      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10818      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10819      */
10820     unselect : function(nodeInfo, keepExisting, suppressEvent)
10821     {
10822         if(nodeInfo instanceof Array){
10823             Roo.each(this.selections, function(s) {
10824                 this.unselect(s, nodeInfo);
10825             }, this);
10826             return;
10827         }
10828         var node = this.getNode(nodeInfo);
10829         if(!node || !this.isSelected(node)){
10830             Roo.log("not selected");
10831             return; // not selected.
10832         }
10833         // fireevent???
10834         var ns = [];
10835         Roo.each(this.selections, function(s) {
10836             if (s == node ) {
10837                 Roo.fly(node).removeClass(this.selectedClass);
10838
10839                 return;
10840             }
10841             ns.push(s);
10842         },this);
10843         
10844         this.selections= ns;
10845         this.fireEvent("selectionchange", this, this.selections);
10846     },
10847
10848     /**
10849      * Gets a template node.
10850      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10851      * @return {HTMLElement} The node or null if it wasn't found
10852      */
10853     getNode : function(nodeInfo){
10854         if(typeof nodeInfo == "string"){
10855             return document.getElementById(nodeInfo);
10856         }else if(typeof nodeInfo == "number"){
10857             return this.nodes[nodeInfo];
10858         }
10859         return nodeInfo;
10860     },
10861
10862     /**
10863      * Gets a range template nodes.
10864      * @param {Number} startIndex
10865      * @param {Number} endIndex
10866      * @return {Array} An array of nodes
10867      */
10868     getNodes : function(start, end){
10869         var ns = this.nodes;
10870         start = start || 0;
10871         end = typeof end == "undefined" ? ns.length - 1 : end;
10872         var nodes = [];
10873         if(start <= end){
10874             for(var i = start; i <= end; i++){
10875                 nodes.push(ns[i]);
10876             }
10877         } else{
10878             for(var i = start; i >= end; i--){
10879                 nodes.push(ns[i]);
10880             }
10881         }
10882         return nodes;
10883     },
10884
10885     /**
10886      * Finds the index of the passed node
10887      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10888      * @return {Number} The index of the node or -1
10889      */
10890     indexOf : function(node){
10891         node = this.getNode(node);
10892         if(typeof node.nodeIndex == "number"){
10893             return node.nodeIndex;
10894         }
10895         var ns = this.nodes;
10896         for(var i = 0, len = ns.length; i < len; i++){
10897             if(ns[i] == node){
10898                 return i;
10899             }
10900         }
10901         return -1;
10902     }
10903 });
10904 /*
10905  * - LGPL
10906  *
10907  * based on jquery fullcalendar
10908  * 
10909  */
10910
10911 Roo.bootstrap = Roo.bootstrap || {};
10912 /**
10913  * @class Roo.bootstrap.Calendar
10914  * @extends Roo.bootstrap.Component
10915  * Bootstrap Calendar class
10916  * @cfg {Boolean} loadMask (true|false) default false
10917  * @cfg {Object} header generate the user specific header of the calendar, default false
10918
10919  * @constructor
10920  * Create a new Container
10921  * @param {Object} config The config object
10922  */
10923
10924
10925
10926 Roo.bootstrap.Calendar = function(config){
10927     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10928      this.addEvents({
10929         /**
10930              * @event select
10931              * Fires when a date is selected
10932              * @param {DatePicker} this
10933              * @param {Date} date The selected date
10934              */
10935         'select': true,
10936         /**
10937              * @event monthchange
10938              * Fires when the displayed month changes 
10939              * @param {DatePicker} this
10940              * @param {Date} date The selected month
10941              */
10942         'monthchange': true,
10943         /**
10944              * @event evententer
10945              * Fires when mouse over an event
10946              * @param {Calendar} this
10947              * @param {event} Event
10948              */
10949         'evententer': true,
10950         /**
10951              * @event eventleave
10952              * Fires when the mouse leaves an
10953              * @param {Calendar} this
10954              * @param {event}
10955              */
10956         'eventleave': true,
10957         /**
10958              * @event eventclick
10959              * Fires when the mouse click an
10960              * @param {Calendar} this
10961              * @param {event}
10962              */
10963         'eventclick': true
10964         
10965     });
10966
10967 };
10968
10969 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
10970     
10971      /**
10972      * @cfg {Number} startDay
10973      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10974      */
10975     startDay : 0,
10976     
10977     loadMask : false,
10978     
10979     header : false,
10980       
10981     getAutoCreate : function(){
10982         
10983         
10984         var fc_button = function(name, corner, style, content ) {
10985             return Roo.apply({},{
10986                 tag : 'span',
10987                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
10988                          (corner.length ?
10989                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10990                             ''
10991                         ),
10992                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10993                 unselectable: 'on'
10994             });
10995         };
10996         
10997         var header = {};
10998         
10999         if(!this.header){
11000             header = {
11001                 tag : 'table',
11002                 cls : 'fc-header',
11003                 style : 'width:100%',
11004                 cn : [
11005                     {
11006                         tag: 'tr',
11007                         cn : [
11008                             {
11009                                 tag : 'td',
11010                                 cls : 'fc-header-left',
11011                                 cn : [
11012                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11013                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11014                                     { tag: 'span', cls: 'fc-header-space' },
11015                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11016
11017
11018                                 ]
11019                             },
11020
11021                             {
11022                                 tag : 'td',
11023                                 cls : 'fc-header-center',
11024                                 cn : [
11025                                     {
11026                                         tag: 'span',
11027                                         cls: 'fc-header-title',
11028                                         cn : {
11029                                             tag: 'H2',
11030                                             html : 'month / year'
11031                                         }
11032                                     }
11033
11034                                 ]
11035                             },
11036                             {
11037                                 tag : 'td',
11038                                 cls : 'fc-header-right',
11039                                 cn : [
11040                               /*      fc_button('month', 'left', '', 'month' ),
11041                                     fc_button('week', '', '', 'week' ),
11042                                     fc_button('day', 'right', '', 'day' )
11043                                 */    
11044
11045                                 ]
11046                             }
11047
11048                         ]
11049                     }
11050                 ]
11051             };
11052         }
11053         
11054         header = this.header;
11055         
11056        
11057         var cal_heads = function() {
11058             var ret = [];
11059             // fixme - handle this.
11060             
11061             for (var i =0; i < Date.dayNames.length; i++) {
11062                 var d = Date.dayNames[i];
11063                 ret.push({
11064                     tag: 'th',
11065                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11066                     html : d.substring(0,3)
11067                 });
11068                 
11069             }
11070             ret[0].cls += ' fc-first';
11071             ret[6].cls += ' fc-last';
11072             return ret;
11073         };
11074         var cal_cell = function(n) {
11075             return  {
11076                 tag: 'td',
11077                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11078                 cn : [
11079                     {
11080                         cn : [
11081                             {
11082                                 cls: 'fc-day-number',
11083                                 html: 'D'
11084                             },
11085                             {
11086                                 cls: 'fc-day-content',
11087                              
11088                                 cn : [
11089                                      {
11090                                         style: 'position: relative;' // height: 17px;
11091                                     }
11092                                 ]
11093                             }
11094                             
11095                             
11096                         ]
11097                     }
11098                 ]
11099                 
11100             }
11101         };
11102         var cal_rows = function() {
11103             
11104             var ret = []
11105             for (var r = 0; r < 6; r++) {
11106                 var row= {
11107                     tag : 'tr',
11108                     cls : 'fc-week',
11109                     cn : []
11110                 };
11111                 
11112                 for (var i =0; i < Date.dayNames.length; i++) {
11113                     var d = Date.dayNames[i];
11114                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11115
11116                 }
11117                 row.cn[0].cls+=' fc-first';
11118                 row.cn[0].cn[0].style = 'min-height:90px';
11119                 row.cn[6].cls+=' fc-last';
11120                 ret.push(row);
11121                 
11122             }
11123             ret[0].cls += ' fc-first';
11124             ret[4].cls += ' fc-prev-last';
11125             ret[5].cls += ' fc-last';
11126             return ret;
11127             
11128         };
11129         
11130         var cal_table = {
11131             tag: 'table',
11132             cls: 'fc-border-separate',
11133             style : 'width:100%',
11134             cellspacing  : 0,
11135             cn : [
11136                 { 
11137                     tag: 'thead',
11138                     cn : [
11139                         { 
11140                             tag: 'tr',
11141                             cls : 'fc-first fc-last',
11142                             cn : cal_heads()
11143                         }
11144                     ]
11145                 },
11146                 { 
11147                     tag: 'tbody',
11148                     cn : cal_rows()
11149                 }
11150                   
11151             ]
11152         };
11153          
11154          var cfg = {
11155             cls : 'fc fc-ltr',
11156             cn : [
11157                 header,
11158                 {
11159                     cls : 'fc-content',
11160                     style : "position: relative;",
11161                     cn : [
11162                         {
11163                             cls : 'fc-view fc-view-month fc-grid',
11164                             style : 'position: relative',
11165                             unselectable : 'on',
11166                             cn : [
11167                                 {
11168                                     cls : 'fc-event-container',
11169                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11170                                 },
11171                                 cal_table
11172                             ]
11173                         }
11174                     ]
11175     
11176                 }
11177            ] 
11178             
11179         };
11180         
11181          
11182         
11183         return cfg;
11184     },
11185     
11186     
11187     initEvents : function()
11188     {
11189         if(!this.store){
11190             throw "can not find store for calendar";
11191         }
11192         
11193         var mark = {
11194             tag: "div",
11195             cls:"x-dlg-mask",
11196             style: "text-align:center",
11197             cn: [
11198                 {
11199                     tag: "div",
11200                     style: "background-color:white;width:50%;margin:250 auto",
11201                     cn: [
11202                         {
11203                             tag: "img",
11204                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11205                         },
11206                         {
11207                             tag: "span",
11208                             html: "Loading"
11209                         }
11210                         
11211                     ]
11212                 }
11213             ]
11214         }
11215         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11216         
11217         var size = this.el.select('.fc-content', true).first().getSize();
11218         this.maskEl.setSize(size.width, size.height);
11219         this.maskEl.enableDisplayMode("block");
11220         if(!this.loadMask){
11221             this.maskEl.hide();
11222         }
11223         
11224         this.store = Roo.factory(this.store, Roo.data);
11225         this.store.on('load', this.onLoad, this);
11226         this.store.on('beforeload', this.onBeforeLoad, this);
11227         
11228         this.resize();
11229         
11230         this.cells = this.el.select('.fc-day',true);
11231         //Roo.log(this.cells);
11232         this.textNodes = this.el.query('.fc-day-number');
11233         this.cells.addClassOnOver('fc-state-hover');
11234         
11235         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11236         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11237         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11238         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11239         
11240         this.on('monthchange', this.onMonthChange, this);
11241         
11242         this.update(new Date().clearTime());
11243     },
11244     
11245     resize : function() {
11246         var sz  = this.el.getSize();
11247         
11248         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11249         this.el.select('.fc-day-content div',true).setHeight(34);
11250     },
11251     
11252     
11253     // private
11254     showPrevMonth : function(e){
11255         this.update(this.activeDate.add("mo", -1));
11256     },
11257     showToday : function(e){
11258         this.update(new Date().clearTime());
11259     },
11260     // private
11261     showNextMonth : function(e){
11262         this.update(this.activeDate.add("mo", 1));
11263     },
11264
11265     // private
11266     showPrevYear : function(){
11267         this.update(this.activeDate.add("y", -1));
11268     },
11269
11270     // private
11271     showNextYear : function(){
11272         this.update(this.activeDate.add("y", 1));
11273     },
11274
11275     
11276    // private
11277     update : function(date)
11278     {
11279         var vd = this.activeDate;
11280         this.activeDate = date;
11281 //        if(vd && this.el){
11282 //            var t = date.getTime();
11283 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11284 //                Roo.log('using add remove');
11285 //                
11286 //                this.fireEvent('monthchange', this, date);
11287 //                
11288 //                this.cells.removeClass("fc-state-highlight");
11289 //                this.cells.each(function(c){
11290 //                   if(c.dateValue == t){
11291 //                       c.addClass("fc-state-highlight");
11292 //                       setTimeout(function(){
11293 //                            try{c.dom.firstChild.focus();}catch(e){}
11294 //                       }, 50);
11295 //                       return false;
11296 //                   }
11297 //                   return true;
11298 //                });
11299 //                return;
11300 //            }
11301 //        }
11302         
11303         var days = date.getDaysInMonth();
11304         
11305         var firstOfMonth = date.getFirstDateOfMonth();
11306         var startingPos = firstOfMonth.getDay()-this.startDay;
11307         
11308         if(startingPos < this.startDay){
11309             startingPos += 7;
11310         }
11311         
11312         var pm = date.add(Date.MONTH, -1);
11313         var prevStart = pm.getDaysInMonth()-startingPos;
11314 //        
11315         this.cells = this.el.select('.fc-day',true);
11316         this.textNodes = this.el.query('.fc-day-number');
11317         this.cells.addClassOnOver('fc-state-hover');
11318         
11319         var cells = this.cells.elements;
11320         var textEls = this.textNodes;
11321         
11322         Roo.each(cells, function(cell){
11323             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11324         });
11325         
11326         days += startingPos;
11327
11328         // convert everything to numbers so it's fast
11329         var day = 86400000;
11330         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11331         //Roo.log(d);
11332         //Roo.log(pm);
11333         //Roo.log(prevStart);
11334         
11335         var today = new Date().clearTime().getTime();
11336         var sel = date.clearTime().getTime();
11337         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11338         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11339         var ddMatch = this.disabledDatesRE;
11340         var ddText = this.disabledDatesText;
11341         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11342         var ddaysText = this.disabledDaysText;
11343         var format = this.format;
11344         
11345         var setCellClass = function(cal, cell){
11346             cell.row = 0;
11347             cell.events = [];
11348             cell.more = [];
11349             //Roo.log('set Cell Class');
11350             cell.title = "";
11351             var t = d.getTime();
11352             
11353             //Roo.log(d);
11354             
11355             cell.dateValue = t;
11356             if(t == today){
11357                 cell.className += " fc-today";
11358                 cell.className += " fc-state-highlight";
11359                 cell.title = cal.todayText;
11360             }
11361             if(t == sel){
11362                 // disable highlight in other month..
11363                 //cell.className += " fc-state-highlight";
11364                 
11365             }
11366             // disabling
11367             if(t < min) {
11368                 cell.className = " fc-state-disabled";
11369                 cell.title = cal.minText;
11370                 return;
11371             }
11372             if(t > max) {
11373                 cell.className = " fc-state-disabled";
11374                 cell.title = cal.maxText;
11375                 return;
11376             }
11377             if(ddays){
11378                 if(ddays.indexOf(d.getDay()) != -1){
11379                     cell.title = ddaysText;
11380                     cell.className = " fc-state-disabled";
11381                 }
11382             }
11383             if(ddMatch && format){
11384                 var fvalue = d.dateFormat(format);
11385                 if(ddMatch.test(fvalue)){
11386                     cell.title = ddText.replace("%0", fvalue);
11387                     cell.className = " fc-state-disabled";
11388                 }
11389             }
11390             
11391             if (!cell.initialClassName) {
11392                 cell.initialClassName = cell.dom.className;
11393             }
11394             
11395             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
11396         };
11397
11398         var i = 0;
11399         
11400         for(; i < startingPos; i++) {
11401             textEls[i].innerHTML = (++prevStart);
11402             d.setDate(d.getDate()+1);
11403             
11404             cells[i].className = "fc-past fc-other-month";
11405             setCellClass(this, cells[i]);
11406         }
11407         
11408         var intDay = 0;
11409         
11410         for(; i < days; i++){
11411             intDay = i - startingPos + 1;
11412             textEls[i].innerHTML = (intDay);
11413             d.setDate(d.getDate()+1);
11414             
11415             cells[i].className = ''; // "x-date-active";
11416             setCellClass(this, cells[i]);
11417         }
11418         var extraDays = 0;
11419         
11420         for(; i < 42; i++) {
11421             textEls[i].innerHTML = (++extraDays);
11422             d.setDate(d.getDate()+1);
11423             
11424             cells[i].className = "fc-future fc-other-month";
11425             setCellClass(this, cells[i]);
11426         }
11427         
11428         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11429         
11430         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11431         
11432         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11433         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11434         
11435         if(totalRows != 6){
11436             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11437             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11438         }
11439         
11440         this.fireEvent('monthchange', this, date);
11441         
11442         
11443         /*
11444         if(!this.internalRender){
11445             var main = this.el.dom.firstChild;
11446             var w = main.offsetWidth;
11447             this.el.setWidth(w + this.el.getBorderWidth("lr"));
11448             Roo.fly(main).setWidth(w);
11449             this.internalRender = true;
11450             // opera does not respect the auto grow header center column
11451             // then, after it gets a width opera refuses to recalculate
11452             // without a second pass
11453             if(Roo.isOpera && !this.secondPass){
11454                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11455                 this.secondPass = true;
11456                 this.update.defer(10, this, [date]);
11457             }
11458         }
11459         */
11460         
11461     },
11462     
11463     findCell : function(dt) {
11464         dt = dt.clearTime().getTime();
11465         var ret = false;
11466         this.cells.each(function(c){
11467             //Roo.log("check " +c.dateValue + '?=' + dt);
11468             if(c.dateValue == dt){
11469                 ret = c;
11470                 return false;
11471             }
11472             return true;
11473         });
11474         
11475         return ret;
11476     },
11477     
11478     findCells : function(ev) {
11479         var s = ev.start.clone().clearTime().getTime();
11480        // Roo.log(s);
11481         var e= ev.end.clone().clearTime().getTime();
11482        // Roo.log(e);
11483         var ret = [];
11484         this.cells.each(function(c){
11485              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11486             
11487             if(c.dateValue > e){
11488                 return ;
11489             }
11490             if(c.dateValue < s){
11491                 return ;
11492             }
11493             ret.push(c);
11494         });
11495         
11496         return ret;    
11497     },
11498     
11499 //    findBestRow: function(cells)
11500 //    {
11501 //        var ret = 0;
11502 //        
11503 //        for (var i =0 ; i < cells.length;i++) {
11504 //            ret  = Math.max(cells[i].rows || 0,ret);
11505 //        }
11506 //        return ret;
11507 //        
11508 //    },
11509     
11510     
11511     addItem : function(ev)
11512     {
11513         // look for vertical location slot in
11514         var cells = this.findCells(ev);
11515         
11516 //        ev.row = this.findBestRow(cells);
11517         
11518         // work out the location.
11519         
11520         var crow = false;
11521         var rows = [];
11522         for(var i =0; i < cells.length; i++) {
11523             
11524             cells[i].row = cells[0].row;
11525             
11526             if(i == 0){
11527                 cells[i].row = cells[i].row + 1;
11528             }
11529             
11530             if (!crow) {
11531                 crow = {
11532                     start : cells[i],
11533                     end :  cells[i]
11534                 };
11535                 continue;
11536             }
11537             if (crow.start.getY() == cells[i].getY()) {
11538                 // on same row.
11539                 crow.end = cells[i];
11540                 continue;
11541             }
11542             // different row.
11543             rows.push(crow);
11544             crow = {
11545                 start: cells[i],
11546                 end : cells[i]
11547             };
11548             
11549         }
11550         
11551         rows.push(crow);
11552         ev.els = [];
11553         ev.rows = rows;
11554         ev.cells = cells;
11555         
11556         cells[0].events.push(ev);
11557         
11558 //        if((typeof(cells[0].events) == 'undefined')){
11559 //            cells[0].events = [];
11560 //        }
11561 //        
11562 //        cells[0].events.push(ev);
11563 //        ev.rendered = false;
11564 //        for (var i = 0; i < cells.length;i++) {
11565 //            cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11566 //            
11567 //        }
11568         
11569         this.calevents.push(ev);
11570     },
11571     
11572     clearEvents: function() {
11573         
11574         if(!this.calevents){
11575             return;
11576         }
11577         
11578         Roo.each(this.cells.elements, function(c){
11579             c.row = 0;
11580             c.events = [];
11581             c.more = [];
11582         });
11583         
11584         Roo.each(this.calevents, function(e) {
11585             Roo.each(e.els, function(el) {
11586                 el.un('mouseenter' ,this.onEventEnter, this);
11587                 el.un('mouseleave' ,this.onEventLeave, this);
11588                 el.remove();
11589             },this);
11590         },this);
11591         
11592         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11593             e.remove();
11594         });
11595         
11596     },
11597     
11598     renderEvents: function()
11599     {  
11600 //        for (var e = 0; e < this.calevents.length; e++) {
11601 //            
11602 //            var ev = this.calevents[e];
11603 //            var cells = ev.cells;
11604 //            var rows = ev.rows;
11605 //            
11606 //            for (var j = 0; j < cells.length; j++){
11607 //            
11608 //                if(!cells[j].more.length){
11609 //                    cells[j].row++;
11610 //                }
11611 //                if(cells[j].row > 3){
11612 //                    cells[j].more.push(ev);
11613 //                    continue;
11614 //                }
11615 //                
11616 //                cells[j].events.push(ev);
11617 //            }
11618 //        }
11619             
11620 //            for (var i = 0; i < rows.length; i++){
11621 //                // how many rows should it span..
11622 //
11623 //                var  cfg = {
11624 //                    cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11625 //                    style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11626 //
11627 //                    unselectable : "on",
11628 //                    cn : [
11629 //                        {
11630 //                            cls: 'fc-event-inner',
11631 //                            cn : [
11632 ////                                {
11633 ////                                  tag:'span',
11634 ////                                  cls: 'fc-event-time',
11635 ////                                  html : cells.length > 1 ? '' : ev.time
11636 ////                                },
11637 //                                {
11638 //                                  tag:'span',
11639 //                                  cls: 'fc-event-title',
11640 //                                  html : String.format('{0}', ev.title)
11641 //                                }
11642 //
11643 //
11644 //                            ]
11645 //                        },
11646 //                        {
11647 //                            cls: 'ui-resizable-handle ui-resizable-e',
11648 //                            html : '&nbsp;&nbsp;&nbsp'
11649 //                        }
11650 //
11651 //                    ]
11652 //                };
11653 //
11654 //                if (i == 0) {
11655 //                    cfg.cls += ' fc-event-start';
11656 //                }
11657 //                if ((i+1) == rows.length) {
11658 //                    cfg.cls += ' fc-event-end';
11659 //                }
11660 //
11661 //                var ctr = this.el.select('.fc-event-container',true).first();
11662 //                var cg = ctr.createChild(cfg);
11663 //
11664 //                var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11665 //                var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11666 //
11667 //                cg.setXY([sbox.x +2, sbox.y +(e * 20)]);    
11668 //                cg.setWidth(ebox.right - sbox.x -2);
11669 //
11670 //                cg.on('mouseenter' ,this.onEventEnter, this, ev);
11671 //                cg.on('mouseleave' ,this.onEventLeave, this, ev);
11672 //                cg.on('click', this.onEventClick, this, ev);
11673 //
11674 //                ev.els.push(cg);
11675 //
11676 //                
11677 //            }
11678 //        }
11679         
11680         var _this = this;
11681         
11682         this.cells.each(function(c) {
11683             
11684             if(c.row < 5){
11685                 return;
11686             }
11687             
11688             var ev = c.events;
11689             
11690             var r = 4;
11691             if(c.row != c.events.length){
11692                 r = 4 - (4 - (c.row - c.events.length));
11693             }
11694             
11695             c.events = ev.slice(0, r);
11696             c.more = ev.slice(r);
11697             
11698             if(c.more.length && c.more.length == 1){
11699                 c.events.push(c.more.pop());
11700             }
11701             
11702             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
11703             
11704         });
11705             
11706 //        for (var e = 0; e < this.calevents.length; e++) {
11707 //            
11708 //            var ev = this.calevents[e];
11709 //            var cells = ev.cells;
11710 //            var rows = ev.rows;
11711 //            
11712 //            for(var i = 0; i < cells.length; i++){
11713 //                
11714 //                var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11715 //                
11716 //                if(cells.length < 2 && cbox.rows.length > 3){
11717 //                    cbox.more.push(ev);
11718 //                    continue;
11719 //                }
11720 //                
11721 //                cbox.rows.push(ev);
11722 //            }
11723 //        }
11724 //        
11725         this.cells.each(function(c) {
11726             
11727             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
11728             
11729             
11730             for (var e = 0; e < c.events.length; e++){
11731                 var ev = c.events[e];
11732                 var rows = ev.rows;
11733                 
11734                 for(var i = 0; i < rows.length; i++) {
11735                 
11736                     // how many rows should it span..
11737
11738                     var  cfg = {
11739                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11740                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11741
11742                         unselectable : "on",
11743                         cn : [
11744                             {
11745                                 cls: 'fc-event-inner',
11746                                 cn : [
11747     //                                {
11748     //                                  tag:'span',
11749     //                                  cls: 'fc-event-time',
11750     //                                  html : cells.length > 1 ? '' : ev.time
11751     //                                },
11752                                     {
11753                                       tag:'span',
11754                                       cls: 'fc-event-title',
11755                                       html : String.format('{0}', ev.title)
11756                                     }
11757
11758
11759                                 ]
11760                             },
11761                             {
11762                                 cls: 'ui-resizable-handle ui-resizable-e',
11763                                 html : '&nbsp;&nbsp;&nbsp'
11764                             }
11765
11766                         ]
11767                     };
11768
11769                     if (i == 0) {
11770                         cfg.cls += ' fc-event-start';
11771                     }
11772                     if ((i+1) == rows.length) {
11773                         cfg.cls += ' fc-event-end';
11774                     }
11775
11776                     var ctr = _this.el.select('.fc-event-container',true).first();
11777                     var cg = ctr.createChild(cfg);
11778
11779                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11780                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11781
11782                     var r = (c.more.length) ? 1 : 0;
11783                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
11784                     cg.setWidth(ebox.right - sbox.x -2);
11785
11786                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11787                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11788                     cg.on('click', _this.onEventClick, _this, ev);
11789
11790                     ev.els.push(cg);
11791                     
11792                 }
11793                 
11794             }
11795             
11796             
11797             if(c.more.length){
11798                 var  cfg = {
11799                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11800                     style : 'position: absolute',
11801                     unselectable : "on",
11802                     cn : [
11803                         {
11804                             cls: 'fc-event-inner',
11805                             cn : [
11806                                 {
11807                                   tag:'span',
11808                                   cls: 'fc-event-title',
11809                                   html : 'More'
11810                                 }
11811
11812
11813                             ]
11814                         },
11815                         {
11816                             cls: 'ui-resizable-handle ui-resizable-e',
11817                             html : '&nbsp;&nbsp;&nbsp'
11818                         }
11819
11820                     ]
11821                 };
11822
11823                 var ctr = _this.el.select('.fc-event-container',true).first();
11824                 var cg = ctr.createChild(cfg);
11825
11826                 var sbox = c.select('.fc-day-content',true).first().getBox();
11827                 var ebox = c.select('.fc-day-content',true).first().getBox();
11828                 //Roo.log(cg);
11829                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
11830                 cg.setWidth(ebox.right - sbox.x -2);
11831
11832                 cg.on('click', _this.onMoreEventClick, _this, c.more);
11833                 
11834             }
11835             
11836         });
11837         
11838         
11839         
11840     },
11841     
11842     onEventEnter: function (e, el,event,d) {
11843         this.fireEvent('evententer', this, el, event);
11844     },
11845     
11846     onEventLeave: function (e, el,event,d) {
11847         this.fireEvent('eventleave', this, el, event);
11848     },
11849     
11850     onEventClick: function (e, el,event,d) {
11851         this.fireEvent('eventclick', this, el, event);
11852     },
11853     
11854     onMonthChange: function () {
11855         this.store.load();
11856     },
11857     
11858     onMoreEventClick: function(e, el, more)
11859     {
11860         var _this = this;
11861         
11862         this.calpopover.placement = 'right';
11863         this.calpopover.setTitle('More');
11864         
11865         this.calpopover.setContent('');
11866         
11867         var ctr = this.calpopover.el.select('.popover-content', true).first();
11868         
11869         Roo.each(more, function(m){
11870             var cfg = {
11871                 cls : 'fc-event-hori fc-event-draggable',
11872                 html : m.title
11873             }
11874             var cg = ctr.createChild(cfg);
11875             
11876             cg.on('click', _this.onEventClick, _this, m);
11877         });
11878         
11879         this.calpopover.show(el);
11880         
11881         
11882     },
11883     
11884     onLoad: function () 
11885     {   
11886         this.calevents = [];
11887         var cal = this;
11888         
11889         if(this.store.getCount() > 0){
11890             this.store.data.each(function(d){
11891                cal.addItem({
11892                     id : d.data.id,
11893                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11894                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11895                     time : d.data.start_time,
11896                     title : d.data.title,
11897                     description : d.data.description,
11898                     venue : d.data.venue
11899                 });
11900             });
11901         }
11902         
11903         this.renderEvents();
11904         
11905         if(this.calevents.length && this.loadMask){
11906             this.maskEl.hide();
11907         }
11908     },
11909     
11910     onBeforeLoad: function()
11911     {
11912         this.clearEvents();
11913         if(this.loadMask){
11914             this.maskEl.show();
11915         }
11916     }
11917 });
11918
11919  
11920  /*
11921  * - LGPL
11922  *
11923  * element
11924  * 
11925  */
11926
11927 /**
11928  * @class Roo.bootstrap.Popover
11929  * @extends Roo.bootstrap.Component
11930  * Bootstrap Popover class
11931  * @cfg {String} html contents of the popover   (or false to use children..)
11932  * @cfg {String} title of popover (or false to hide)
11933  * @cfg {String} placement how it is placed
11934  * @cfg {String} trigger click || hover (or false to trigger manually)
11935  * @cfg {String} over what (parent or false to trigger manually.)
11936  * 
11937  * @constructor
11938  * Create a new Popover
11939  * @param {Object} config The config object
11940  */
11941
11942 Roo.bootstrap.Popover = function(config){
11943     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11944 };
11945
11946 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
11947     
11948     title: 'Fill in a title',
11949     html: false,
11950     
11951     placement : 'right',
11952     trigger : 'hover', // hover
11953     
11954     over: 'parent',
11955     
11956     can_build_overlaid : false,
11957     
11958     getChildContainer : function()
11959     {
11960         return this.el.select('.popover-content',true).first();
11961     },
11962     
11963     getAutoCreate : function(){
11964          Roo.log('make popover?');
11965         var cfg = {
11966            cls : 'popover roo-dynamic',
11967            style: 'display:block',
11968            cn : [
11969                 {
11970                     cls : 'arrow'
11971                 },
11972                 {
11973                     cls : 'popover-inner',
11974                     cn : [
11975                         {
11976                             tag: 'h3',
11977                             cls: 'popover-title',
11978                             html : this.title
11979                         },
11980                         {
11981                             cls : 'popover-content',
11982                             html : this.html
11983                         }
11984                     ]
11985                     
11986                 }
11987            ]
11988         };
11989         
11990         return cfg;
11991     },
11992     setTitle: function(str)
11993     {
11994         this.el.select('.popover-title',true).first().dom.innerHTML = str;
11995     },
11996     setContent: function(str)
11997     {
11998         this.el.select('.popover-content',true).first().dom.innerHTML = str;
11999     },
12000     // as it get's added to the bottom of the page.
12001     onRender : function(ct, position)
12002     {
12003         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12004         if(!this.el){
12005             var cfg = Roo.apply({},  this.getAutoCreate());
12006             cfg.id = Roo.id();
12007             
12008             if (this.cls) {
12009                 cfg.cls += ' ' + this.cls;
12010             }
12011             if (this.style) {
12012                 cfg.style = this.style;
12013             }
12014             Roo.log("adding to ")
12015             this.el = Roo.get(document.body).createChild(cfg, position);
12016             Roo.log(this.el);
12017         }
12018         this.initEvents();
12019     },
12020     
12021     initEvents : function()
12022     {
12023         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12024         this.el.enableDisplayMode('block');
12025         this.el.hide();
12026         if (this.over === false) {
12027             return; 
12028         }
12029         if (this.triggers === false) {
12030             return;
12031         }
12032         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12033         var triggers = this.trigger ? this.trigger.split(' ') : [];
12034         Roo.each(triggers, function(trigger) {
12035         
12036             if (trigger == 'click') {
12037                 on_el.on('click', this.toggle, this);
12038             } else if (trigger != 'manual') {
12039                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12040                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12041       
12042                 on_el.on(eventIn  ,this.enter, this);
12043                 on_el.on(eventOut, this.leave, this);
12044             }
12045         }, this);
12046         
12047     },
12048     
12049     
12050     // private
12051     timeout : null,
12052     hoverState : null,
12053     
12054     toggle : function () {
12055         this.hoverState == 'in' ? this.leave() : this.enter();
12056     },
12057     
12058     enter : function () {
12059        
12060     
12061         clearTimeout(this.timeout);
12062     
12063         this.hoverState = 'in'
12064     
12065         if (!this.delay || !this.delay.show) {
12066             this.show();
12067             return 
12068         }
12069         var _t = this;
12070         this.timeout = setTimeout(function () {
12071             if (_t.hoverState == 'in') {
12072                 _t.show();
12073             }
12074         }, this.delay.show)
12075     },
12076     leave : function() {
12077         clearTimeout(this.timeout);
12078     
12079         this.hoverState = 'out'
12080     
12081         if (!this.delay || !this.delay.hide) {
12082             this.hide();
12083             return 
12084         }
12085         var _t = this;
12086         this.timeout = setTimeout(function () {
12087             if (_t.hoverState == 'out') {
12088                 _t.hide();
12089             }
12090         }, this.delay.hide)
12091     },
12092     
12093     show : function (on_el)
12094     {
12095         if (!on_el) {
12096             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12097         }
12098         // set content.
12099         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12100         if (this.html !== false) {
12101             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12102         }
12103         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12104         if (!this.title.length) {
12105             this.el.select('.popover-title',true).hide();
12106         }
12107         
12108         var placement = typeof this.placement == 'function' ?
12109             this.placement.call(this, this.el, on_el) :
12110             this.placement;
12111             
12112         var autoToken = /\s?auto?\s?/i;
12113         var autoPlace = autoToken.test(placement);
12114         if (autoPlace) {
12115             placement = placement.replace(autoToken, '') || 'top';
12116         }
12117         
12118         //this.el.detach()
12119         //this.el.setXY([0,0]);
12120         this.el.show();
12121         this.el.dom.style.display='block';
12122         this.el.addClass(placement);
12123         
12124         //this.el.appendTo(on_el);
12125         
12126         var p = this.getPosition();
12127         var box = this.el.getBox();
12128         
12129         if (autoPlace) {
12130             // fixme..
12131         }
12132         var align = Roo.bootstrap.Popover.alignment[placement]
12133         this.el.alignTo(on_el, align[0],align[1]);
12134         //var arrow = this.el.select('.arrow',true).first();
12135         //arrow.set(align[2], 
12136         
12137         this.el.addClass('in');
12138         this.hoverState = null;
12139         
12140         if (this.el.hasClass('fade')) {
12141             // fade it?
12142         }
12143         
12144     },
12145     hide : function()
12146     {
12147         this.el.setXY([0,0]);
12148         this.el.removeClass('in');
12149         this.el.hide();
12150         
12151     }
12152     
12153 });
12154
12155 Roo.bootstrap.Popover.alignment = {
12156     'left' : ['r-l', [-10,0], 'right'],
12157     'right' : ['l-r', [10,0], 'left'],
12158     'bottom' : ['t-b', [0,10], 'top'],
12159     'top' : [ 'b-t', [0,-10], 'bottom']
12160 };
12161
12162  /*
12163  * - LGPL
12164  *
12165  * Progress
12166  * 
12167  */
12168
12169 /**
12170  * @class Roo.bootstrap.Progress
12171  * @extends Roo.bootstrap.Component
12172  * Bootstrap Progress class
12173  * @cfg {Boolean} striped striped of the progress bar
12174  * @cfg {Boolean} active animated of the progress bar
12175  * 
12176  * 
12177  * @constructor
12178  * Create a new Progress
12179  * @param {Object} config The config object
12180  */
12181
12182 Roo.bootstrap.Progress = function(config){
12183     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12184 };
12185
12186 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12187     
12188     striped : false,
12189     active: false,
12190     
12191     getAutoCreate : function(){
12192         var cfg = {
12193             tag: 'div',
12194             cls: 'progress'
12195         };
12196         
12197         
12198         if(this.striped){
12199             cfg.cls += ' progress-striped';
12200         }
12201       
12202         if(this.active){
12203             cfg.cls += ' active';
12204         }
12205         
12206         
12207         return cfg;
12208     }
12209    
12210 });
12211
12212  
12213
12214  /*
12215  * - LGPL
12216  *
12217  * ProgressBar
12218  * 
12219  */
12220
12221 /**
12222  * @class Roo.bootstrap.ProgressBar
12223  * @extends Roo.bootstrap.Component
12224  * Bootstrap ProgressBar class
12225  * @cfg {Number} aria_valuenow aria-value now
12226  * @cfg {Number} aria_valuemin aria-value min
12227  * @cfg {Number} aria_valuemax aria-value max
12228  * @cfg {String} label label for the progress bar
12229  * @cfg {String} panel (success | info | warning | danger )
12230  * @cfg {String} role role of the progress bar
12231  * @cfg {String} sr_only text
12232  * 
12233  * 
12234  * @constructor
12235  * Create a new ProgressBar
12236  * @param {Object} config The config object
12237  */
12238
12239 Roo.bootstrap.ProgressBar = function(config){
12240     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12241 };
12242
12243 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12244     
12245     aria_valuenow : 0,
12246     aria_valuemin : 0,
12247     aria_valuemax : 100,
12248     label : false,
12249     panel : false,
12250     role : false,
12251     sr_only: false,
12252     
12253     getAutoCreate : function()
12254     {
12255         
12256         var cfg = {
12257             tag: 'div',
12258             cls: 'progress-bar',
12259             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12260         };
12261         
12262         if(this.sr_only){
12263             cfg.cn = {
12264                 tag: 'span',
12265                 cls: 'sr-only',
12266                 html: this.sr_only
12267             }
12268         }
12269         
12270         if(this.role){
12271             cfg.role = this.role;
12272         }
12273         
12274         if(this.aria_valuenow){
12275             cfg['aria-valuenow'] = this.aria_valuenow;
12276         }
12277         
12278         if(this.aria_valuemin){
12279             cfg['aria-valuemin'] = this.aria_valuemin;
12280         }
12281         
12282         if(this.aria_valuemax){
12283             cfg['aria-valuemax'] = this.aria_valuemax;
12284         }
12285         
12286         if(this.label && !this.sr_only){
12287             cfg.html = this.label;
12288         }
12289         
12290         if(this.panel){
12291             cfg.cls += ' progress-bar-' + this.panel;
12292         }
12293         
12294         return cfg;
12295     },
12296     
12297     update : function(aria_valuenow)
12298     {
12299         this.aria_valuenow = aria_valuenow;
12300         
12301         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12302     }
12303    
12304 });
12305
12306  
12307
12308  /*
12309  * - LGPL
12310  *
12311  * TabPanel
12312  * 
12313  */
12314
12315 /**
12316  * @class Roo.bootstrap.TabPanel
12317  * @extends Roo.bootstrap.Component
12318  * Bootstrap TabPanel class
12319  * @cfg {Boolean} active panel active
12320  * @cfg {String} html panel content
12321  * @cfg {String} tabId tab relate id
12322  * @cfg {String} navId The navbar which triggers show hide
12323  * 
12324  * 
12325  * @constructor
12326  * Create a new TabPanel
12327  * @param {Object} config The config object
12328  */
12329
12330 Roo.bootstrap.TabPanel = function(config){
12331     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12332      this.addEvents({
12333         /**
12334              * @event changed
12335              * Fires when the active status changes
12336              * @param {Roo.bootstrap.TabPanel} this
12337              * @param {Boolean} state the new state
12338             
12339          */
12340         'changed': true
12341      });
12342 };
12343
12344 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
12345     
12346     active: false,
12347     html: false,
12348     tabId: false,
12349     navId : false,
12350     
12351     getAutoCreate : function(){
12352         var cfg = {
12353             tag: 'div',
12354             cls: 'tab-pane',
12355             html: this.html || ''
12356         };
12357         
12358         if(this.active){
12359             cfg.cls += ' active';
12360         }
12361         
12362         if(this.tabId){
12363             cfg.tabId = this.tabId;
12364         }
12365         
12366         return cfg;
12367     },
12368     onRender : function(ct, position)
12369     {
12370        // Roo.log("Call onRender: " + this.xtype);
12371         
12372         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12373         
12374         if (this.navId && this.tabId) {
12375             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12376             if (!item) {
12377                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
12378             } else {
12379                 item.on('changed', function(item, state) {
12380                     this.setActive(state);
12381                 }, this);
12382             }
12383         }
12384         
12385     },
12386     setActive: function(state)
12387     {
12388         Roo.log("panel - set active " + this.tabId + "=" + state);
12389         
12390         this.active = state;
12391         if (!state) {
12392             this.el.removeClass('active');
12393             
12394         } else  if (!this.el.hasClass('active')) {
12395             this.el.addClass('active');
12396         }
12397         this.fireEvent('changed', this, state);
12398     }
12399     
12400     
12401 });
12402  
12403
12404  
12405
12406  /*
12407  * - LGPL
12408  *
12409  * DateField
12410  * 
12411  */
12412
12413 /**
12414  * @class Roo.bootstrap.DateField
12415  * @extends Roo.bootstrap.Input
12416  * Bootstrap DateField class
12417  * @cfg {Number} weekStart default 0
12418  * @cfg {Number} weekStart default 0
12419  * @cfg {Number} viewMode default empty, (months|years)
12420  * @cfg {Number} minViewMode default empty, (months|years)
12421  * @cfg {Number} startDate default -Infinity
12422  * @cfg {Number} endDate default Infinity
12423  * @cfg {Boolean} todayHighlight default false
12424  * @cfg {Boolean} todayBtn default false
12425  * @cfg {Boolean} calendarWeeks default false
12426  * @cfg {Object} daysOfWeekDisabled default empty
12427  * 
12428  * @cfg {Boolean} keyboardNavigation default true
12429  * @cfg {String} language default en
12430  * 
12431  * @constructor
12432  * Create a new DateField
12433  * @param {Object} config The config object
12434  */
12435
12436 Roo.bootstrap.DateField = function(config){
12437     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12438      this.addEvents({
12439             /**
12440              * @event show
12441              * Fires when this field show.
12442              * @param {Roo.bootstrap.DateField} this
12443              * @param {Mixed} date The date value
12444              */
12445             show : true,
12446             /**
12447              * @event show
12448              * Fires when this field hide.
12449              * @param {Roo.bootstrap.DateField} this
12450              * @param {Mixed} date The date value
12451              */
12452             hide : true,
12453             /**
12454              * @event select
12455              * Fires when select a date.
12456              * @param {Roo.bootstrap.DateField} this
12457              * @param {Mixed} date The date value
12458              */
12459             select : true
12460         });
12461 };
12462
12463 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
12464     
12465     /**
12466      * @cfg {String} format
12467      * The default date format string which can be overriden for localization support.  The format must be
12468      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12469      */
12470     format : "m/d/y",
12471     /**
12472      * @cfg {String} altFormats
12473      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12474      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12475      */
12476     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12477     
12478     weekStart : 0,
12479     
12480     viewMode : '',
12481     
12482     minViewMode : '',
12483     
12484     todayHighlight : false,
12485     
12486     todayBtn: false,
12487     
12488     language: 'en',
12489     
12490     keyboardNavigation: true,
12491     
12492     calendarWeeks: false,
12493     
12494     startDate: -Infinity,
12495     
12496     endDate: Infinity,
12497     
12498     daysOfWeekDisabled: [],
12499     
12500     _events: [],
12501     
12502     UTCDate: function()
12503     {
12504         return new Date(Date.UTC.apply(Date, arguments));
12505     },
12506     
12507     UTCToday: function()
12508     {
12509         var today = new Date();
12510         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12511     },
12512     
12513     getDate: function() {
12514             var d = this.getUTCDate();
12515             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12516     },
12517     
12518     getUTCDate: function() {
12519             return this.date;
12520     },
12521     
12522     setDate: function(d) {
12523             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12524     },
12525     
12526     setUTCDate: function(d) {
12527             this.date = d;
12528             this.setValue(this.formatDate(this.date));
12529     },
12530         
12531     onRender: function(ct, position)
12532     {
12533         
12534         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12535         
12536         this.language = this.language || 'en';
12537         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12538         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12539         
12540         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12541         this.format = this.format || 'm/d/y';
12542         this.isInline = false;
12543         this.isInput = true;
12544         this.component = this.el.select('.add-on', true).first() || false;
12545         this.component = (this.component && this.component.length === 0) ? false : this.component;
12546         this.hasInput = this.component && this.inputEL().length;
12547         
12548         if (typeof(this.minViewMode === 'string')) {
12549             switch (this.minViewMode) {
12550                 case 'months':
12551                     this.minViewMode = 1;
12552                     break;
12553                 case 'years':
12554                     this.minViewMode = 2;
12555                     break;
12556                 default:
12557                     this.minViewMode = 0;
12558                     break;
12559             }
12560         }
12561         
12562         if (typeof(this.viewMode === 'string')) {
12563             switch (this.viewMode) {
12564                 case 'months':
12565                     this.viewMode = 1;
12566                     break;
12567                 case 'years':
12568                     this.viewMode = 2;
12569                     break;
12570                 default:
12571                     this.viewMode = 0;
12572                     break;
12573             }
12574         }
12575                 
12576         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12577         
12578         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12579         
12580         this.picker().on('mousedown', this.onMousedown, this);
12581         this.picker().on('click', this.onClick, this);
12582         
12583         this.picker().addClass('datepicker-dropdown');
12584         
12585         this.startViewMode = this.viewMode;
12586         
12587         
12588         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12589             if(!this.calendarWeeks){
12590                 v.remove();
12591                 return;
12592             };
12593             
12594             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12595             v.attr('colspan', function(i, val){
12596                 return parseInt(val) + 1;
12597             });
12598         })
12599                         
12600         
12601         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12602         
12603         this.setStartDate(this.startDate);
12604         this.setEndDate(this.endDate);
12605         
12606         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12607         
12608         this.fillDow();
12609         this.fillMonths();
12610         this.update();
12611         this.showMode();
12612         
12613         if(this.isInline) {
12614             this.show();
12615         }
12616     },
12617     
12618     picker : function()
12619     {
12620         return this.el.select('.datepicker', true).first();
12621     },
12622     
12623     fillDow: function()
12624     {
12625         var dowCnt = this.weekStart;
12626         
12627         var dow = {
12628             tag: 'tr',
12629             cn: [
12630                 
12631             ]
12632         };
12633         
12634         if(this.calendarWeeks){
12635             dow.cn.push({
12636                 tag: 'th',
12637                 cls: 'cw',
12638                 html: '&nbsp;'
12639             })
12640         }
12641         
12642         while (dowCnt < this.weekStart + 7) {
12643             dow.cn.push({
12644                 tag: 'th',
12645                 cls: 'dow',
12646                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12647             });
12648         }
12649         
12650         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12651     },
12652     
12653     fillMonths: function()
12654     {    
12655         var i = 0
12656         var months = this.picker().select('>.datepicker-months td', true).first();
12657         
12658         months.dom.innerHTML = '';
12659         
12660         while (i < 12) {
12661             var month = {
12662                 tag: 'span',
12663                 cls: 'month',
12664                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12665             }
12666             
12667             months.createChild(month);
12668         }
12669         
12670     },
12671     
12672     update: function(){
12673         
12674         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12675         
12676         if (this.date < this.startDate) {
12677             this.viewDate = new Date(this.startDate);
12678         } else if (this.date > this.endDate) {
12679             this.viewDate = new Date(this.endDate);
12680         } else {
12681             this.viewDate = new Date(this.date);
12682         }
12683         
12684         this.fill();
12685     },
12686     
12687     fill: function() {
12688         var d = new Date(this.viewDate),
12689                 year = d.getUTCFullYear(),
12690                 month = d.getUTCMonth(),
12691                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12692                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12693                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12694                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12695                 currentDate = this.date && this.date.valueOf(),
12696                 today = this.UTCToday();
12697         
12698         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12699         
12700 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12701         
12702 //        this.picker.select('>tfoot th.today').
12703 //                                              .text(dates[this.language].today)
12704 //                                              .toggle(this.todayBtn !== false);
12705     
12706         this.updateNavArrows();
12707         this.fillMonths();
12708                                                 
12709         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12710         
12711         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12712          
12713         prevMonth.setUTCDate(day);
12714         
12715         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12716         
12717         var nextMonth = new Date(prevMonth);
12718         
12719         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12720         
12721         nextMonth = nextMonth.valueOf();
12722         
12723         var fillMonths = false;
12724         
12725         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12726         
12727         while(prevMonth.valueOf() < nextMonth) {
12728             var clsName = '';
12729             
12730             if (prevMonth.getUTCDay() === this.weekStart) {
12731                 if(fillMonths){
12732                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12733                 }
12734                     
12735                 fillMonths = {
12736                     tag: 'tr',
12737                     cn: []
12738                 };
12739                 
12740                 if(this.calendarWeeks){
12741                     // ISO 8601: First week contains first thursday.
12742                     // ISO also states week starts on Monday, but we can be more abstract here.
12743                     var
12744                     // Start of current week: based on weekstart/current date
12745                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12746                     // Thursday of this week
12747                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12748                     // First Thursday of year, year from thursday
12749                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12750                     // Calendar week: ms between thursdays, div ms per day, div 7 days
12751                     calWeek =  (th - yth) / 864e5 / 7 + 1;
12752                     
12753                     fillMonths.cn.push({
12754                         tag: 'td',
12755                         cls: 'cw',
12756                         html: calWeek
12757                     });
12758                 }
12759             }
12760             
12761             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12762                 clsName += ' old';
12763             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12764                 clsName += ' new';
12765             }
12766             if (this.todayHighlight &&
12767                 prevMonth.getUTCFullYear() == today.getFullYear() &&
12768                 prevMonth.getUTCMonth() == today.getMonth() &&
12769                 prevMonth.getUTCDate() == today.getDate()) {
12770                 clsName += ' today';
12771             }
12772             
12773             if (currentDate && prevMonth.valueOf() === currentDate) {
12774                 clsName += ' active';
12775             }
12776             
12777             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12778                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12779                     clsName += ' disabled';
12780             }
12781             
12782             fillMonths.cn.push({
12783                 tag: 'td',
12784                 cls: 'day ' + clsName,
12785                 html: prevMonth.getDate()
12786             })
12787             
12788             prevMonth.setDate(prevMonth.getDate()+1);
12789         }
12790           
12791         var currentYear = this.date && this.date.getUTCFullYear();
12792         var currentMonth = this.date && this.date.getUTCMonth();
12793         
12794         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12795         
12796         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12797             v.removeClass('active');
12798             
12799             if(currentYear === year && k === currentMonth){
12800                 v.addClass('active');
12801             }
12802             
12803             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12804                 v.addClass('disabled');
12805             }
12806             
12807         });
12808         
12809         
12810         year = parseInt(year/10, 10) * 10;
12811         
12812         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12813         
12814         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12815         
12816         year -= 1;
12817         for (var i = -1; i < 11; i++) {
12818             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12819                 tag: 'span',
12820                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12821                 html: year
12822             })
12823             
12824             year += 1;
12825         }
12826     },
12827     
12828     showMode: function(dir) {
12829         if (dir) {
12830             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12831         }
12832         Roo.each(this.picker().select('>div',true).elements, function(v){
12833             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12834             v.hide();
12835         });
12836         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12837     },
12838     
12839     place: function()
12840     {
12841         if(this.isInline) return;
12842         
12843         this.picker().removeClass(['bottom', 'top']);
12844         
12845         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12846             /*
12847              * place to the top of element!
12848              *
12849              */
12850             
12851             this.picker().addClass('top');
12852             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12853             
12854             return;
12855         }
12856         
12857         this.picker().addClass('bottom');
12858         
12859         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12860     },
12861     
12862     parseDate : function(value){
12863         if(!value || value instanceof Date){
12864             return value;
12865         }
12866         var v = Date.parseDate(value, this.format);
12867         if (!v && this.useIso) {
12868             v = Date.parseDate(value, 'Y-m-d');
12869         }
12870         if(!v && this.altFormats){
12871             if(!this.altFormatsArray){
12872                 this.altFormatsArray = this.altFormats.split("|");
12873             }
12874             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12875                 v = Date.parseDate(value, this.altFormatsArray[i]);
12876             }
12877         }
12878         return v;
12879     },
12880     
12881     formatDate : function(date, fmt){
12882         return (!date || !(date instanceof Date)) ?
12883         date : date.dateFormat(fmt || this.format);
12884     },
12885     
12886     onFocus : function()
12887     {
12888         Roo.bootstrap.DateField.superclass.onFocus.call(this);
12889         this.show();
12890     },
12891     
12892     onBlur : function()
12893     {
12894         Roo.bootstrap.DateField.superclass.onBlur.call(this);
12895         this.hide();
12896     },
12897     
12898     show : function()
12899     {
12900         this.picker().show();
12901         this.update();
12902         this.place();
12903         
12904         this.fireEvent('show', this, this.date);
12905     },
12906     
12907     hide : function()
12908     {
12909         if(this.isInline) return;
12910         this.picker().hide();
12911         this.viewMode = this.startViewMode;
12912         this.showMode();
12913         
12914         this.fireEvent('hide', this, this.date);
12915         
12916     },
12917     
12918     onMousedown: function(e){
12919         e.stopPropagation();
12920         e.preventDefault();
12921     },
12922     
12923     keyup: function(e){
12924         Roo.bootstrap.DateField.superclass.keyup.call(this);
12925         this.update();
12926         
12927     },
12928
12929     setValue: function(v){
12930         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12931         
12932         this.fireEvent('select', this, this.date);
12933         
12934     },
12935     
12936     fireKey: function(e){
12937         if (!this.picker().isVisible()){
12938             if (e.keyCode == 27) // allow escape to hide and re-show picker
12939                 this.show();
12940             return;
12941         }
12942         var dateChanged = false,
12943         dir, day, month,
12944         newDate, newViewDate;
12945         switch(e.keyCode){
12946             case 27: // escape
12947                 this.hide();
12948                 e.preventDefault();
12949                 break;
12950             case 37: // left
12951             case 39: // right
12952                 if (!this.keyboardNavigation) break;
12953                 dir = e.keyCode == 37 ? -1 : 1;
12954                 
12955                 if (e.ctrlKey){
12956                     newDate = this.moveYear(this.date, dir);
12957                     newViewDate = this.moveYear(this.viewDate, dir);
12958                 } else if (e.shiftKey){
12959                     newDate = this.moveMonth(this.date, dir);
12960                     newViewDate = this.moveMonth(this.viewDate, dir);
12961                 } else {
12962                     newDate = new Date(this.date);
12963                     newDate.setUTCDate(this.date.getUTCDate() + dir);
12964                     newViewDate = new Date(this.viewDate);
12965                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12966                 }
12967                 if (this.dateWithinRange(newDate)){
12968                     this.date = newDate;
12969                     this.viewDate = newViewDate;
12970                     this.setValue(this.formatDate(this.date));
12971                     this.update();
12972                     e.preventDefault();
12973                     dateChanged = true;
12974                 }
12975                 break;
12976             case 38: // up
12977             case 40: // down
12978                 if (!this.keyboardNavigation) break;
12979                 dir = e.keyCode == 38 ? -1 : 1;
12980                 if (e.ctrlKey){
12981                     newDate = this.moveYear(this.date, dir);
12982                     newViewDate = this.moveYear(this.viewDate, dir);
12983                 } else if (e.shiftKey){
12984                     newDate = this.moveMonth(this.date, dir);
12985                     newViewDate = this.moveMonth(this.viewDate, dir);
12986                 } else {
12987                     newDate = new Date(this.date);
12988                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12989                     newViewDate = new Date(this.viewDate);
12990                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12991                 }
12992                 if (this.dateWithinRange(newDate)){
12993                     this.date = newDate;
12994                     this.viewDate = newViewDate;
12995                     this.setValue(this.formatDate(this.date));
12996                     this.update();
12997                     e.preventDefault();
12998                     dateChanged = true;
12999                 }
13000                 break;
13001             case 13: // enter
13002                 this.setValue(this.formatDate(this.date));
13003                 this.hide();
13004                 e.preventDefault();
13005                 break;
13006             case 9: // tab
13007                 this.setValue(this.formatDate(this.date));
13008                 this.hide();
13009                 break;
13010         }
13011     },
13012     
13013     
13014     onClick: function(e) {
13015         e.stopPropagation();
13016         e.preventDefault();
13017         
13018         var target = e.getTarget();
13019         
13020         if(target.nodeName.toLowerCase() === 'i'){
13021             target = Roo.get(target).dom.parentNode;
13022         }
13023         
13024         var nodeName = target.nodeName;
13025         var className = target.className;
13026         var html = target.innerHTML;
13027         
13028         switch(nodeName.toLowerCase()) {
13029             case 'th':
13030                 switch(className) {
13031                     case 'switch':
13032                         this.showMode(1);
13033                         break;
13034                     case 'prev':
13035                     case 'next':
13036                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13037                         switch(this.viewMode){
13038                                 case 0:
13039                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13040                                         break;
13041                                 case 1:
13042                                 case 2:
13043                                         this.viewDate = this.moveYear(this.viewDate, dir);
13044                                         break;
13045                         }
13046                         this.fill();
13047                         break;
13048                     case 'today':
13049                         var date = new Date();
13050                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13051                         this.fill()
13052                         this.setValue(this.formatDate(this.date));
13053                         this.hide();
13054                         break;
13055                 }
13056                 break;
13057             case 'span':
13058                 if (className.indexOf('disabled') === -1) {
13059                     this.viewDate.setUTCDate(1);
13060                     if (className.indexOf('month') !== -1) {
13061                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13062                     } else {
13063                         var year = parseInt(html, 10) || 0;
13064                         this.viewDate.setUTCFullYear(year);
13065                         
13066                     }
13067                     this.showMode(-1);
13068                     this.fill();
13069                 }
13070                 break;
13071                 
13072             case 'td':
13073                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13074                     var day = parseInt(html, 10) || 1;
13075                     var year = this.viewDate.getUTCFullYear(),
13076                         month = this.viewDate.getUTCMonth();
13077
13078                     if (className.indexOf('old') !== -1) {
13079                         if(month === 0 ){
13080                             month = 11;
13081                             year -= 1;
13082                         }else{
13083                             month -= 1;
13084                         }
13085                     } else if (className.indexOf('new') !== -1) {
13086                         if (month == 11) {
13087                             month = 0;
13088                             year += 1;
13089                         } else {
13090                             month += 1;
13091                         }
13092                     }
13093                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13094                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13095                     this.fill();
13096                     this.setValue(this.formatDate(this.date));
13097                     this.hide();
13098                 }
13099                 break;
13100         }
13101     },
13102     
13103     setStartDate: function(startDate){
13104         this.startDate = startDate || -Infinity;
13105         if (this.startDate !== -Infinity) {
13106             this.startDate = this.parseDate(this.startDate);
13107         }
13108         this.update();
13109         this.updateNavArrows();
13110     },
13111
13112     setEndDate: function(endDate){
13113         this.endDate = endDate || Infinity;
13114         if (this.endDate !== Infinity) {
13115             this.endDate = this.parseDate(this.endDate);
13116         }
13117         this.update();
13118         this.updateNavArrows();
13119     },
13120     
13121     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13122         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13123         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13124             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13125         }
13126         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13127             return parseInt(d, 10);
13128         });
13129         this.update();
13130         this.updateNavArrows();
13131     },
13132     
13133     updateNavArrows: function() {
13134         var d = new Date(this.viewDate),
13135         year = d.getUTCFullYear(),
13136         month = d.getUTCMonth();
13137         
13138         Roo.each(this.picker().select('.prev', true).elements, function(v){
13139             v.show();
13140             switch (this.viewMode) {
13141                 case 0:
13142
13143                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13144                         v.hide();
13145                     }
13146                     break;
13147                 case 1:
13148                 case 2:
13149                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13150                         v.hide();
13151                     }
13152                     break;
13153             }
13154         });
13155         
13156         Roo.each(this.picker().select('.next', true).elements, function(v){
13157             v.show();
13158             switch (this.viewMode) {
13159                 case 0:
13160
13161                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13162                         v.hide();
13163                     }
13164                     break;
13165                 case 1:
13166                 case 2:
13167                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13168                         v.hide();
13169                     }
13170                     break;
13171             }
13172         })
13173     },
13174     
13175     moveMonth: function(date, dir){
13176         if (!dir) return date;
13177         var new_date = new Date(date.valueOf()),
13178         day = new_date.getUTCDate(),
13179         month = new_date.getUTCMonth(),
13180         mag = Math.abs(dir),
13181         new_month, test;
13182         dir = dir > 0 ? 1 : -1;
13183         if (mag == 1){
13184             test = dir == -1
13185             // If going back one month, make sure month is not current month
13186             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13187             ? function(){
13188                 return new_date.getUTCMonth() == month;
13189             }
13190             // If going forward one month, make sure month is as expected
13191             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13192             : function(){
13193                 return new_date.getUTCMonth() != new_month;
13194             };
13195             new_month = month + dir;
13196             new_date.setUTCMonth(new_month);
13197             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13198             if (new_month < 0 || new_month > 11)
13199                 new_month = (new_month + 12) % 12;
13200         } else {
13201             // For magnitudes >1, move one month at a time...
13202             for (var i=0; i<mag; i++)
13203                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13204                 new_date = this.moveMonth(new_date, dir);
13205             // ...then reset the day, keeping it in the new month
13206             new_month = new_date.getUTCMonth();
13207             new_date.setUTCDate(day);
13208             test = function(){
13209                 return new_month != new_date.getUTCMonth();
13210             };
13211         }
13212         // Common date-resetting loop -- if date is beyond end of month, make it
13213         // end of month
13214         while (test()){
13215             new_date.setUTCDate(--day);
13216             new_date.setUTCMonth(new_month);
13217         }
13218         return new_date;
13219     },
13220
13221     moveYear: function(date, dir){
13222         return this.moveMonth(date, dir*12);
13223     },
13224
13225     dateWithinRange: function(date){
13226         return date >= this.startDate && date <= this.endDate;
13227     },
13228
13229     
13230     remove: function() {
13231         this.picker().remove();
13232     }
13233    
13234 });
13235
13236 Roo.apply(Roo.bootstrap.DateField,  {
13237     
13238     head : {
13239         tag: 'thead',
13240         cn: [
13241         {
13242             tag: 'tr',
13243             cn: [
13244             {
13245                 tag: 'th',
13246                 cls: 'prev',
13247                 html: '<i class="icon-arrow-left"/>'
13248             },
13249             {
13250                 tag: 'th',
13251                 cls: 'switch',
13252                 colspan: '5'
13253             },
13254             {
13255                 tag: 'th',
13256                 cls: 'next',
13257                 html: '<i class="icon-arrow-right"/>'
13258             }
13259
13260             ]
13261         }
13262         ]
13263     },
13264     
13265     content : {
13266         tag: 'tbody',
13267         cn: [
13268         {
13269             tag: 'tr',
13270             cn: [
13271             {
13272                 tag: 'td',
13273                 colspan: '7'
13274             }
13275             ]
13276         }
13277         ]
13278     },
13279     
13280     footer : {
13281         tag: 'tfoot',
13282         cn: [
13283         {
13284             tag: 'tr',
13285             cn: [
13286             {
13287                 tag: 'th',
13288                 colspan: '7',
13289                 cls: 'today'
13290             }
13291                     
13292             ]
13293         }
13294         ]
13295     },
13296     
13297     dates:{
13298         en: {
13299             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13300             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13301             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13302             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13303             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13304             today: "Today"
13305         }
13306     },
13307     
13308     modes: [
13309     {
13310         clsName: 'days',
13311         navFnc: 'Month',
13312         navStep: 1
13313     },
13314     {
13315         clsName: 'months',
13316         navFnc: 'FullYear',
13317         navStep: 1
13318     },
13319     {
13320         clsName: 'years',
13321         navFnc: 'FullYear',
13322         navStep: 10
13323     }]
13324 });
13325
13326 Roo.apply(Roo.bootstrap.DateField,  {
13327   
13328     template : {
13329         tag: 'div',
13330         cls: 'datepicker dropdown-menu',
13331         cn: [
13332         {
13333             tag: 'div',
13334             cls: 'datepicker-days',
13335             cn: [
13336             {
13337                 tag: 'table',
13338                 cls: 'table-condensed',
13339                 cn:[
13340                 Roo.bootstrap.DateField.head,
13341                 {
13342                     tag: 'tbody'
13343                 },
13344                 Roo.bootstrap.DateField.footer
13345                 ]
13346             }
13347             ]
13348         },
13349         {
13350             tag: 'div',
13351             cls: 'datepicker-months',
13352             cn: [
13353             {
13354                 tag: 'table',
13355                 cls: 'table-condensed',
13356                 cn:[
13357                 Roo.bootstrap.DateField.head,
13358                 Roo.bootstrap.DateField.content,
13359                 Roo.bootstrap.DateField.footer
13360                 ]
13361             }
13362             ]
13363         },
13364         {
13365             tag: 'div',
13366             cls: 'datepicker-years',
13367             cn: [
13368             {
13369                 tag: 'table',
13370                 cls: 'table-condensed',
13371                 cn:[
13372                 Roo.bootstrap.DateField.head,
13373                 Roo.bootstrap.DateField.content,
13374                 Roo.bootstrap.DateField.footer
13375                 ]
13376             }
13377             ]
13378         }
13379         ]
13380     }
13381 });
13382
13383  
13384
13385  /*
13386  * - LGPL
13387  *
13388  * TimeField
13389  * 
13390  */
13391
13392 /**
13393  * @class Roo.bootstrap.TimeField
13394  * @extends Roo.bootstrap.Input
13395  * Bootstrap DateField class
13396  * 
13397  * 
13398  * @constructor
13399  * Create a new TimeField
13400  * @param {Object} config The config object
13401  */
13402
13403 Roo.bootstrap.TimeField = function(config){
13404     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13405     this.addEvents({
13406             /**
13407              * @event show
13408              * Fires when this field show.
13409              * @param {Roo.bootstrap.DateField} this
13410              * @param {Mixed} date The date value
13411              */
13412             show : true,
13413             /**
13414              * @event show
13415              * Fires when this field hide.
13416              * @param {Roo.bootstrap.DateField} this
13417              * @param {Mixed} date The date value
13418              */
13419             hide : true,
13420             /**
13421              * @event select
13422              * Fires when select a date.
13423              * @param {Roo.bootstrap.DateField} this
13424              * @param {Mixed} date The date value
13425              */
13426             select : true
13427         });
13428 };
13429
13430 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
13431     
13432     /**
13433      * @cfg {String} format
13434      * The default time format string which can be overriden for localization support.  The format must be
13435      * valid according to {@link Date#parseDate} (defaults to 'H:i').
13436      */
13437     format : "H:i",
13438        
13439     onRender: function(ct, position)
13440     {
13441         
13442         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13443                 
13444         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13445         
13446         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13447         
13448         this.pop = this.picker().select('>.datepicker-time',true).first();
13449         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
13450         
13451         this.picker().on('mousedown', this.onMousedown, this);
13452         this.picker().on('click', this.onClick, this);
13453         
13454         this.picker().addClass('datepicker-dropdown');
13455     
13456         this.fillTime();
13457         this.update();
13458             
13459         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13460         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13461         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13462         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13463         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13464         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13465
13466     },
13467     
13468     fireKey: function(e){
13469         if (!this.picker().isVisible()){
13470             if (e.keyCode == 27) // allow escape to hide and re-show picker
13471                 this.show();
13472             return;
13473         }
13474
13475         e.preventDefault();
13476         
13477         switch(e.keyCode){
13478             case 27: // escape
13479                 this.hide();
13480                 break;
13481             case 37: // left
13482             case 39: // right
13483                 this.onTogglePeriod();
13484                 break;
13485             case 38: // up
13486                 this.onIncrementMinutes();
13487                 break;
13488             case 40: // down
13489                 this.onDecrementMinutes();
13490                 break;
13491             case 13: // enter
13492             case 9: // tab
13493                 this.setTime();
13494                 break;
13495         }
13496     },
13497     
13498     onClick: function(e) {
13499         e.stopPropagation();
13500         e.preventDefault();
13501     },
13502     
13503     picker : function()
13504     {
13505         return this.el.select('.datepicker', true).first();
13506     },
13507     
13508     fillTime: function()
13509     {    
13510         var time = this.pop.select('tbody', true).first();
13511         
13512         time.dom.innerHTML = '';
13513         
13514         time.createChild({
13515             tag: 'tr',
13516             cn: [
13517                 {
13518                     tag: 'td',
13519                     cn: [
13520                         {
13521                             tag: 'a',
13522                             href: '#',
13523                             cls: 'btn',
13524                             cn: [
13525                                 {
13526                                     tag: 'span',
13527                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
13528                                 }
13529                             ]
13530                         } 
13531                     ]
13532                 },
13533                 {
13534                     tag: 'td',
13535                     cls: 'separator'
13536                 },
13537                 {
13538                     tag: 'td',
13539                     cn: [
13540                         {
13541                             tag: 'a',
13542                             href: '#',
13543                             cls: 'btn',
13544                             cn: [
13545                                 {
13546                                     tag: 'span',
13547                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
13548                                 }
13549                             ]
13550                         }
13551                     ]
13552                 },
13553                 {
13554                     tag: 'td',
13555                     cls: 'separator'
13556                 }
13557             ]
13558         });
13559         
13560         time.createChild({
13561             tag: 'tr',
13562             cn: [
13563                 {
13564                     tag: 'td',
13565                     cn: [
13566                         {
13567                             tag: 'span',
13568                             cls: 'timepicker-hour',
13569                             html: '00'
13570                         }  
13571                     ]
13572                 },
13573                 {
13574                     tag: 'td',
13575                     cls: 'separator',
13576                     html: ':'
13577                 },
13578                 {
13579                     tag: 'td',
13580                     cn: [
13581                         {
13582                             tag: 'span',
13583                             cls: 'timepicker-minute',
13584                             html: '00'
13585                         }  
13586                     ]
13587                 },
13588                 {
13589                     tag: 'td',
13590                     cls: 'separator'
13591                 },
13592                 {
13593                     tag: 'td',
13594                     cn: [
13595                         {
13596                             tag: 'button',
13597                             type: 'button',
13598                             cls: 'btn btn-primary period',
13599                             html: 'AM'
13600                             
13601                         }
13602                     ]
13603                 }
13604             ]
13605         });
13606         
13607         time.createChild({
13608             tag: 'tr',
13609             cn: [
13610                 {
13611                     tag: 'td',
13612                     cn: [
13613                         {
13614                             tag: 'a',
13615                             href: '#',
13616                             cls: 'btn',
13617                             cn: [
13618                                 {
13619                                     tag: 'span',
13620                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
13621                                 }
13622                             ]
13623                         }
13624                     ]
13625                 },
13626                 {
13627                     tag: 'td',
13628                     cls: 'separator'
13629                 },
13630                 {
13631                     tag: 'td',
13632                     cn: [
13633                         {
13634                             tag: 'a',
13635                             href: '#',
13636                             cls: 'btn',
13637                             cn: [
13638                                 {
13639                                     tag: 'span',
13640                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
13641                                 }
13642                             ]
13643                         }
13644                     ]
13645                 },
13646                 {
13647                     tag: 'td',
13648                     cls: 'separator'
13649                 }
13650             ]
13651         });
13652         
13653     },
13654     
13655     update: function()
13656     {
13657         
13658         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13659         
13660         this.fill();
13661     },
13662     
13663     fill: function() 
13664     {
13665         var hours = this.time.getHours();
13666         var minutes = this.time.getMinutes();
13667         var period = 'AM';
13668         
13669         if(hours > 11){
13670             period = 'PM';
13671         }
13672         
13673         if(hours == 0){
13674             hours = 12;
13675         }
13676         
13677         
13678         if(hours > 12){
13679             hours = hours - 12;
13680         }
13681         
13682         if(hours < 10){
13683             hours = '0' + hours;
13684         }
13685         
13686         if(minutes < 10){
13687             minutes = '0' + minutes;
13688         }
13689         
13690         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13691         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13692         this.pop.select('button', true).first().dom.innerHTML = period;
13693         
13694     },
13695     
13696     place: function()
13697     {   
13698         this.picker().removeClass(['bottom', 'top']);
13699         
13700         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13701             /*
13702              * place to the top of element!
13703              *
13704              */
13705             
13706             this.picker().addClass('top');
13707             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13708             
13709             return;
13710         }
13711         
13712         this.picker().addClass('bottom');
13713         
13714         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13715     },
13716   
13717     onFocus : function()
13718     {
13719         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13720         this.show();
13721     },
13722     
13723     onBlur : function()
13724     {
13725         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13726         this.hide();
13727     },
13728     
13729     show : function()
13730     {
13731         this.picker().show();
13732         this.pop.show();
13733         this.update();
13734         this.place();
13735         
13736         this.fireEvent('show', this, this.date);
13737     },
13738     
13739     hide : function()
13740     {
13741         this.picker().hide();
13742         this.pop.hide();
13743         
13744         this.fireEvent('hide', this, this.date);
13745     },
13746     
13747     setTime : function()
13748     {
13749         this.hide();
13750         this.setValue(this.time.format(this.format));
13751         
13752         this.fireEvent('select', this, this.date);
13753         
13754         
13755     },
13756     
13757     onMousedown: function(e){
13758         e.stopPropagation();
13759         e.preventDefault();
13760     },
13761     
13762     onIncrementHours: function()
13763     {
13764         Roo.log('onIncrementHours');
13765         this.time = this.time.add(Date.HOUR, 1);
13766         this.update();
13767         
13768     },
13769     
13770     onDecrementHours: function()
13771     {
13772         Roo.log('onDecrementHours');
13773         this.time = this.time.add(Date.HOUR, -1);
13774         this.update();
13775     },
13776     
13777     onIncrementMinutes: function()
13778     {
13779         Roo.log('onIncrementMinutes');
13780         this.time = this.time.add(Date.MINUTE, 1);
13781         this.update();
13782     },
13783     
13784     onDecrementMinutes: function()
13785     {
13786         Roo.log('onDecrementMinutes');
13787         this.time = this.time.add(Date.MINUTE, -1);
13788         this.update();
13789     },
13790     
13791     onTogglePeriod: function()
13792     {
13793         Roo.log('onTogglePeriod');
13794         this.time = this.time.add(Date.HOUR, 12);
13795         this.update();
13796     }
13797     
13798    
13799 });
13800
13801 Roo.apply(Roo.bootstrap.TimeField,  {
13802     
13803     content : {
13804         tag: 'tbody',
13805         cn: [
13806             {
13807                 tag: 'tr',
13808                 cn: [
13809                 {
13810                     tag: 'td',
13811                     colspan: '7'
13812                 }
13813                 ]
13814             }
13815         ]
13816     },
13817     
13818     footer : {
13819         tag: 'tfoot',
13820         cn: [
13821             {
13822                 tag: 'tr',
13823                 cn: [
13824                 {
13825                     tag: 'th',
13826                     colspan: '7',
13827                     cls: '',
13828                     cn: [
13829                         {
13830                             tag: 'button',
13831                             cls: 'btn btn-info ok',
13832                             html: 'OK'
13833                         }
13834                     ]
13835                 }
13836
13837                 ]
13838             }
13839         ]
13840     }
13841 });
13842
13843 Roo.apply(Roo.bootstrap.TimeField,  {
13844   
13845     template : {
13846         tag: 'div',
13847         cls: 'datepicker dropdown-menu',
13848         cn: [
13849             {
13850                 tag: 'div',
13851                 cls: 'datepicker-time',
13852                 cn: [
13853                 {
13854                     tag: 'table',
13855                     cls: 'table-condensed',
13856                     cn:[
13857                     Roo.bootstrap.TimeField.content,
13858                     Roo.bootstrap.TimeField.footer
13859                     ]
13860                 }
13861                 ]
13862             }
13863         ]
13864     }
13865 });
13866
13867  
13868
13869  /*
13870  * - LGPL
13871  *
13872  * CheckBox
13873  * 
13874  */
13875
13876 /**
13877  * @class Roo.bootstrap.CheckBox
13878  * @extends Roo.bootstrap.Input
13879  * Bootstrap CheckBox class
13880  * 
13881  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13882  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13883  * @cfg {String} boxLabel The text that appears beside the checkbox
13884  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13885  * @cfg {Boolean} checked initnal the element
13886  * 
13887  * 
13888  * @constructor
13889  * Create a new CheckBox
13890  * @param {Object} config The config object
13891  */
13892
13893 Roo.bootstrap.CheckBox = function(config){
13894     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13895    
13896         this.addEvents({
13897             /**
13898             * @event check
13899             * Fires when the element is checked or unchecked.
13900             * @param {Roo.bootstrap.CheckBox} this This input
13901             * @param {Boolean} checked The new checked value
13902             */
13903            check : true
13904         });
13905 };
13906
13907 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
13908     
13909     inputType: 'checkbox',
13910     inputValue: 1,
13911     valueOff: 0,
13912     boxLabel: false,
13913     checked: false,
13914     weight : false,
13915     
13916     getAutoCreate : function()
13917     {
13918         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13919         
13920         var id = Roo.id();
13921         
13922         var cfg = {};
13923         
13924         cfg.cls = 'form-group checkbox' //input-group
13925         
13926         
13927         
13928         
13929         var input =  {
13930             tag: 'input',
13931             id : id,
13932             type : this.inputType,
13933             value : (!this.checked) ? this.valueOff : this.inputValue,
13934             cls : 'roo-checkbox', //'form-box',
13935             placeholder : this.placeholder || ''
13936             
13937         };
13938         
13939         if (this.weight) { // Validity check?
13940             cfg.cls += " checkbox-" + this.weight;
13941         }
13942         
13943         if (this.disabled) {
13944             input.disabled=true;
13945         }
13946         
13947         if(this.checked){
13948             input.checked = this.checked;
13949         }
13950         
13951         if (this.name) {
13952             input.name = this.name;
13953         }
13954         
13955         if (this.size) {
13956             input.cls += ' input-' + this.size;
13957         }
13958         
13959         var settings=this;
13960         ['xs','sm','md','lg'].map(function(size){
13961             if (settings[size]) {
13962                 cfg.cls += ' col-' + size + '-' + settings[size];
13963             }
13964         });
13965         
13966        
13967         
13968         var inputblock = input;
13969         
13970         
13971         
13972         
13973         if (this.before || this.after) {
13974             
13975             inputblock = {
13976                 cls : 'input-group',
13977                 cn :  [] 
13978             };
13979             if (this.before) {
13980                 inputblock.cn.push({
13981                     tag :'span',
13982                     cls : 'input-group-addon',
13983                     html : this.before
13984                 });
13985             }
13986             inputblock.cn.push(input);
13987             if (this.after) {
13988                 inputblock.cn.push({
13989                     tag :'span',
13990                     cls : 'input-group-addon',
13991                     html : this.after
13992                 });
13993             }
13994             
13995         };
13996         
13997         if (align ==='left' && this.fieldLabel.length) {
13998                 Roo.log("left and has label");
13999                 cfg.cn = [
14000                     
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label col-md-' + this.labelWidth,
14005                         html : this.fieldLabel
14006                         
14007                     },
14008                     {
14009                         cls : "col-md-" + (12 - this.labelWidth), 
14010                         cn: [
14011                             inputblock
14012                         ]
14013                     }
14014                     
14015                 ];
14016         } else if ( this.fieldLabel.length) {
14017                 Roo.log(" label");
14018                 cfg.cn = [
14019                    
14020                     {
14021                         tag: this.boxLabel ? 'span' : 'label',
14022                         'for': id,
14023                         cls: 'control-label box-input-label',
14024                         //cls : 'input-group-addon',
14025                         html : this.fieldLabel
14026                         
14027                     },
14028                     
14029                     inputblock
14030                     
14031                 ];
14032
14033         } else {
14034             
14035                 Roo.log(" no label && no align");
14036                 cfg.cn = [  inputblock ] ;
14037                 
14038                 
14039         };
14040          if(this.boxLabel){
14041             cfg.cn.push( {
14042                 tag: 'label',
14043                 'for': id,
14044                 cls: 'box-label',
14045                 html: this.boxLabel
14046                 
14047             });
14048         }
14049         
14050         
14051        
14052         return cfg;
14053         
14054     },
14055     
14056     /**
14057      * return the real input element.
14058      */
14059     inputEl: function ()
14060     {
14061         return this.el.select('input.roo-checkbox',true).first();
14062     },
14063     
14064     label: function()
14065     {
14066         return this.el.select('label.control-label',true).first();
14067     },
14068     
14069     initEvents : function()
14070     {
14071 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14072         
14073         this.inputEl().on('click', this.onClick,  this);
14074         
14075     },
14076     
14077     onClick : function()
14078     {   
14079         this.setChecked(!this.checked);
14080     },
14081     
14082     setChecked : function(state,suppressEvent)
14083     {
14084         this.checked = state;
14085         
14086         this.inputEl().dom.checked = state;
14087         
14088         if(suppressEvent !== true){
14089             this.fireEvent('check', this, state);
14090         }
14091         
14092         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14093         
14094     },
14095     
14096     setValue : function(v,suppressEvent)
14097     {
14098         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14099     }
14100     
14101 });
14102
14103  
14104 /*
14105  * - LGPL
14106  *
14107  * Radio
14108  * 
14109  */
14110
14111 /**
14112  * @class Roo.bootstrap.Radio
14113  * @extends Roo.bootstrap.CheckBox
14114  * Bootstrap Radio class
14115
14116  * @constructor
14117  * Create a new Radio
14118  * @param {Object} config The config object
14119  */
14120
14121 Roo.bootstrap.Radio = function(config){
14122     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14123    
14124 };
14125
14126 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14127     
14128     inputType: 'radio',
14129     inputValue: '',
14130     valueOff: '',
14131     
14132     getAutoCreate : function()
14133     {
14134         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14135         
14136         var id = Roo.id();
14137         
14138         var cfg = {};
14139         
14140         cfg.cls = 'form-group radio' //input-group
14141         
14142         var input =  {
14143             tag: 'input',
14144             id : id,
14145             type : this.inputType,
14146             value : (!this.checked) ? this.valueOff : this.inputValue,
14147             cls : 'roo-radio',
14148             placeholder : this.placeholder || ''
14149             
14150         };
14151           if (this.weight) { // Validity check?
14152             cfg.cls += " radio-" + this.weight;
14153         }
14154         if (this.disabled) {
14155             input.disabled=true;
14156         }
14157         
14158         if(this.checked){
14159             input.checked = this.checked;
14160         }
14161         
14162         if (this.name) {
14163             input.name = this.name;
14164         }
14165         
14166         if (this.size) {
14167             input.cls += ' input-' + this.size;
14168         }
14169         
14170         var settings=this;
14171         ['xs','sm','md','lg'].map(function(size){
14172             if (settings[size]) {
14173                 cfg.cls += ' col-' + size + '-' + settings[size];
14174             }
14175         });
14176         
14177         var inputblock = input;
14178         
14179         if (this.before || this.after) {
14180             
14181             inputblock = {
14182                 cls : 'input-group',
14183                 cn :  [] 
14184             };
14185             if (this.before) {
14186                 inputblock.cn.push({
14187                     tag :'span',
14188                     cls : 'input-group-addon',
14189                     html : this.before
14190                 });
14191             }
14192             inputblock.cn.push(input);
14193             if (this.after) {
14194                 inputblock.cn.push({
14195                     tag :'span',
14196                     cls : 'input-group-addon',
14197                     html : this.after
14198                 });
14199             }
14200             
14201         };
14202         
14203         if (align ==='left' && this.fieldLabel.length) {
14204                 Roo.log("left and has label");
14205                 cfg.cn = [
14206                     
14207                     {
14208                         tag: 'label',
14209                         'for' :  id,
14210                         cls : 'control-label col-md-' + this.labelWidth,
14211                         html : this.fieldLabel
14212                         
14213                     },
14214                     {
14215                         cls : "col-md-" + (12 - this.labelWidth), 
14216                         cn: [
14217                             inputblock
14218                         ]
14219                     }
14220                     
14221                 ];
14222         } else if ( this.fieldLabel.length) {
14223                 Roo.log(" label");
14224                  cfg.cn = [
14225                    
14226                     {
14227                         tag: 'label',
14228                         'for': id,
14229                         cls: 'control-label box-input-label',
14230                         //cls : 'input-group-addon',
14231                         html : this.fieldLabel
14232                         
14233                     },
14234                     
14235                     inputblock
14236                     
14237                 ];
14238
14239         } else {
14240             
14241                    Roo.log(" no label && no align");
14242                 cfg.cn = [
14243                     
14244                         inputblock
14245                     
14246                 ];
14247                 
14248                 
14249         };
14250         
14251         if(this.boxLabel){
14252             cfg.cn.push({
14253                 tag: 'label',
14254                 'for': id,
14255                 cls: 'box-label',
14256                 html: this.boxLabel
14257             })
14258         }
14259         
14260         return cfg;
14261         
14262     },
14263     inputEl: function ()
14264     {
14265         return this.el.select('input.roo-radio',true).first();
14266     },
14267     onClick : function()
14268     {   
14269         this.setChecked(true);
14270     },
14271     
14272     setChecked : function(state,suppressEvent)
14273     {
14274         if(state){
14275             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14276                 v.dom.checked = false;
14277             });
14278         }
14279         
14280         this.checked = state;
14281         this.inputEl().dom.checked = state;
14282         
14283         if(suppressEvent !== true){
14284             this.fireEvent('check', this, state);
14285         }
14286         
14287         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14288         
14289     },
14290     
14291     getGroupValue : function()
14292     {
14293         var value = ''
14294         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14295             if(v.dom.checked == true){
14296                 value = v.dom.value;
14297             }
14298         });
14299         
14300         return value;
14301     },
14302     
14303     /**
14304      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
14305      * @return {Mixed} value The field value
14306      */
14307     getValue : function(){
14308         return this.getGroupValue();
14309     }
14310     
14311 });
14312
14313  
14314 //<script type="text/javascript">
14315
14316 /*
14317  * Based  Ext JS Library 1.1.1
14318  * Copyright(c) 2006-2007, Ext JS, LLC.
14319  * LGPL
14320  *
14321  */
14322  
14323 /**
14324  * @class Roo.HtmlEditorCore
14325  * @extends Roo.Component
14326  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14327  *
14328  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14329  */
14330
14331 Roo.HtmlEditorCore = function(config){
14332     
14333     
14334     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14335     this.addEvents({
14336         /**
14337          * @event initialize
14338          * Fires when the editor is fully initialized (including the iframe)
14339          * @param {Roo.HtmlEditorCore} this
14340          */
14341         initialize: true,
14342         /**
14343          * @event activate
14344          * Fires when the editor is first receives the focus. Any insertion must wait
14345          * until after this event.
14346          * @param {Roo.HtmlEditorCore} this
14347          */
14348         activate: true,
14349          /**
14350          * @event beforesync
14351          * Fires before the textarea is updated with content from the editor iframe. Return false
14352          * to cancel the sync.
14353          * @param {Roo.HtmlEditorCore} this
14354          * @param {String} html
14355          */
14356         beforesync: true,
14357          /**
14358          * @event beforepush
14359          * Fires before the iframe editor is updated with content from the textarea. Return false
14360          * to cancel the push.
14361          * @param {Roo.HtmlEditorCore} this
14362          * @param {String} html
14363          */
14364         beforepush: true,
14365          /**
14366          * @event sync
14367          * Fires when the textarea is updated with content from the editor iframe.
14368          * @param {Roo.HtmlEditorCore} this
14369          * @param {String} html
14370          */
14371         sync: true,
14372          /**
14373          * @event push
14374          * Fires when the iframe editor is updated with content from the textarea.
14375          * @param {Roo.HtmlEditorCore} this
14376          * @param {String} html
14377          */
14378         push: true,
14379         
14380         /**
14381          * @event editorevent
14382          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14383          * @param {Roo.HtmlEditorCore} this
14384          */
14385         editorevent: true
14386     });
14387      
14388 };
14389
14390
14391 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
14392
14393
14394      /**
14395      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
14396      */
14397     
14398     owner : false,
14399     
14400      /**
14401      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
14402      *                        Roo.resizable.
14403      */
14404     resizable : false,
14405      /**
14406      * @cfg {Number} height (in pixels)
14407      */   
14408     height: 300,
14409    /**
14410      * @cfg {Number} width (in pixels)
14411      */   
14412     width: 500,
14413     
14414     /**
14415      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14416      * 
14417      */
14418     stylesheets: false,
14419     
14420     // id of frame..
14421     frameId: false,
14422     
14423     // private properties
14424     validationEvent : false,
14425     deferHeight: true,
14426     initialized : false,
14427     activated : false,
14428     sourceEditMode : false,
14429     onFocus : Roo.emptyFn,
14430     iframePad:3,
14431     hideMode:'offsets',
14432     
14433     clearUp: true,
14434     
14435      
14436     
14437
14438     /**
14439      * Protected method that will not generally be called directly. It
14440      * is called when the editor initializes the iframe with HTML contents. Override this method if you
14441      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14442      */
14443     getDocMarkup : function(){
14444         // body styles..
14445         var st = '';
14446         Roo.log(this.stylesheets);
14447         
14448         // inherit styels from page...?? 
14449         if (this.stylesheets === false) {
14450             
14451             Roo.get(document.head).select('style').each(function(node) {
14452                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14453             });
14454             
14455             Roo.get(document.head).select('link').each(function(node) { 
14456                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14457             });
14458             
14459         } else if (!this.stylesheets.length) {
14460                 // simple..
14461                 st = '<style type="text/css">' +
14462                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14463                    '</style>';
14464         } else {
14465             Roo.each(this.stylesheets, function(s) {
14466                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14467             });
14468             
14469         }
14470         
14471         st +=  '<style type="text/css">' +
14472             'IMG { cursor: pointer } ' +
14473         '</style>';
14474
14475         
14476         return '<html><head>' + st  +
14477             //<style type="text/css">' +
14478             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14479             //'</style>' +
14480             ' </head><body class="roo-htmleditor-body"></body></html>';
14481     },
14482
14483     // private
14484     onRender : function(ct, position)
14485     {
14486         var _t = this;
14487         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14488         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14489         
14490         
14491         this.el.dom.style.border = '0 none';
14492         this.el.dom.setAttribute('tabIndex', -1);
14493         this.el.addClass('x-hidden hide');
14494         
14495         
14496         
14497         if(Roo.isIE){ // fix IE 1px bogus margin
14498             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14499         }
14500        
14501         
14502         this.frameId = Roo.id();
14503         
14504          
14505         
14506         var iframe = this.owner.wrap.createChild({
14507             tag: 'iframe',
14508             cls: 'form-control', // bootstrap..
14509             id: this.frameId,
14510             name: this.frameId,
14511             frameBorder : 'no',
14512             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
14513         }, this.el
14514         );
14515         
14516         
14517         this.iframe = iframe.dom;
14518
14519          this.assignDocWin();
14520         
14521         this.doc.designMode = 'on';
14522        
14523         this.doc.open();
14524         this.doc.write(this.getDocMarkup());
14525         this.doc.close();
14526
14527         
14528         var task = { // must defer to wait for browser to be ready
14529             run : function(){
14530                 //console.log("run task?" + this.doc.readyState);
14531                 this.assignDocWin();
14532                 if(this.doc.body || this.doc.readyState == 'complete'){
14533                     try {
14534                         this.doc.designMode="on";
14535                     } catch (e) {
14536                         return;
14537                     }
14538                     Roo.TaskMgr.stop(task);
14539                     this.initEditor.defer(10, this);
14540                 }
14541             },
14542             interval : 10,
14543             duration: 10000,
14544             scope: this
14545         };
14546         Roo.TaskMgr.start(task);
14547
14548         
14549          
14550     },
14551
14552     // private
14553     onResize : function(w, h)
14554     {
14555          Roo.log('resize: ' +w + ',' + h );
14556         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14557         if(!this.iframe){
14558             return;
14559         }
14560         if(typeof w == 'number'){
14561             
14562             this.iframe.style.width = w + 'px';
14563         }
14564         if(typeof h == 'number'){
14565             
14566             this.iframe.style.height = h + 'px';
14567             if(this.doc){
14568                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14569             }
14570         }
14571         
14572     },
14573
14574     /**
14575      * Toggles the editor between standard and source edit mode.
14576      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14577      */
14578     toggleSourceEdit : function(sourceEditMode){
14579         
14580         this.sourceEditMode = sourceEditMode === true;
14581         
14582         if(this.sourceEditMode){
14583  
14584             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
14585             
14586         }else{
14587             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14588             //this.iframe.className = '';
14589             this.deferFocus();
14590         }
14591         //this.setSize(this.owner.wrap.getSize());
14592         //this.fireEvent('editmodechange', this, this.sourceEditMode);
14593     },
14594
14595     
14596   
14597
14598     /**
14599      * Protected method that will not generally be called directly. If you need/want
14600      * custom HTML cleanup, this is the method you should override.
14601      * @param {String} html The HTML to be cleaned
14602      * return {String} The cleaned HTML
14603      */
14604     cleanHtml : function(html){
14605         html = String(html);
14606         if(html.length > 5){
14607             if(Roo.isSafari){ // strip safari nonsense
14608                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14609             }
14610         }
14611         if(html == '&nbsp;'){
14612             html = '';
14613         }
14614         return html;
14615     },
14616
14617     /**
14618      * HTML Editor -> Textarea
14619      * Protected method that will not generally be called directly. Syncs the contents
14620      * of the editor iframe with the textarea.
14621      */
14622     syncValue : function(){
14623         if(this.initialized){
14624             var bd = (this.doc.body || this.doc.documentElement);
14625             //this.cleanUpPaste(); -- this is done else where and causes havoc..
14626             var html = bd.innerHTML;
14627             if(Roo.isSafari){
14628                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14629                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14630                 if(m && m[1]){
14631                     html = '<div style="'+m[0]+'">' + html + '</div>';
14632                 }
14633             }
14634             html = this.cleanHtml(html);
14635             // fix up the special chars.. normaly like back quotes in word...
14636             // however we do not want to do this with chinese..
14637             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14638                 var cc = b.charCodeAt();
14639                 if (
14640                     (cc >= 0x4E00 && cc < 0xA000 ) ||
14641                     (cc >= 0x3400 && cc < 0x4E00 ) ||
14642                     (cc >= 0xf900 && cc < 0xfb00 )
14643                 ) {
14644                         return b;
14645                 }
14646                 return "&#"+cc+";" 
14647             });
14648             if(this.owner.fireEvent('beforesync', this, html) !== false){
14649                 this.el.dom.value = html;
14650                 this.owner.fireEvent('sync', this, html);
14651             }
14652         }
14653     },
14654
14655     /**
14656      * Protected method that will not generally be called directly. Pushes the value of the textarea
14657      * into the iframe editor.
14658      */
14659     pushValue : function(){
14660         if(this.initialized){
14661             var v = this.el.dom.value.trim();
14662             
14663 //            if(v.length < 1){
14664 //                v = '&#160;';
14665 //            }
14666             
14667             if(this.owner.fireEvent('beforepush', this, v) !== false){
14668                 var d = (this.doc.body || this.doc.documentElement);
14669                 d.innerHTML = v;
14670                 this.cleanUpPaste();
14671                 this.el.dom.value = d.innerHTML;
14672                 this.owner.fireEvent('push', this, v);
14673             }
14674         }
14675     },
14676
14677     // private
14678     deferFocus : function(){
14679         this.focus.defer(10, this);
14680     },
14681
14682     // doc'ed in Field
14683     focus : function(){
14684         if(this.win && !this.sourceEditMode){
14685             this.win.focus();
14686         }else{
14687             this.el.focus();
14688         }
14689     },
14690     
14691     assignDocWin: function()
14692     {
14693         var iframe = this.iframe;
14694         
14695          if(Roo.isIE){
14696             this.doc = iframe.contentWindow.document;
14697             this.win = iframe.contentWindow;
14698         } else {
14699             if (!Roo.get(this.frameId)) {
14700                 return;
14701             }
14702             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14703             this.win = Roo.get(this.frameId).dom.contentWindow;
14704         }
14705     },
14706     
14707     // private
14708     initEditor : function(){
14709         //console.log("INIT EDITOR");
14710         this.assignDocWin();
14711         
14712         
14713         
14714         this.doc.designMode="on";
14715         this.doc.open();
14716         this.doc.write(this.getDocMarkup());
14717         this.doc.close();
14718         
14719         var dbody = (this.doc.body || this.doc.documentElement);
14720         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14721         // this copies styles from the containing element into thsi one..
14722         // not sure why we need all of this..
14723         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14724         
14725         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
14726         //ss['background-attachment'] = 'fixed'; // w3c
14727         dbody.bgProperties = 'fixed'; // ie
14728         //Roo.DomHelper.applyStyles(dbody, ss);
14729         Roo.EventManager.on(this.doc, {
14730             //'mousedown': this.onEditorEvent,
14731             'mouseup': this.onEditorEvent,
14732             'dblclick': this.onEditorEvent,
14733             'click': this.onEditorEvent,
14734             'keyup': this.onEditorEvent,
14735             buffer:100,
14736             scope: this
14737         });
14738         if(Roo.isGecko){
14739             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14740         }
14741         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14742             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14743         }
14744         this.initialized = true;
14745
14746         this.owner.fireEvent('initialize', this);
14747         this.pushValue();
14748     },
14749
14750     // private
14751     onDestroy : function(){
14752         
14753         
14754         
14755         if(this.rendered){
14756             
14757             //for (var i =0; i < this.toolbars.length;i++) {
14758             //    // fixme - ask toolbars for heights?
14759             //    this.toolbars[i].onDestroy();
14760            // }
14761             
14762             //this.wrap.dom.innerHTML = '';
14763             //this.wrap.remove();
14764         }
14765     },
14766
14767     // private
14768     onFirstFocus : function(){
14769         
14770         this.assignDocWin();
14771         
14772         
14773         this.activated = true;
14774          
14775     
14776         if(Roo.isGecko){ // prevent silly gecko errors
14777             this.win.focus();
14778             var s = this.win.getSelection();
14779             if(!s.focusNode || s.focusNode.nodeType != 3){
14780                 var r = s.getRangeAt(0);
14781                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14782                 r.collapse(true);
14783                 this.deferFocus();
14784             }
14785             try{
14786                 this.execCmd('useCSS', true);
14787                 this.execCmd('styleWithCSS', false);
14788             }catch(e){}
14789         }
14790         this.owner.fireEvent('activate', this);
14791     },
14792
14793     // private
14794     adjustFont: function(btn){
14795         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14796         //if(Roo.isSafari){ // safari
14797         //    adjust *= 2;
14798        // }
14799         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14800         if(Roo.isSafari){ // safari
14801             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14802             v =  (v < 10) ? 10 : v;
14803             v =  (v > 48) ? 48 : v;
14804             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14805             
14806         }
14807         
14808         
14809         v = Math.max(1, v+adjust);
14810         
14811         this.execCmd('FontSize', v  );
14812     },
14813
14814     onEditorEvent : function(e){
14815         this.owner.fireEvent('editorevent', this, e);
14816       //  this.updateToolbar();
14817         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14818     },
14819
14820     insertTag : function(tg)
14821     {
14822         // could be a bit smarter... -> wrap the current selected tRoo..
14823         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14824             
14825             range = this.createRange(this.getSelection());
14826             var wrappingNode = this.doc.createElement(tg.toLowerCase());
14827             wrappingNode.appendChild(range.extractContents());
14828             range.insertNode(wrappingNode);
14829
14830             return;
14831             
14832             
14833             
14834         }
14835         this.execCmd("formatblock",   tg);
14836         
14837     },
14838     
14839     insertText : function(txt)
14840     {
14841         
14842         
14843         var range = this.createRange();
14844         range.deleteContents();
14845                //alert(Sender.getAttribute('label'));
14846                
14847         range.insertNode(this.doc.createTextNode(txt));
14848     } ,
14849     
14850      
14851
14852     /**
14853      * Executes a Midas editor command on the editor document and performs necessary focus and
14854      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14855      * @param {String} cmd The Midas command
14856      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14857      */
14858     relayCmd : function(cmd, value){
14859         this.win.focus();
14860         this.execCmd(cmd, value);
14861         this.owner.fireEvent('editorevent', this);
14862         //this.updateToolbar();
14863         this.owner.deferFocus();
14864     },
14865
14866     /**
14867      * Executes a Midas editor command directly on the editor document.
14868      * For visual commands, you should use {@link #relayCmd} instead.
14869      * <b>This should only be called after the editor is initialized.</b>
14870      * @param {String} cmd The Midas command
14871      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14872      */
14873     execCmd : function(cmd, value){
14874         this.doc.execCommand(cmd, false, value === undefined ? null : value);
14875         this.syncValue();
14876     },
14877  
14878  
14879    
14880     /**
14881      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14882      * to insert tRoo.
14883      * @param {String} text | dom node.. 
14884      */
14885     insertAtCursor : function(text)
14886     {
14887         
14888         
14889         
14890         if(!this.activated){
14891             return;
14892         }
14893         /*
14894         if(Roo.isIE){
14895             this.win.focus();
14896             var r = this.doc.selection.createRange();
14897             if(r){
14898                 r.collapse(true);
14899                 r.pasteHTML(text);
14900                 this.syncValue();
14901                 this.deferFocus();
14902             
14903             }
14904             return;
14905         }
14906         */
14907         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14908             this.win.focus();
14909             
14910             
14911             // from jquery ui (MIT licenced)
14912             var range, node;
14913             var win = this.win;
14914             
14915             if (win.getSelection && win.getSelection().getRangeAt) {
14916                 range = win.getSelection().getRangeAt(0);
14917                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14918                 range.insertNode(node);
14919             } else if (win.document.selection && win.document.selection.createRange) {
14920                 // no firefox support
14921                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14922                 win.document.selection.createRange().pasteHTML(txt);
14923             } else {
14924                 // no firefox support
14925                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14926                 this.execCmd('InsertHTML', txt);
14927             } 
14928             
14929             this.syncValue();
14930             
14931             this.deferFocus();
14932         }
14933     },
14934  // private
14935     mozKeyPress : function(e){
14936         if(e.ctrlKey){
14937             var c = e.getCharCode(), cmd;
14938           
14939             if(c > 0){
14940                 c = String.fromCharCode(c).toLowerCase();
14941                 switch(c){
14942                     case 'b':
14943                         cmd = 'bold';
14944                         break;
14945                     case 'i':
14946                         cmd = 'italic';
14947                         break;
14948                     
14949                     case 'u':
14950                         cmd = 'underline';
14951                         break;
14952                     
14953                     case 'v':
14954                         this.cleanUpPaste.defer(100, this);
14955                         return;
14956                         
14957                 }
14958                 if(cmd){
14959                     this.win.focus();
14960                     this.execCmd(cmd);
14961                     this.deferFocus();
14962                     e.preventDefault();
14963                 }
14964                 
14965             }
14966         }
14967     },
14968
14969     // private
14970     fixKeys : function(){ // load time branching for fastest keydown performance
14971         if(Roo.isIE){
14972             return function(e){
14973                 var k = e.getKey(), r;
14974                 if(k == e.TAB){
14975                     e.stopEvent();
14976                     r = this.doc.selection.createRange();
14977                     if(r){
14978                         r.collapse(true);
14979                         r.pasteHTML('&#160;&#160;&#160;&#160;');
14980                         this.deferFocus();
14981                     }
14982                     return;
14983                 }
14984                 
14985                 if(k == e.ENTER){
14986                     r = this.doc.selection.createRange();
14987                     if(r){
14988                         var target = r.parentElement();
14989                         if(!target || target.tagName.toLowerCase() != 'li'){
14990                             e.stopEvent();
14991                             r.pasteHTML('<br />');
14992                             r.collapse(false);
14993                             r.select();
14994                         }
14995                     }
14996                 }
14997                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14998                     this.cleanUpPaste.defer(100, this);
14999                     return;
15000                 }
15001                 
15002                 
15003             };
15004         }else if(Roo.isOpera){
15005             return function(e){
15006                 var k = e.getKey();
15007                 if(k == e.TAB){
15008                     e.stopEvent();
15009                     this.win.focus();
15010                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15011                     this.deferFocus();
15012                 }
15013                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15014                     this.cleanUpPaste.defer(100, this);
15015                     return;
15016                 }
15017                 
15018             };
15019         }else if(Roo.isSafari){
15020             return function(e){
15021                 var k = e.getKey();
15022                 
15023                 if(k == e.TAB){
15024                     e.stopEvent();
15025                     this.execCmd('InsertText','\t');
15026                     this.deferFocus();
15027                     return;
15028                 }
15029                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15030                     this.cleanUpPaste.defer(100, this);
15031                     return;
15032                 }
15033                 
15034              };
15035         }
15036     }(),
15037     
15038     getAllAncestors: function()
15039     {
15040         var p = this.getSelectedNode();
15041         var a = [];
15042         if (!p) {
15043             a.push(p); // push blank onto stack..
15044             p = this.getParentElement();
15045         }
15046         
15047         
15048         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15049             a.push(p);
15050             p = p.parentNode;
15051         }
15052         a.push(this.doc.body);
15053         return a;
15054     },
15055     lastSel : false,
15056     lastSelNode : false,
15057     
15058     
15059     getSelection : function() 
15060     {
15061         this.assignDocWin();
15062         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15063     },
15064     
15065     getSelectedNode: function() 
15066     {
15067         // this may only work on Gecko!!!
15068         
15069         // should we cache this!!!!
15070         
15071         
15072         
15073          
15074         var range = this.createRange(this.getSelection()).cloneRange();
15075         
15076         if (Roo.isIE) {
15077             var parent = range.parentElement();
15078             while (true) {
15079                 var testRange = range.duplicate();
15080                 testRange.moveToElementText(parent);
15081                 if (testRange.inRange(range)) {
15082                     break;
15083                 }
15084                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15085                     break;
15086                 }
15087                 parent = parent.parentElement;
15088             }
15089             return parent;
15090         }
15091         
15092         // is ancestor a text element.
15093         var ac =  range.commonAncestorContainer;
15094         if (ac.nodeType == 3) {
15095             ac = ac.parentNode;
15096         }
15097         
15098         var ar = ac.childNodes;
15099          
15100         var nodes = [];
15101         var other_nodes = [];
15102         var has_other_nodes = false;
15103         for (var i=0;i<ar.length;i++) {
15104             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15105                 continue;
15106             }
15107             // fullly contained node.
15108             
15109             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15110                 nodes.push(ar[i]);
15111                 continue;
15112             }
15113             
15114             // probably selected..
15115             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15116                 other_nodes.push(ar[i]);
15117                 continue;
15118             }
15119             // outer..
15120             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15121                 continue;
15122             }
15123             
15124             
15125             has_other_nodes = true;
15126         }
15127         if (!nodes.length && other_nodes.length) {
15128             nodes= other_nodes;
15129         }
15130         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15131             return false;
15132         }
15133         
15134         return nodes[0];
15135     },
15136     createRange: function(sel)
15137     {
15138         // this has strange effects when using with 
15139         // top toolbar - not sure if it's a great idea.
15140         //this.editor.contentWindow.focus();
15141         if (typeof sel != "undefined") {
15142             try {
15143                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15144             } catch(e) {
15145                 return this.doc.createRange();
15146             }
15147         } else {
15148             return this.doc.createRange();
15149         }
15150     },
15151     getParentElement: function()
15152     {
15153         
15154         this.assignDocWin();
15155         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15156         
15157         var range = this.createRange(sel);
15158          
15159         try {
15160             var p = range.commonAncestorContainer;
15161             while (p.nodeType == 3) { // text node
15162                 p = p.parentNode;
15163             }
15164             return p;
15165         } catch (e) {
15166             return null;
15167         }
15168     
15169     },
15170     /***
15171      *
15172      * Range intersection.. the hard stuff...
15173      *  '-1' = before
15174      *  '0' = hits..
15175      *  '1' = after.
15176      *         [ -- selected range --- ]
15177      *   [fail]                        [fail]
15178      *
15179      *    basically..
15180      *      if end is before start or  hits it. fail.
15181      *      if start is after end or hits it fail.
15182      *
15183      *   if either hits (but other is outside. - then it's not 
15184      *   
15185      *    
15186      **/
15187     
15188     
15189     // @see http://www.thismuchiknow.co.uk/?p=64.
15190     rangeIntersectsNode : function(range, node)
15191     {
15192         var nodeRange = node.ownerDocument.createRange();
15193         try {
15194             nodeRange.selectNode(node);
15195         } catch (e) {
15196             nodeRange.selectNodeContents(node);
15197         }
15198     
15199         var rangeStartRange = range.cloneRange();
15200         rangeStartRange.collapse(true);
15201     
15202         var rangeEndRange = range.cloneRange();
15203         rangeEndRange.collapse(false);
15204     
15205         var nodeStartRange = nodeRange.cloneRange();
15206         nodeStartRange.collapse(true);
15207     
15208         var nodeEndRange = nodeRange.cloneRange();
15209         nodeEndRange.collapse(false);
15210     
15211         return rangeStartRange.compareBoundaryPoints(
15212                  Range.START_TO_START, nodeEndRange) == -1 &&
15213                rangeEndRange.compareBoundaryPoints(
15214                  Range.START_TO_START, nodeStartRange) == 1;
15215         
15216          
15217     },
15218     rangeCompareNode : function(range, node)
15219     {
15220         var nodeRange = node.ownerDocument.createRange();
15221         try {
15222             nodeRange.selectNode(node);
15223         } catch (e) {
15224             nodeRange.selectNodeContents(node);
15225         }
15226         
15227         
15228         range.collapse(true);
15229     
15230         nodeRange.collapse(true);
15231      
15232         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15233         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15234          
15235         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15236         
15237         var nodeIsBefore   =  ss == 1;
15238         var nodeIsAfter    = ee == -1;
15239         
15240         if (nodeIsBefore && nodeIsAfter)
15241             return 0; // outer
15242         if (!nodeIsBefore && nodeIsAfter)
15243             return 1; //right trailed.
15244         
15245         if (nodeIsBefore && !nodeIsAfter)
15246             return 2;  // left trailed.
15247         // fully contined.
15248         return 3;
15249     },
15250
15251     // private? - in a new class?
15252     cleanUpPaste :  function()
15253     {
15254         // cleans up the whole document..
15255         Roo.log('cleanuppaste');
15256         
15257         this.cleanUpChildren(this.doc.body);
15258         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15259         if (clean != this.doc.body.innerHTML) {
15260             this.doc.body.innerHTML = clean;
15261         }
15262         
15263     },
15264     
15265     cleanWordChars : function(input) {// change the chars to hex code
15266         var he = Roo.HtmlEditorCore;
15267         
15268         var output = input;
15269         Roo.each(he.swapCodes, function(sw) { 
15270             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15271             
15272             output = output.replace(swapper, sw[1]);
15273         });
15274         
15275         return output;
15276     },
15277     
15278     
15279     cleanUpChildren : function (n)
15280     {
15281         if (!n.childNodes.length) {
15282             return;
15283         }
15284         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15285            this.cleanUpChild(n.childNodes[i]);
15286         }
15287     },
15288     
15289     
15290         
15291     
15292     cleanUpChild : function (node)
15293     {
15294         var ed = this;
15295         //console.log(node);
15296         if (node.nodeName == "#text") {
15297             // clean up silly Windows -- stuff?
15298             return; 
15299         }
15300         if (node.nodeName == "#comment") {
15301             node.parentNode.removeChild(node);
15302             // clean up silly Windows -- stuff?
15303             return; 
15304         }
15305         
15306         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15307             // remove node.
15308             node.parentNode.removeChild(node);
15309             return;
15310             
15311         }
15312         
15313         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15314         
15315         // remove <a name=....> as rendering on yahoo mailer is borked with this.
15316         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15317         
15318         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15319         //    remove_keep_children = true;
15320         //}
15321         
15322         if (remove_keep_children) {
15323             this.cleanUpChildren(node);
15324             // inserts everything just before this node...
15325             while (node.childNodes.length) {
15326                 var cn = node.childNodes[0];
15327                 node.removeChild(cn);
15328                 node.parentNode.insertBefore(cn, node);
15329             }
15330             node.parentNode.removeChild(node);
15331             return;
15332         }
15333         
15334         if (!node.attributes || !node.attributes.length) {
15335             this.cleanUpChildren(node);
15336             return;
15337         }
15338         
15339         function cleanAttr(n,v)
15340         {
15341             
15342             if (v.match(/^\./) || v.match(/^\//)) {
15343                 return;
15344             }
15345             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15346                 return;
15347             }
15348             if (v.match(/^#/)) {
15349                 return;
15350             }
15351 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15352             node.removeAttribute(n);
15353             
15354         }
15355         
15356         function cleanStyle(n,v)
15357         {
15358             if (v.match(/expression/)) { //XSS?? should we even bother..
15359                 node.removeAttribute(n);
15360                 return;
15361             }
15362             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15363             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15364             
15365             
15366             var parts = v.split(/;/);
15367             var clean = [];
15368             
15369             Roo.each(parts, function(p) {
15370                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15371                 if (!p.length) {
15372                     return true;
15373                 }
15374                 var l = p.split(':').shift().replace(/\s+/g,'');
15375                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15376                 
15377                 if ( cblack.indexOf(l) > -1) {
15378 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15379                     //node.removeAttribute(n);
15380                     return true;
15381                 }
15382                 //Roo.log()
15383                 // only allow 'c whitelisted system attributes'
15384                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
15385 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15386                     //node.removeAttribute(n);
15387                     return true;
15388                 }
15389                 
15390                 
15391                  
15392                 
15393                 clean.push(p);
15394                 return true;
15395             });
15396             if (clean.length) { 
15397                 node.setAttribute(n, clean.join(';'));
15398             } else {
15399                 node.removeAttribute(n);
15400             }
15401             
15402         }
15403         
15404         
15405         for (var i = node.attributes.length-1; i > -1 ; i--) {
15406             var a = node.attributes[i];
15407             //console.log(a);
15408             
15409             if (a.name.toLowerCase().substr(0,2)=='on')  {
15410                 node.removeAttribute(a.name);
15411                 continue;
15412             }
15413             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15414                 node.removeAttribute(a.name);
15415                 continue;
15416             }
15417             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15418                 cleanAttr(a.name,a.value); // fixme..
15419                 continue;
15420             }
15421             if (a.name == 'style') {
15422                 cleanStyle(a.name,a.value);
15423                 continue;
15424             }
15425             /// clean up MS crap..
15426             // tecnically this should be a list of valid class'es..
15427             
15428             
15429             if (a.name == 'class') {
15430                 if (a.value.match(/^Mso/)) {
15431                     node.className = '';
15432                 }
15433                 
15434                 if (a.value.match(/body/)) {
15435                     node.className = '';
15436                 }
15437                 continue;
15438             }
15439             
15440             // style cleanup!?
15441             // class cleanup?
15442             
15443         }
15444         
15445         
15446         this.cleanUpChildren(node);
15447         
15448         
15449     },
15450     /**
15451      * Clean up MS wordisms...
15452      */
15453     cleanWord : function(node)
15454     {
15455         var _t = this;
15456         var cleanWordChildren = function()
15457         {
15458             if (!node.childNodes.length) {
15459                 return;
15460             }
15461             for (var i = node.childNodes.length-1; i > -1 ; i--) {
15462                _t.cleanWord(node.childNodes[i]);
15463             }
15464         }
15465         
15466         
15467         if (!node) {
15468             this.cleanWord(this.doc.body);
15469             return;
15470         }
15471         if (node.nodeName == "#text") {
15472             // clean up silly Windows -- stuff?
15473             return; 
15474         }
15475         if (node.nodeName == "#comment") {
15476             node.parentNode.removeChild(node);
15477             // clean up silly Windows -- stuff?
15478             return; 
15479         }
15480         
15481         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15482             node.parentNode.removeChild(node);
15483             return;
15484         }
15485         
15486         // remove - but keep children..
15487         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15488             while (node.childNodes.length) {
15489                 var cn = node.childNodes[0];
15490                 node.removeChild(cn);
15491                 node.parentNode.insertBefore(cn, node);
15492             }
15493             node.parentNode.removeChild(node);
15494             cleanWordChildren();
15495             return;
15496         }
15497         // clean styles
15498         if (node.className.length) {
15499             
15500             var cn = node.className.split(/\W+/);
15501             var cna = [];
15502             Roo.each(cn, function(cls) {
15503                 if (cls.match(/Mso[a-zA-Z]+/)) {
15504                     return;
15505                 }
15506                 cna.push(cls);
15507             });
15508             node.className = cna.length ? cna.join(' ') : '';
15509             if (!cna.length) {
15510                 node.removeAttribute("class");
15511             }
15512         }
15513         
15514         if (node.hasAttribute("lang")) {
15515             node.removeAttribute("lang");
15516         }
15517         
15518         if (node.hasAttribute("style")) {
15519             
15520             var styles = node.getAttribute("style").split(";");
15521             var nstyle = [];
15522             Roo.each(styles, function(s) {
15523                 if (!s.match(/:/)) {
15524                     return;
15525                 }
15526                 var kv = s.split(":");
15527                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15528                     return;
15529                 }
15530                 // what ever is left... we allow.
15531                 nstyle.push(s);
15532             });
15533             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15534             if (!nstyle.length) {
15535                 node.removeAttribute('style');
15536             }
15537         }
15538         
15539         cleanWordChildren();
15540         
15541         
15542     },
15543     domToHTML : function(currentElement, depth, nopadtext) {
15544         
15545             depth = depth || 0;
15546             nopadtext = nopadtext || false;
15547         
15548             if (!currentElement) {
15549                 return this.domToHTML(this.doc.body);
15550             }
15551             
15552             //Roo.log(currentElement);
15553             var j;
15554             var allText = false;
15555             var nodeName = currentElement.nodeName;
15556             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15557             
15558             if  (nodeName == '#text') {
15559                 return currentElement.nodeValue;
15560             }
15561             
15562             
15563             var ret = '';
15564             if (nodeName != 'BODY') {
15565                  
15566                 var i = 0;
15567                 // Prints the node tagName, such as <A>, <IMG>, etc
15568                 if (tagName) {
15569                     var attr = [];
15570                     for(i = 0; i < currentElement.attributes.length;i++) {
15571                         // quoting?
15572                         var aname = currentElement.attributes.item(i).name;
15573                         if (!currentElement.attributes.item(i).value.length) {
15574                             continue;
15575                         }
15576                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15577                     }
15578                     
15579                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15580                 } 
15581                 else {
15582                     
15583                     // eack
15584                 }
15585             } else {
15586                 tagName = false;
15587             }
15588             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15589                 return ret;
15590             }
15591             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15592                 nopadtext = true;
15593             }
15594             
15595             
15596             // Traverse the tree
15597             i = 0;
15598             var currentElementChild = currentElement.childNodes.item(i);
15599             var allText = true;
15600             var innerHTML  = '';
15601             lastnode = '';
15602             while (currentElementChild) {
15603                 // Formatting code (indent the tree so it looks nice on the screen)
15604                 var nopad = nopadtext;
15605                 if (lastnode == 'SPAN') {
15606                     nopad  = true;
15607                 }
15608                 // text
15609                 if  (currentElementChild.nodeName == '#text') {
15610                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15611                     if (!nopad && toadd.length > 80) {
15612                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
15613                     }
15614                     innerHTML  += toadd;
15615                     
15616                     i++;
15617                     currentElementChild = currentElement.childNodes.item(i);
15618                     lastNode = '';
15619                     continue;
15620                 }
15621                 allText = false;
15622                 
15623                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
15624                     
15625                 // Recursively traverse the tree structure of the child node
15626                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
15627                 lastnode = currentElementChild.nodeName;
15628                 i++;
15629                 currentElementChild=currentElement.childNodes.item(i);
15630             }
15631             
15632             ret += innerHTML;
15633             
15634             if (!allText) {
15635                     // The remaining code is mostly for formatting the tree
15636                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
15637             }
15638             
15639             
15640             if (tagName) {
15641                 ret+= "</"+tagName+">";
15642             }
15643             return ret;
15644             
15645         }
15646     
15647     // hide stuff that is not compatible
15648     /**
15649      * @event blur
15650      * @hide
15651      */
15652     /**
15653      * @event change
15654      * @hide
15655      */
15656     /**
15657      * @event focus
15658      * @hide
15659      */
15660     /**
15661      * @event specialkey
15662      * @hide
15663      */
15664     /**
15665      * @cfg {String} fieldClass @hide
15666      */
15667     /**
15668      * @cfg {String} focusClass @hide
15669      */
15670     /**
15671      * @cfg {String} autoCreate @hide
15672      */
15673     /**
15674      * @cfg {String} inputType @hide
15675      */
15676     /**
15677      * @cfg {String} invalidClass @hide
15678      */
15679     /**
15680      * @cfg {String} invalidText @hide
15681      */
15682     /**
15683      * @cfg {String} msgFx @hide
15684      */
15685     /**
15686      * @cfg {String} validateOnBlur @hide
15687      */
15688 });
15689
15690 Roo.HtmlEditorCore.white = [
15691         'area', 'br', 'img', 'input', 'hr', 'wbr',
15692         
15693        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
15694        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
15695        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
15696        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
15697        'table',   'ul',         'xmp', 
15698        
15699        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
15700       'thead',   'tr', 
15701      
15702       'dir', 'menu', 'ol', 'ul', 'dl',
15703        
15704       'embed',  'object'
15705 ];
15706
15707
15708 Roo.HtmlEditorCore.black = [
15709     //    'embed',  'object', // enable - backend responsiblity to clean thiese
15710         'applet', // 
15711         'base',   'basefont', 'bgsound', 'blink',  'body', 
15712         'frame',  'frameset', 'head',    'html',   'ilayer', 
15713         'iframe', 'layer',  'link',     'meta',    'object',   
15714         'script', 'style' ,'title',  'xml' // clean later..
15715 ];
15716 Roo.HtmlEditorCore.clean = [
15717     'script', 'style', 'title', 'xml'
15718 ];
15719 Roo.HtmlEditorCore.remove = [
15720     'font'
15721 ];
15722 // attributes..
15723
15724 Roo.HtmlEditorCore.ablack = [
15725     'on'
15726 ];
15727     
15728 Roo.HtmlEditorCore.aclean = [ 
15729     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
15730 ];
15731
15732 // protocols..
15733 Roo.HtmlEditorCore.pwhite= [
15734         'http',  'https',  'mailto'
15735 ];
15736
15737 // white listed style attributes.
15738 Roo.HtmlEditorCore.cwhite= [
15739       //  'text-align', /// default is to allow most things..
15740       
15741          
15742 //        'font-size'//??
15743 ];
15744
15745 // black listed style attributes.
15746 Roo.HtmlEditorCore.cblack= [
15747       //  'font-size' -- this can be set by the project 
15748 ];
15749
15750
15751 Roo.HtmlEditorCore.swapCodes   =[ 
15752     [    8211, "--" ], 
15753     [    8212, "--" ], 
15754     [    8216,  "'" ],  
15755     [    8217, "'" ],  
15756     [    8220, '"' ],  
15757     [    8221, '"' ],  
15758     [    8226, "*" ],  
15759     [    8230, "..." ]
15760 ]; 
15761
15762     /*
15763  * - LGPL
15764  *
15765  * HtmlEditor
15766  * 
15767  */
15768
15769 /**
15770  * @class Roo.bootstrap.HtmlEditor
15771  * @extends Roo.bootstrap.TextArea
15772  * Bootstrap HtmlEditor class
15773
15774  * @constructor
15775  * Create a new HtmlEditor
15776  * @param {Object} config The config object
15777  */
15778
15779 Roo.bootstrap.HtmlEditor = function(config){
15780     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15781     if (!this.toolbars) {
15782         this.toolbars = [];
15783     }
15784     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15785     this.addEvents({
15786             /**
15787              * @event initialize
15788              * Fires when the editor is fully initialized (including the iframe)
15789              * @param {HtmlEditor} this
15790              */
15791             initialize: true,
15792             /**
15793              * @event activate
15794              * Fires when the editor is first receives the focus. Any insertion must wait
15795              * until after this event.
15796              * @param {HtmlEditor} this
15797              */
15798             activate: true,
15799              /**
15800              * @event beforesync
15801              * Fires before the textarea is updated with content from the editor iframe. Return false
15802              * to cancel the sync.
15803              * @param {HtmlEditor} this
15804              * @param {String} html
15805              */
15806             beforesync: true,
15807              /**
15808              * @event beforepush
15809              * Fires before the iframe editor is updated with content from the textarea. Return false
15810              * to cancel the push.
15811              * @param {HtmlEditor} this
15812              * @param {String} html
15813              */
15814             beforepush: true,
15815              /**
15816              * @event sync
15817              * Fires when the textarea is updated with content from the editor iframe.
15818              * @param {HtmlEditor} this
15819              * @param {String} html
15820              */
15821             sync: true,
15822              /**
15823              * @event push
15824              * Fires when the iframe editor is updated with content from the textarea.
15825              * @param {HtmlEditor} this
15826              * @param {String} html
15827              */
15828             push: true,
15829              /**
15830              * @event editmodechange
15831              * Fires when the editor switches edit modes
15832              * @param {HtmlEditor} this
15833              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15834              */
15835             editmodechange: true,
15836             /**
15837              * @event editorevent
15838              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15839              * @param {HtmlEditor} this
15840              */
15841             editorevent: true,
15842             /**
15843              * @event firstfocus
15844              * Fires when on first focus - needed by toolbars..
15845              * @param {HtmlEditor} this
15846              */
15847             firstfocus: true,
15848             /**
15849              * @event autosave
15850              * Auto save the htmlEditor value as a file into Events
15851              * @param {HtmlEditor} this
15852              */
15853             autosave: true,
15854             /**
15855              * @event savedpreview
15856              * preview the saved version of htmlEditor
15857              * @param {HtmlEditor} this
15858              */
15859             savedpreview: true
15860         });
15861 };
15862
15863
15864 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
15865     
15866     
15867       /**
15868      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15869      */
15870     toolbars : false,
15871    
15872      /**
15873      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15874      *                        Roo.resizable.
15875      */
15876     resizable : false,
15877      /**
15878      * @cfg {Number} height (in pixels)
15879      */   
15880     height: 300,
15881    /**
15882      * @cfg {Number} width (in pixels)
15883      */   
15884     width: false,
15885     
15886     /**
15887      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15888      * 
15889      */
15890     stylesheets: false,
15891     
15892     // id of frame..
15893     frameId: false,
15894     
15895     // private properties
15896     validationEvent : false,
15897     deferHeight: true,
15898     initialized : false,
15899     activated : false,
15900     
15901     onFocus : Roo.emptyFn,
15902     iframePad:3,
15903     hideMode:'offsets',
15904     
15905     
15906     tbContainer : false,
15907     
15908     toolbarContainer :function() {
15909         return this.wrap.select('.x-html-editor-tb',true).first();
15910     },
15911
15912     /**
15913      * Protected method that will not generally be called directly. It
15914      * is called when the editor creates its toolbar. Override this method if you need to
15915      * add custom toolbar buttons.
15916      * @param {HtmlEditor} editor
15917      */
15918     createToolbar : function(){
15919         
15920         Roo.log("create toolbars");
15921         
15922         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
15923         this.toolbars[0].render(this.toolbarContainer());
15924         
15925         return;
15926         
15927 //        if (!editor.toolbars || !editor.toolbars.length) {
15928 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15929 //        }
15930 //        
15931 //        for (var i =0 ; i < editor.toolbars.length;i++) {
15932 //            editor.toolbars[i] = Roo.factory(
15933 //                    typeof(editor.toolbars[i]) == 'string' ?
15934 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
15935 //                Roo.bootstrap.HtmlEditor);
15936 //            editor.toolbars[i].init(editor);
15937 //        }
15938     },
15939
15940      
15941     // private
15942     onRender : function(ct, position)
15943     {
15944        // Roo.log("Call onRender: " + this.xtype);
15945         var _t = this;
15946         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15947       
15948         this.wrap = this.inputEl().wrap({
15949             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15950         });
15951         
15952         this.editorcore.onRender(ct, position);
15953          
15954         if (this.resizable) {
15955             this.resizeEl = new Roo.Resizable(this.wrap, {
15956                 pinned : true,
15957                 wrap: true,
15958                 dynamic : true,
15959                 minHeight : this.height,
15960                 height: this.height,
15961                 handles : this.resizable,
15962                 width: this.width,
15963                 listeners : {
15964                     resize : function(r, w, h) {
15965                         _t.onResize(w,h); // -something
15966                     }
15967                 }
15968             });
15969             
15970         }
15971         this.createToolbar(this);
15972        
15973         
15974         if(!this.width && this.resizable){
15975             this.setSize(this.wrap.getSize());
15976         }
15977         if (this.resizeEl) {
15978             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15979             // should trigger onReize..
15980         }
15981         
15982     },
15983
15984     // private
15985     onResize : function(w, h)
15986     {
15987         Roo.log('resize: ' +w + ',' + h );
15988         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15989         var ew = false;
15990         var eh = false;
15991         
15992         if(this.inputEl() ){
15993             if(typeof w == 'number'){
15994                 var aw = w - this.wrap.getFrameWidth('lr');
15995                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15996                 ew = aw;
15997             }
15998             if(typeof h == 'number'){
15999                  var tbh = -11;  // fixme it needs to tool bar size!
16000                 for (var i =0; i < this.toolbars.length;i++) {
16001                     // fixme - ask toolbars for heights?
16002                     tbh += this.toolbars[i].el.getHeight();
16003                     //if (this.toolbars[i].footer) {
16004                     //    tbh += this.toolbars[i].footer.el.getHeight();
16005                     //}
16006                 }
16007               
16008                 
16009                 
16010                 
16011                 
16012                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16013                 ah -= 5; // knock a few pixes off for look..
16014                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16015                 var eh = ah;
16016             }
16017         }
16018         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16019         this.editorcore.onResize(ew,eh);
16020         
16021     },
16022
16023     /**
16024      * Toggles the editor between standard and source edit mode.
16025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16026      */
16027     toggleSourceEdit : function(sourceEditMode)
16028     {
16029         this.editorcore.toggleSourceEdit(sourceEditMode);
16030         
16031         if(this.editorcore.sourceEditMode){
16032             Roo.log('editor - showing textarea');
16033             
16034 //            Roo.log('in');
16035 //            Roo.log(this.syncValue());
16036             this.syncValue();
16037             this.inputEl().removeClass('hide');
16038             this.inputEl().dom.removeAttribute('tabIndex');
16039             this.inputEl().focus();
16040         }else{
16041             Roo.log('editor - hiding textarea');
16042 //            Roo.log('out')
16043 //            Roo.log(this.pushValue()); 
16044             this.pushValue();
16045             
16046             this.inputEl().addClass('hide');
16047             this.inputEl().dom.setAttribute('tabIndex', -1);
16048             //this.deferFocus();
16049         }
16050          
16051         if(this.resizable){
16052             this.setSize(this.wrap.getSize());
16053         }
16054         
16055         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16056     },
16057  
16058     // private (for BoxComponent)
16059     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16060
16061     // private (for BoxComponent)
16062     getResizeEl : function(){
16063         return this.wrap;
16064     },
16065
16066     // private (for BoxComponent)
16067     getPositionEl : function(){
16068         return this.wrap;
16069     },
16070
16071     // private
16072     initEvents : function(){
16073         this.originalValue = this.getValue();
16074     },
16075
16076 //    /**
16077 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16078 //     * @method
16079 //     */
16080 //    markInvalid : Roo.emptyFn,
16081 //    /**
16082 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16083 //     * @method
16084 //     */
16085 //    clearInvalid : Roo.emptyFn,
16086
16087     setValue : function(v){
16088         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16089         this.editorcore.pushValue();
16090     },
16091
16092      
16093     // private
16094     deferFocus : function(){
16095         this.focus.defer(10, this);
16096     },
16097
16098     // doc'ed in Field
16099     focus : function(){
16100         this.editorcore.focus();
16101         
16102     },
16103       
16104
16105     // private
16106     onDestroy : function(){
16107         
16108         
16109         
16110         if(this.rendered){
16111             
16112             for (var i =0; i < this.toolbars.length;i++) {
16113                 // fixme - ask toolbars for heights?
16114                 this.toolbars[i].onDestroy();
16115             }
16116             
16117             this.wrap.dom.innerHTML = '';
16118             this.wrap.remove();
16119         }
16120     },
16121
16122     // private
16123     onFirstFocus : function(){
16124         //Roo.log("onFirstFocus");
16125         this.editorcore.onFirstFocus();
16126          for (var i =0; i < this.toolbars.length;i++) {
16127             this.toolbars[i].onFirstFocus();
16128         }
16129         
16130     },
16131     
16132     // private
16133     syncValue : function()
16134     {   
16135         this.editorcore.syncValue();
16136     },
16137     
16138     pushValue : function()
16139     {   
16140         this.editorcore.pushValue();
16141     }
16142      
16143     
16144     // hide stuff that is not compatible
16145     /**
16146      * @event blur
16147      * @hide
16148      */
16149     /**
16150      * @event change
16151      * @hide
16152      */
16153     /**
16154      * @event focus
16155      * @hide
16156      */
16157     /**
16158      * @event specialkey
16159      * @hide
16160      */
16161     /**
16162      * @cfg {String} fieldClass @hide
16163      */
16164     /**
16165      * @cfg {String} focusClass @hide
16166      */
16167     /**
16168      * @cfg {String} autoCreate @hide
16169      */
16170     /**
16171      * @cfg {String} inputType @hide
16172      */
16173     /**
16174      * @cfg {String} invalidClass @hide
16175      */
16176     /**
16177      * @cfg {String} invalidText @hide
16178      */
16179     /**
16180      * @cfg {String} msgFx @hide
16181      */
16182     /**
16183      * @cfg {String} validateOnBlur @hide
16184      */
16185 });
16186  
16187     
16188    
16189    
16190    
16191       
16192 Roo.namespace('Roo.bootstrap.htmleditor');
16193 /**
16194  * @class Roo.bootstrap.HtmlEditorToolbar1
16195  * Basic Toolbar
16196  * 
16197  * Usage:
16198  *
16199  new Roo.bootstrap.HtmlEditor({
16200     ....
16201     toolbars : [
16202         new Roo.bootstrap.HtmlEditorToolbar1({
16203             disable : { fonts: 1 , format: 1, ..., ... , ...],
16204             btns : [ .... ]
16205         })
16206     }
16207      
16208  * 
16209  * @cfg {Object} disable List of elements to disable..
16210  * @cfg {Array} btns List of additional buttons.
16211  * 
16212  * 
16213  * NEEDS Extra CSS? 
16214  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16215  */
16216  
16217 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16218 {
16219     
16220     Roo.apply(this, config);
16221     
16222     // default disabled, based on 'good practice'..
16223     this.disable = this.disable || {};
16224     Roo.applyIf(this.disable, {
16225         fontSize : true,
16226         colors : true,
16227         specialElements : true
16228     });
16229     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16230     
16231     this.editor = config.editor;
16232     this.editorcore = config.editor.editorcore;
16233     
16234     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16235     
16236     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16237     // dont call parent... till later.
16238 }
16239 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16240      
16241     bar : true,
16242     
16243     editor : false,
16244     editorcore : false,
16245     
16246     
16247     formats : [
16248         "p" ,  
16249         "h1","h2","h3","h4","h5","h6", 
16250         "pre", "code", 
16251         "abbr", "acronym", "address", "cite", "samp", "var",
16252         'div','span'
16253     ],
16254     
16255     onRender : function(ct, position)
16256     {
16257        // Roo.log("Call onRender: " + this.xtype);
16258         
16259        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16260        Roo.log(this.el);
16261        this.el.dom.style.marginBottom = '0';
16262        var _this = this;
16263        var editorcore = this.editorcore;
16264        var editor= this.editor;
16265        
16266        var children = [];
16267        var btn = function(id,cmd , toggle, handler){
16268        
16269             var  event = toggle ? 'toggle' : 'click';
16270        
16271             var a = {
16272                 size : 'sm',
16273                 xtype: 'Button',
16274                 xns: Roo.bootstrap,
16275                 glyphicon : id,
16276                 cmd : id || cmd,
16277                 enableToggle:toggle !== false,
16278                 //html : 'submit'
16279                 pressed : toggle ? false : null,
16280                 listeners : {}
16281             }
16282             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16283                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16284             }
16285             children.push(a);
16286             return a;
16287        }
16288         
16289         var style = {
16290                 xtype: 'Button',
16291                 size : 'sm',
16292                 xns: Roo.bootstrap,
16293                 glyphicon : 'font',
16294                 //html : 'submit'
16295                 menu : {
16296                     xtype: 'Menu',
16297                     xns: Roo.bootstrap,
16298                     items:  []
16299                 }
16300         };
16301         Roo.each(this.formats, function(f) {
16302             style.menu.items.push({
16303                 xtype :'MenuItem',
16304                 xns: Roo.bootstrap,
16305                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16306                 tagname : f,
16307                 listeners : {
16308                     click : function()
16309                     {
16310                         editorcore.insertTag(this.tagname);
16311                         editor.focus();
16312                     }
16313                 }
16314                 
16315             });
16316         });
16317          children.push(style);   
16318             
16319             
16320         btn('bold',false,true);
16321         btn('italic',false,true);
16322         btn('align-left', 'justifyleft',true);
16323         btn('align-center', 'justifycenter',true);
16324         btn('align-right' , 'justifyright',true);
16325         btn('link', false, false, function(btn) {
16326             //Roo.log("create link?");
16327             var url = prompt(this.createLinkText, this.defaultLinkValue);
16328             if(url && url != 'http:/'+'/'){
16329                 this.editorcore.relayCmd('createlink', url);
16330             }
16331         }),
16332         btn('list','insertunorderedlist',true);
16333         btn('pencil', false,true, function(btn){
16334                 Roo.log(this);
16335                 
16336                 this.toggleSourceEdit(btn.pressed);
16337         });
16338         /*
16339         var cog = {
16340                 xtype: 'Button',
16341                 size : 'sm',
16342                 xns: Roo.bootstrap,
16343                 glyphicon : 'cog',
16344                 //html : 'submit'
16345                 menu : {
16346                     xtype: 'Menu',
16347                     xns: Roo.bootstrap,
16348                     items:  []
16349                 }
16350         };
16351         
16352         cog.menu.items.push({
16353             xtype :'MenuItem',
16354             xns: Roo.bootstrap,
16355             html : Clean styles,
16356             tagname : f,
16357             listeners : {
16358                 click : function()
16359                 {
16360                     editorcore.insertTag(this.tagname);
16361                     editor.focus();
16362                 }
16363             }
16364             
16365         });
16366        */
16367         
16368          
16369        this.xtype = 'NavSimplebar';
16370         
16371         for(var i=0;i< children.length;i++) {
16372             
16373             this.buttons.add(this.addxtypeChild(children[i]));
16374             
16375         }
16376         
16377         editor.on('editorevent', this.updateToolbar, this);
16378     },
16379     onBtnClick : function(id)
16380     {
16381        this.editorcore.relayCmd(id);
16382        this.editorcore.focus();
16383     },
16384     
16385     /**
16386      * Protected method that will not generally be called directly. It triggers
16387      * a toolbar update by reading the markup state of the current selection in the editor.
16388      */
16389     updateToolbar: function(){
16390
16391         if(!this.editorcore.activated){
16392             this.editor.onFirstFocus(); // is this neeed?
16393             return;
16394         }
16395
16396         var btns = this.buttons; 
16397         var doc = this.editorcore.doc;
16398         btns.get('bold').setActive(doc.queryCommandState('bold'));
16399         btns.get('italic').setActive(doc.queryCommandState('italic'));
16400         //btns.get('underline').setActive(doc.queryCommandState('underline'));
16401         
16402         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16403         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16404         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16405         
16406         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16407         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16408          /*
16409         
16410         var ans = this.editorcore.getAllAncestors();
16411         if (this.formatCombo) {
16412             
16413             
16414             var store = this.formatCombo.store;
16415             this.formatCombo.setValue("");
16416             for (var i =0; i < ans.length;i++) {
16417                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16418                     // select it..
16419                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16420                     break;
16421                 }
16422             }
16423         }
16424         
16425         
16426         
16427         // hides menus... - so this cant be on a menu...
16428         Roo.bootstrap.MenuMgr.hideAll();
16429         */
16430         Roo.bootstrap.MenuMgr.hideAll();
16431         //this.editorsyncValue();
16432     },
16433     onFirstFocus: function() {
16434         this.buttons.each(function(item){
16435            item.enable();
16436         });
16437     },
16438     toggleSourceEdit : function(sourceEditMode){
16439         
16440           
16441         if(sourceEditMode){
16442             Roo.log("disabling buttons");
16443            this.buttons.each( function(item){
16444                 if(item.cmd != 'pencil'){
16445                     item.disable();
16446                 }
16447             });
16448           
16449         }else{
16450             Roo.log("enabling buttons");
16451             if(this.editorcore.initialized){
16452                 this.buttons.each( function(item){
16453                     item.enable();
16454                 });
16455             }
16456             
16457         }
16458         Roo.log("calling toggole on editor");
16459         // tell the editor that it's been pressed..
16460         this.editor.toggleSourceEdit(sourceEditMode);
16461        
16462     }
16463 });
16464
16465
16466
16467
16468 /*
16469  * Based on:
16470  * Ext JS Library 1.1.1
16471  * Copyright(c) 2006-2007, Ext JS, LLC.
16472  *
16473  * Originally Released Under LGPL - original licence link has changed is not relivant.
16474  *
16475  * Fork - LGPL
16476  * <script type="text/javascript">
16477  */
16478  
16479
16480 /**
16481  * @class Roo.grid.ColumnModel
16482  * @extends Roo.util.Observable
16483  * This is the default implementation of a ColumnModel used by the Grid. It defines
16484  * the columns in the grid.
16485  * <br>Usage:<br>
16486  <pre><code>
16487  var colModel = new Roo.grid.ColumnModel([
16488         {header: "Ticker", width: 60, sortable: true, locked: true},
16489         {header: "Company Name", width: 150, sortable: true},
16490         {header: "Market Cap.", width: 100, sortable: true},
16491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
16492         {header: "Employees", width: 100, sortable: true, resizable: false}
16493  ]);
16494  </code></pre>
16495  * <p>
16496  
16497  * The config options listed for this class are options which may appear in each
16498  * individual column definition.
16499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
16500  * @constructor
16501  * @param {Object} config An Array of column config objects. See this class's
16502  * config objects for details.
16503 */
16504 Roo.grid.ColumnModel = function(config){
16505         /**
16506      * The config passed into the constructor
16507      */
16508     this.config = config;
16509     this.lookup = {};
16510
16511     // if no id, create one
16512     // if the column does not have a dataIndex mapping,
16513     // map it to the order it is in the config
16514     for(var i = 0, len = config.length; i < len; i++){
16515         var c = config[i];
16516         if(typeof c.dataIndex == "undefined"){
16517             c.dataIndex = i;
16518         }
16519         if(typeof c.renderer == "string"){
16520             c.renderer = Roo.util.Format[c.renderer];
16521         }
16522         if(typeof c.id == "undefined"){
16523             c.id = Roo.id();
16524         }
16525         if(c.editor && c.editor.xtype){
16526             c.editor  = Roo.factory(c.editor, Roo.grid);
16527         }
16528         if(c.editor && c.editor.isFormField){
16529             c.editor = new Roo.grid.GridEditor(c.editor);
16530         }
16531         this.lookup[c.id] = c;
16532     }
16533
16534     /**
16535      * The width of columns which have no width specified (defaults to 100)
16536      * @type Number
16537      */
16538     this.defaultWidth = 100;
16539
16540     /**
16541      * Default sortable of columns which have no sortable specified (defaults to false)
16542      * @type Boolean
16543      */
16544     this.defaultSortable = false;
16545
16546     this.addEvents({
16547         /**
16548              * @event widthchange
16549              * Fires when the width of a column changes.
16550              * @param {ColumnModel} this
16551              * @param {Number} columnIndex The column index
16552              * @param {Number} newWidth The new width
16553              */
16554             "widthchange": true,
16555         /**
16556              * @event headerchange
16557              * Fires when the text of a header changes.
16558              * @param {ColumnModel} this
16559              * @param {Number} columnIndex The column index
16560              * @param {Number} newText The new header text
16561              */
16562             "headerchange": true,
16563         /**
16564              * @event hiddenchange
16565              * Fires when a column is hidden or "unhidden".
16566              * @param {ColumnModel} this
16567              * @param {Number} columnIndex The column index
16568              * @param {Boolean} hidden true if hidden, false otherwise
16569              */
16570             "hiddenchange": true,
16571             /**
16572          * @event columnmoved
16573          * Fires when a column is moved.
16574          * @param {ColumnModel} this
16575          * @param {Number} oldIndex
16576          * @param {Number} newIndex
16577          */
16578         "columnmoved" : true,
16579         /**
16580          * @event columlockchange
16581          * Fires when a column's locked state is changed
16582          * @param {ColumnModel} this
16583          * @param {Number} colIndex
16584          * @param {Boolean} locked true if locked
16585          */
16586         "columnlockchange" : true
16587     });
16588     Roo.grid.ColumnModel.superclass.constructor.call(this);
16589 };
16590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
16591     /**
16592      * @cfg {String} header The header text to display in the Grid view.
16593      */
16594     /**
16595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
16597      * specified, the column's index is used as an index into the Record's data Array.
16598      */
16599     /**
16600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16602      */
16603     /**
16604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16605      * Defaults to the value of the {@link #defaultSortable} property.
16606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16607      */
16608     /**
16609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
16610      */
16611     /**
16612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
16613      */
16614     /**
16615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16616      */
16617     /**
16618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16619      */
16620     /**
16621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
16623      * default renderer uses the raw data value.
16624      */
16625        /**
16626      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
16627      */
16628     /**
16629      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
16630      */
16631
16632     /**
16633      * Returns the id of the column at the specified index.
16634      * @param {Number} index The column index
16635      * @return {String} the id
16636      */
16637     getColumnId : function(index){
16638         return this.config[index].id;
16639     },
16640
16641     /**
16642      * Returns the column for a specified id.
16643      * @param {String} id The column id
16644      * @return {Object} the column
16645      */
16646     getColumnById : function(id){
16647         return this.lookup[id];
16648     },
16649
16650     
16651     /**
16652      * Returns the column for a specified dataIndex.
16653      * @param {String} dataIndex The column dataIndex
16654      * @return {Object|Boolean} the column or false if not found
16655      */
16656     getColumnByDataIndex: function(dataIndex){
16657         var index = this.findColumnIndex(dataIndex);
16658         return index > -1 ? this.config[index] : false;
16659     },
16660     
16661     /**
16662      * Returns the index for a specified column id.
16663      * @param {String} id The column id
16664      * @return {Number} the index, or -1 if not found
16665      */
16666     getIndexById : function(id){
16667         for(var i = 0, len = this.config.length; i < len; i++){
16668             if(this.config[i].id == id){
16669                 return i;
16670             }
16671         }
16672         return -1;
16673     },
16674     
16675     /**
16676      * Returns the index for a specified column dataIndex.
16677      * @param {String} dataIndex The column dataIndex
16678      * @return {Number} the index, or -1 if not found
16679      */
16680     
16681     findColumnIndex : function(dataIndex){
16682         for(var i = 0, len = this.config.length; i < len; i++){
16683             if(this.config[i].dataIndex == dataIndex){
16684                 return i;
16685             }
16686         }
16687         return -1;
16688     },
16689     
16690     
16691     moveColumn : function(oldIndex, newIndex){
16692         var c = this.config[oldIndex];
16693         this.config.splice(oldIndex, 1);
16694         this.config.splice(newIndex, 0, c);
16695         this.dataMap = null;
16696         this.fireEvent("columnmoved", this, oldIndex, newIndex);
16697     },
16698
16699     isLocked : function(colIndex){
16700         return this.config[colIndex].locked === true;
16701     },
16702
16703     setLocked : function(colIndex, value, suppressEvent){
16704         if(this.isLocked(colIndex) == value){
16705             return;
16706         }
16707         this.config[colIndex].locked = value;
16708         if(!suppressEvent){
16709             this.fireEvent("columnlockchange", this, colIndex, value);
16710         }
16711     },
16712
16713     getTotalLockedWidth : function(){
16714         var totalWidth = 0;
16715         for(var i = 0; i < this.config.length; i++){
16716             if(this.isLocked(i) && !this.isHidden(i)){
16717                 this.totalWidth += this.getColumnWidth(i);
16718             }
16719         }
16720         return totalWidth;
16721     },
16722
16723     getLockedCount : function(){
16724         for(var i = 0, len = this.config.length; i < len; i++){
16725             if(!this.isLocked(i)){
16726                 return i;
16727             }
16728         }
16729     },
16730
16731     /**
16732      * Returns the number of columns.
16733      * @return {Number}
16734      */
16735     getColumnCount : function(visibleOnly){
16736         if(visibleOnly === true){
16737             var c = 0;
16738             for(var i = 0, len = this.config.length; i < len; i++){
16739                 if(!this.isHidden(i)){
16740                     c++;
16741                 }
16742             }
16743             return c;
16744         }
16745         return this.config.length;
16746     },
16747
16748     /**
16749      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16750      * @param {Function} fn
16751      * @param {Object} scope (optional)
16752      * @return {Array} result
16753      */
16754     getColumnsBy : function(fn, scope){
16755         var r = [];
16756         for(var i = 0, len = this.config.length; i < len; i++){
16757             var c = this.config[i];
16758             if(fn.call(scope||this, c, i) === true){
16759                 r[r.length] = c;
16760             }
16761         }
16762         return r;
16763     },
16764
16765     /**
16766      * Returns true if the specified column is sortable.
16767      * @param {Number} col The column index
16768      * @return {Boolean}
16769      */
16770     isSortable : function(col){
16771         if(typeof this.config[col].sortable == "undefined"){
16772             return this.defaultSortable;
16773         }
16774         return this.config[col].sortable;
16775     },
16776
16777     /**
16778      * Returns the rendering (formatting) function defined for the column.
16779      * @param {Number} col The column index.
16780      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16781      */
16782     getRenderer : function(col){
16783         if(!this.config[col].renderer){
16784             return Roo.grid.ColumnModel.defaultRenderer;
16785         }
16786         return this.config[col].renderer;
16787     },
16788
16789     /**
16790      * Sets the rendering (formatting) function for a column.
16791      * @param {Number} col The column index
16792      * @param {Function} fn The function to use to process the cell's raw data
16793      * to return HTML markup for the grid view. The render function is called with
16794      * the following parameters:<ul>
16795      * <li>Data value.</li>
16796      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16797      * <li>css A CSS style string to apply to the table cell.</li>
16798      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16799      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16800      * <li>Row index</li>
16801      * <li>Column index</li>
16802      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16803      */
16804     setRenderer : function(col, fn){
16805         this.config[col].renderer = fn;
16806     },
16807
16808     /**
16809      * Returns the width for the specified column.
16810      * @param {Number} col The column index
16811      * @return {Number}
16812      */
16813     getColumnWidth : function(col){
16814         return this.config[col].width * 1 || this.defaultWidth;
16815     },
16816
16817     /**
16818      * Sets the width for a column.
16819      * @param {Number} col The column index
16820      * @param {Number} width The new width
16821      */
16822     setColumnWidth : function(col, width, suppressEvent){
16823         this.config[col].width = width;
16824         this.totalWidth = null;
16825         if(!suppressEvent){
16826              this.fireEvent("widthchange", this, col, width);
16827         }
16828     },
16829
16830     /**
16831      * Returns the total width of all columns.
16832      * @param {Boolean} includeHidden True to include hidden column widths
16833      * @return {Number}
16834      */
16835     getTotalWidth : function(includeHidden){
16836         if(!this.totalWidth){
16837             this.totalWidth = 0;
16838             for(var i = 0, len = this.config.length; i < len; i++){
16839                 if(includeHidden || !this.isHidden(i)){
16840                     this.totalWidth += this.getColumnWidth(i);
16841                 }
16842             }
16843         }
16844         return this.totalWidth;
16845     },
16846
16847     /**
16848      * Returns the header for the specified column.
16849      * @param {Number} col The column index
16850      * @return {String}
16851      */
16852     getColumnHeader : function(col){
16853         return this.config[col].header;
16854     },
16855
16856     /**
16857      * Sets the header for a column.
16858      * @param {Number} col The column index
16859      * @param {String} header The new header
16860      */
16861     setColumnHeader : function(col, header){
16862         this.config[col].header = header;
16863         this.fireEvent("headerchange", this, col, header);
16864     },
16865
16866     /**
16867      * Returns the tooltip for the specified column.
16868      * @param {Number} col The column index
16869      * @return {String}
16870      */
16871     getColumnTooltip : function(col){
16872             return this.config[col].tooltip;
16873     },
16874     /**
16875      * Sets the tooltip for a column.
16876      * @param {Number} col The column index
16877      * @param {String} tooltip The new tooltip
16878      */
16879     setColumnTooltip : function(col, tooltip){
16880             this.config[col].tooltip = tooltip;
16881     },
16882
16883     /**
16884      * Returns the dataIndex for the specified column.
16885      * @param {Number} col The column index
16886      * @return {Number}
16887      */
16888     getDataIndex : function(col){
16889         return this.config[col].dataIndex;
16890     },
16891
16892     /**
16893      * Sets the dataIndex for a column.
16894      * @param {Number} col The column index
16895      * @param {Number} dataIndex The new dataIndex
16896      */
16897     setDataIndex : function(col, dataIndex){
16898         this.config[col].dataIndex = dataIndex;
16899     },
16900
16901     
16902     
16903     /**
16904      * Returns true if the cell is editable.
16905      * @param {Number} colIndex The column index
16906      * @param {Number} rowIndex The row index
16907      * @return {Boolean}
16908      */
16909     isCellEditable : function(colIndex, rowIndex){
16910         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16911     },
16912
16913     /**
16914      * Returns the editor defined for the cell/column.
16915      * return false or null to disable editing.
16916      * @param {Number} colIndex The column index
16917      * @param {Number} rowIndex The row index
16918      * @return {Object}
16919      */
16920     getCellEditor : function(colIndex, rowIndex){
16921         return this.config[colIndex].editor;
16922     },
16923
16924     /**
16925      * Sets if a column is editable.
16926      * @param {Number} col The column index
16927      * @param {Boolean} editable True if the column is editable
16928      */
16929     setEditable : function(col, editable){
16930         this.config[col].editable = editable;
16931     },
16932
16933
16934     /**
16935      * Returns true if the column is hidden.
16936      * @param {Number} colIndex The column index
16937      * @return {Boolean}
16938      */
16939     isHidden : function(colIndex){
16940         return this.config[colIndex].hidden;
16941     },
16942
16943
16944     /**
16945      * Returns true if the column width cannot be changed
16946      */
16947     isFixed : function(colIndex){
16948         return this.config[colIndex].fixed;
16949     },
16950
16951     /**
16952      * Returns true if the column can be resized
16953      * @return {Boolean}
16954      */
16955     isResizable : function(colIndex){
16956         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16957     },
16958     /**
16959      * Sets if a column is hidden.
16960      * @param {Number} colIndex The column index
16961      * @param {Boolean} hidden True if the column is hidden
16962      */
16963     setHidden : function(colIndex, hidden){
16964         this.config[colIndex].hidden = hidden;
16965         this.totalWidth = null;
16966         this.fireEvent("hiddenchange", this, colIndex, hidden);
16967     },
16968
16969     /**
16970      * Sets the editor for a column.
16971      * @param {Number} col The column index
16972      * @param {Object} editor The editor object
16973      */
16974     setEditor : function(col, editor){
16975         this.config[col].editor = editor;
16976     }
16977 });
16978
16979 Roo.grid.ColumnModel.defaultRenderer = function(value){
16980         if(typeof value == "string" && value.length < 1){
16981             return "&#160;";
16982         }
16983         return value;
16984 };
16985
16986 // Alias for backwards compatibility
16987 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
16988
16989 /**
16990  * @class Roo.bootstrap.Table.AbstractSelectionModel
16991  * @extends Roo.util.Observable
16992  * Abstract base class for grid SelectionModels.  It provides the interface that should be
16993  * implemented by descendant classes.  This class should not be directly instantiated.
16994  * @constructor
16995  */
16996 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16997     this.locked = false;
16998     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16999 };
17000
17001
17002 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17003     /** @ignore Called by the grid automatically. Do not call directly. */
17004     init : function(grid){
17005         this.grid = grid;
17006         this.initEvents();
17007     },
17008
17009     /**
17010      * Locks the selections.
17011      */
17012     lock : function(){
17013         this.locked = true;
17014     },
17015
17016     /**
17017      * Unlocks the selections.
17018      */
17019     unlock : function(){
17020         this.locked = false;
17021     },
17022
17023     /**
17024      * Returns true if the selections are locked.
17025      * @return {Boolean}
17026      */
17027     isLocked : function(){
17028         return this.locked;
17029     }
17030 });
17031 /**
17032  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17033  * @class Roo.bootstrap.Table.RowSelectionModel
17034  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17035  * It supports multiple selections and keyboard selection/navigation. 
17036  * @constructor
17037  * @param {Object} config
17038  */
17039
17040 Roo.bootstrap.Table.RowSelectionModel = function(config){
17041     Roo.apply(this, config);
17042     this.selections = new Roo.util.MixedCollection(false, function(o){
17043         return o.id;
17044     });
17045
17046     this.last = false;
17047     this.lastActive = false;
17048
17049     this.addEvents({
17050         /**
17051              * @event selectionchange
17052              * Fires when the selection changes
17053              * @param {SelectionModel} this
17054              */
17055             "selectionchange" : true,
17056         /**
17057              * @event afterselectionchange
17058              * Fires after the selection changes (eg. by key press or clicking)
17059              * @param {SelectionModel} this
17060              */
17061             "afterselectionchange" : true,
17062         /**
17063              * @event beforerowselect
17064              * Fires when a row is selected being selected, return false to cancel.
17065              * @param {SelectionModel} this
17066              * @param {Number} rowIndex The selected index
17067              * @param {Boolean} keepExisting False if other selections will be cleared
17068              */
17069             "beforerowselect" : true,
17070         /**
17071              * @event rowselect
17072              * Fires when a row is selected.
17073              * @param {SelectionModel} this
17074              * @param {Number} rowIndex The selected index
17075              * @param {Roo.data.Record} r The record
17076              */
17077             "rowselect" : true,
17078         /**
17079              * @event rowdeselect
17080              * Fires when a row is deselected.
17081              * @param {SelectionModel} this
17082              * @param {Number} rowIndex The selected index
17083              */
17084         "rowdeselect" : true
17085     });
17086     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17087     this.locked = false;
17088 };
17089
17090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17091     /**
17092      * @cfg {Boolean} singleSelect
17093      * True to allow selection of only one row at a time (defaults to false)
17094      */
17095     singleSelect : false,
17096
17097     // private
17098     initEvents : function(){
17099
17100         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17101             this.grid.on("mousedown", this.handleMouseDown, this);
17102         }else{ // allow click to work like normal
17103             this.grid.on("rowclick", this.handleDragableRowClick, this);
17104         }
17105
17106         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17107             "up" : function(e){
17108                 if(!e.shiftKey){
17109                     this.selectPrevious(e.shiftKey);
17110                 }else if(this.last !== false && this.lastActive !== false){
17111                     var last = this.last;
17112                     this.selectRange(this.last,  this.lastActive-1);
17113                     this.grid.getView().focusRow(this.lastActive);
17114                     if(last !== false){
17115                         this.last = last;
17116                     }
17117                 }else{
17118                     this.selectFirstRow();
17119                 }
17120                 this.fireEvent("afterselectionchange", this);
17121             },
17122             "down" : function(e){
17123                 if(!e.shiftKey){
17124                     this.selectNext(e.shiftKey);
17125                 }else if(this.last !== false && this.lastActive !== false){
17126                     var last = this.last;
17127                     this.selectRange(this.last,  this.lastActive+1);
17128                     this.grid.getView().focusRow(this.lastActive);
17129                     if(last !== false){
17130                         this.last = last;
17131                     }
17132                 }else{
17133                     this.selectFirstRow();
17134                 }
17135                 this.fireEvent("afterselectionchange", this);
17136             },
17137             scope: this
17138         });
17139
17140         var view = this.grid.view;
17141         view.on("refresh", this.onRefresh, this);
17142         view.on("rowupdated", this.onRowUpdated, this);
17143         view.on("rowremoved", this.onRemove, this);
17144     },
17145
17146     // private
17147     onRefresh : function(){
17148         var ds = this.grid.dataSource, i, v = this.grid.view;
17149         var s = this.selections;
17150         s.each(function(r){
17151             if((i = ds.indexOfId(r.id)) != -1){
17152                 v.onRowSelect(i);
17153             }else{
17154                 s.remove(r);
17155             }
17156         });
17157     },
17158
17159     // private
17160     onRemove : function(v, index, r){
17161         this.selections.remove(r);
17162     },
17163
17164     // private
17165     onRowUpdated : function(v, index, r){
17166         if(this.isSelected(r)){
17167             v.onRowSelect(index);
17168         }
17169     },
17170
17171     /**
17172      * Select records.
17173      * @param {Array} records The records to select
17174      * @param {Boolean} keepExisting (optional) True to keep existing selections
17175      */
17176     selectRecords : function(records, keepExisting){
17177         if(!keepExisting){
17178             this.clearSelections();
17179         }
17180         var ds = this.grid.dataSource;
17181         for(var i = 0, len = records.length; i < len; i++){
17182             this.selectRow(ds.indexOf(records[i]), true);
17183         }
17184     },
17185
17186     /**
17187      * Gets the number of selected rows.
17188      * @return {Number}
17189      */
17190     getCount : function(){
17191         return this.selections.length;
17192     },
17193
17194     /**
17195      * Selects the first row in the grid.
17196      */
17197     selectFirstRow : function(){
17198         this.selectRow(0);
17199     },
17200
17201     /**
17202      * Select the last row.
17203      * @param {Boolean} keepExisting (optional) True to keep existing selections
17204      */
17205     selectLastRow : function(keepExisting){
17206         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17207     },
17208
17209     /**
17210      * Selects the row immediately following the last selected row.
17211      * @param {Boolean} keepExisting (optional) True to keep existing selections
17212      */
17213     selectNext : function(keepExisting){
17214         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17215             this.selectRow(this.last+1, keepExisting);
17216             this.grid.getView().focusRow(this.last);
17217         }
17218     },
17219
17220     /**
17221      * Selects the row that precedes the last selected row.
17222      * @param {Boolean} keepExisting (optional) True to keep existing selections
17223      */
17224     selectPrevious : function(keepExisting){
17225         if(this.last){
17226             this.selectRow(this.last-1, keepExisting);
17227             this.grid.getView().focusRow(this.last);
17228         }
17229     },
17230
17231     /**
17232      * Returns the selected records
17233      * @return {Array} Array of selected records
17234      */
17235     getSelections : function(){
17236         return [].concat(this.selections.items);
17237     },
17238
17239     /**
17240      * Returns the first selected record.
17241      * @return {Record}
17242      */
17243     getSelected : function(){
17244         return this.selections.itemAt(0);
17245     },
17246
17247
17248     /**
17249      * Clears all selections.
17250      */
17251     clearSelections : function(fast){
17252         if(this.locked) return;
17253         if(fast !== true){
17254             var ds = this.grid.dataSource;
17255             var s = this.selections;
17256             s.each(function(r){
17257                 this.deselectRow(ds.indexOfId(r.id));
17258             }, this);
17259             s.clear();
17260         }else{
17261             this.selections.clear();
17262         }
17263         this.last = false;
17264     },
17265
17266
17267     /**
17268      * Selects all rows.
17269      */
17270     selectAll : function(){
17271         if(this.locked) return;
17272         this.selections.clear();
17273         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17274             this.selectRow(i, true);
17275         }
17276     },
17277
17278     /**
17279      * Returns True if there is a selection.
17280      * @return {Boolean}
17281      */
17282     hasSelection : function(){
17283         return this.selections.length > 0;
17284     },
17285
17286     /**
17287      * Returns True if the specified row is selected.
17288      * @param {Number/Record} record The record or index of the record to check
17289      * @return {Boolean}
17290      */
17291     isSelected : function(index){
17292         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17293         return (r && this.selections.key(r.id) ? true : false);
17294     },
17295
17296     /**
17297      * Returns True if the specified record id is selected.
17298      * @param {String} id The id of record to check
17299      * @return {Boolean}
17300      */
17301     isIdSelected : function(id){
17302         return (this.selections.key(id) ? true : false);
17303     },
17304
17305     // private
17306     handleMouseDown : function(e, t){
17307         var view = this.grid.getView(), rowIndex;
17308         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17309             return;
17310         };
17311         if(e.shiftKey && this.last !== false){
17312             var last = this.last;
17313             this.selectRange(last, rowIndex, e.ctrlKey);
17314             this.last = last; // reset the last
17315             view.focusRow(rowIndex);
17316         }else{
17317             var isSelected = this.isSelected(rowIndex);
17318             if(e.button !== 0 && isSelected){
17319                 view.focusRow(rowIndex);
17320             }else if(e.ctrlKey && isSelected){
17321                 this.deselectRow(rowIndex);
17322             }else if(!isSelected){
17323                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17324                 view.focusRow(rowIndex);
17325             }
17326         }
17327         this.fireEvent("afterselectionchange", this);
17328     },
17329     // private
17330     handleDragableRowClick :  function(grid, rowIndex, e) 
17331     {
17332         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17333             this.selectRow(rowIndex, false);
17334             grid.view.focusRow(rowIndex);
17335              this.fireEvent("afterselectionchange", this);
17336         }
17337     },
17338     
17339     /**
17340      * Selects multiple rows.
17341      * @param {Array} rows Array of the indexes of the row to select
17342      * @param {Boolean} keepExisting (optional) True to keep existing selections
17343      */
17344     selectRows : function(rows, keepExisting){
17345         if(!keepExisting){
17346             this.clearSelections();
17347         }
17348         for(var i = 0, len = rows.length; i < len; i++){
17349             this.selectRow(rows[i], true);
17350         }
17351     },
17352
17353     /**
17354      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17355      * @param {Number} startRow The index of the first row in the range
17356      * @param {Number} endRow The index of the last row in the range
17357      * @param {Boolean} keepExisting (optional) True to retain existing selections
17358      */
17359     selectRange : function(startRow, endRow, keepExisting){
17360         if(this.locked) return;
17361         if(!keepExisting){
17362             this.clearSelections();
17363         }
17364         if(startRow <= endRow){
17365             for(var i = startRow; i <= endRow; i++){
17366                 this.selectRow(i, true);
17367             }
17368         }else{
17369             for(var i = startRow; i >= endRow; i--){
17370                 this.selectRow(i, true);
17371             }
17372         }
17373     },
17374
17375     /**
17376      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17377      * @param {Number} startRow The index of the first row in the range
17378      * @param {Number} endRow The index of the last row in the range
17379      */
17380     deselectRange : function(startRow, endRow, preventViewNotify){
17381         if(this.locked) return;
17382         for(var i = startRow; i <= endRow; i++){
17383             this.deselectRow(i, preventViewNotify);
17384         }
17385     },
17386
17387     /**
17388      * Selects a row.
17389      * @param {Number} row The index of the row to select
17390      * @param {Boolean} keepExisting (optional) True to keep existing selections
17391      */
17392     selectRow : function(index, keepExisting, preventViewNotify){
17393         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17394         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17395             if(!keepExisting || this.singleSelect){
17396                 this.clearSelections();
17397             }
17398             var r = this.grid.dataSource.getAt(index);
17399             this.selections.add(r);
17400             this.last = this.lastActive = index;
17401             if(!preventViewNotify){
17402                 this.grid.getView().onRowSelect(index);
17403             }
17404             this.fireEvent("rowselect", this, index, r);
17405             this.fireEvent("selectionchange", this);
17406         }
17407     },
17408
17409     /**
17410      * Deselects a row.
17411      * @param {Number} row The index of the row to deselect
17412      */
17413     deselectRow : function(index, preventViewNotify){
17414         if(this.locked) return;
17415         if(this.last == index){
17416             this.last = false;
17417         }
17418         if(this.lastActive == index){
17419             this.lastActive = false;
17420         }
17421         var r = this.grid.dataSource.getAt(index);
17422         this.selections.remove(r);
17423         if(!preventViewNotify){
17424             this.grid.getView().onRowDeselect(index);
17425         }
17426         this.fireEvent("rowdeselect", this, index);
17427         this.fireEvent("selectionchange", this);
17428     },
17429
17430     // private
17431     restoreLast : function(){
17432         if(this._last){
17433             this.last = this._last;
17434         }
17435     },
17436
17437     // private
17438     acceptsNav : function(row, col, cm){
17439         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17440     },
17441
17442     // private
17443     onEditorKey : function(field, e){
17444         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17445         if(k == e.TAB){
17446             e.stopEvent();
17447             ed.completeEdit();
17448             if(e.shiftKey){
17449                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17450             }else{
17451                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17452             }
17453         }else if(k == e.ENTER && !e.ctrlKey){
17454             e.stopEvent();
17455             ed.completeEdit();
17456             if(e.shiftKey){
17457                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17458             }else{
17459                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17460             }
17461         }else if(k == e.ESC){
17462             ed.cancelEdit();
17463         }
17464         if(newCell){
17465             g.startEditing(newCell[0], newCell[1]);
17466         }
17467     }
17468 });/*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478  
17479 /**
17480  * @class Roo.bootstrap.PagingToolbar
17481  * @extends Roo.Row
17482  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17483  * @constructor
17484  * Create a new PagingToolbar
17485  * @param {Object} config The config object
17486  */
17487 Roo.bootstrap.PagingToolbar = function(config)
17488 {
17489     // old args format still supported... - xtype is prefered..
17490         // created from xtype...
17491     var ds = config.dataSource;
17492     var items = [];
17493     if (config.items) {
17494         items = config.items;
17495         config.items = [];
17496     }
17497     
17498     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17499     this.ds = ds;
17500     this.cursor = 0;
17501     if (ds) { 
17502         this.bind(ds);
17503     }
17504     
17505     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17506     
17507     // supprot items array.
17508     
17509     Roo.each(items, function(e) {
17510         this.add(Roo.factory(e));
17511     },this);
17512     
17513     
17514     
17515     
17516     
17517 };
17518
17519 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17520     /**
17521      * @cfg {Roo.data.Store} dataSource
17522      * The underlying data store providing the paged data
17523      */
17524     /**
17525      * @cfg {String/HTMLElement/Element} container
17526      * container The id or element that will contain the toolbar
17527      */
17528     /**
17529      * @cfg {Boolean} displayInfo
17530      * True to display the displayMsg (defaults to false)
17531      */
17532     /**
17533      * @cfg {Number} pageSize
17534      * The number of records to display per page (defaults to 20)
17535      */
17536     pageSize: 20,
17537     /**
17538      * @cfg {String} displayMsg
17539      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17540      */
17541     displayMsg : 'Displaying {0} - {1} of {2}',
17542     /**
17543      * @cfg {String} emptyMsg
17544      * The message to display when no records are found (defaults to "No data to display")
17545      */
17546     emptyMsg : 'No data to display',
17547     /**
17548      * Customizable piece of the default paging text (defaults to "Page")
17549      * @type String
17550      */
17551     beforePageText : "Page",
17552     /**
17553      * Customizable piece of the default paging text (defaults to "of %0")
17554      * @type String
17555      */
17556     afterPageText : "of {0}",
17557     /**
17558      * Customizable piece of the default paging text (defaults to "First Page")
17559      * @type String
17560      */
17561     firstText : "First Page",
17562     /**
17563      * Customizable piece of the default paging text (defaults to "Previous Page")
17564      * @type String
17565      */
17566     prevText : "Previous Page",
17567     /**
17568      * Customizable piece of the default paging text (defaults to "Next Page")
17569      * @type String
17570      */
17571     nextText : "Next Page",
17572     /**
17573      * Customizable piece of the default paging text (defaults to "Last Page")
17574      * @type String
17575      */
17576     lastText : "Last Page",
17577     /**
17578      * Customizable piece of the default paging text (defaults to "Refresh")
17579      * @type String
17580      */
17581     refreshText : "Refresh",
17582
17583     // private
17584     onRender : function(ct, position) 
17585     {
17586         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17587         this.navgroup.parentId = this.id;
17588         this.navgroup.onRender(this.el, null);
17589         // add the buttons to the navgroup
17590         
17591         this.first = this.navgroup.addItem({
17592             tooltip: this.firstText,
17593             cls: "prev",
17594             icon : 'fa fa-backward',
17595             disabled: true,
17596             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17597         });
17598         
17599         this.prev =  this.navgroup.addItem({
17600             tooltip: this.prevText,
17601             cls: "prev",
17602             icon : 'fa fa-step-backward',
17603             disabled: true,
17604             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17605         });
17606     //this.addSeparator();
17607         
17608         
17609         var field = this.navgroup.addItem( {
17610             tagtype : 'span',
17611             cls : 'x-paging-position',
17612             
17613             html : this.beforePageText  +
17614                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17615                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17616          } ); //?? escaped?
17617         
17618         this.field = field.el.select('input', true).first();
17619         this.field.on("keydown", this.onPagingKeydown, this);
17620         this.field.on("focus", function(){this.dom.select();});
17621     
17622     
17623         this.afterTextEl =  field.el.select('.x-paging-after').first();
17624         //this.field.setHeight(18);
17625         //this.addSeparator();
17626         this.next = this.navgroup.addItem({
17627             tooltip: this.nextText,
17628             cls: "next",
17629             html : ' <i class="fa fa-step-forward">',
17630             disabled: true,
17631             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17632         });
17633         this.last = this.navgroup.addItem({
17634             tooltip: this.lastText,
17635             icon : 'fa fa-forward',
17636             cls: "next",
17637             disabled: true,
17638             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17639         });
17640     //this.addSeparator();
17641         this.loading = this.navgroup.addItem({
17642             tooltip: this.refreshText,
17643             icon: 'fa fa-refresh',
17644             
17645             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17646         });
17647
17648         if(this.displayInfo){
17649             var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info' } );
17650             this.displayEl = navel.el.select('a',true).first();
17651         }
17652     
17653     },
17654
17655     // private
17656     updateInfo : function(){
17657         if(this.displayEl){
17658             var count = this.ds.getCount();
17659             var msg = count == 0 ?
17660                 this.emptyMsg :
17661                 String.format(
17662                     this.displayMsg,
17663                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17664                 );
17665             this.displayEl.update(msg);
17666         }
17667     },
17668
17669     // private
17670     onLoad : function(ds, r, o){
17671        this.cursor = o.params ? o.params.start : 0;
17672        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
17673
17674        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
17675        this.field.dom.value = ap;
17676        this.first.setDisabled(ap == 1);
17677        this.prev.setDisabled(ap == 1);
17678        this.next.setDisabled(ap == ps);
17679        this.last.setDisabled(ap == ps);
17680        this.loading.enable();
17681        this.updateInfo();
17682     },
17683
17684     // private
17685     getPageData : function(){
17686         var total = this.ds.getTotalCount();
17687         return {
17688             total : total,
17689             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17690             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17691         };
17692     },
17693
17694     // private
17695     onLoadError : function(){
17696         this.loading.enable();
17697     },
17698
17699     // private
17700     onPagingKeydown : function(e){
17701         var k = e.getKey();
17702         var d = this.getPageData();
17703         if(k == e.RETURN){
17704             var v = this.field.dom.value, pageNum;
17705             if(!v || isNaN(pageNum = parseInt(v, 10))){
17706                 this.field.dom.value = d.activePage;
17707                 return;
17708             }
17709             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17710             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17711             e.stopEvent();
17712         }
17713         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
17714         {
17715           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17716           this.field.dom.value = pageNum;
17717           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17718           e.stopEvent();
17719         }
17720         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17721         {
17722           var v = this.field.dom.value, pageNum; 
17723           var increment = (e.shiftKey) ? 10 : 1;
17724           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17725             increment *= -1;
17726           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17727             this.field.dom.value = d.activePage;
17728             return;
17729           }
17730           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17731           {
17732             this.field.dom.value = parseInt(v, 10) + increment;
17733             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17734             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17735           }
17736           e.stopEvent();
17737         }
17738     },
17739
17740     // private
17741     beforeLoad : function(){
17742         if(this.loading){
17743             this.loading.disable();
17744         }
17745     },
17746
17747     // private
17748     onClick : function(which){
17749         var ds = this.ds;
17750         if (!ds) {
17751             return;
17752         }
17753         switch(which){
17754             case "first":
17755                 ds.load({params:{start: 0, limit: this.pageSize}});
17756             break;
17757             case "prev":
17758                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17759             break;
17760             case "next":
17761                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17762             break;
17763             case "last":
17764                 var total = ds.getTotalCount();
17765                 var extra = total % this.pageSize;
17766                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17767                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17768             break;
17769             case "refresh":
17770                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17771             break;
17772         }
17773     },
17774
17775     /**
17776      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17777      * @param {Roo.data.Store} store The data store to unbind
17778      */
17779     unbind : function(ds){
17780         ds.un("beforeload", this.beforeLoad, this);
17781         ds.un("load", this.onLoad, this);
17782         ds.un("loadexception", this.onLoadError, this);
17783         ds.un("remove", this.updateInfo, this);
17784         ds.un("add", this.updateInfo, this);
17785         this.ds = undefined;
17786     },
17787
17788     /**
17789      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17790      * @param {Roo.data.Store} store The data store to bind
17791      */
17792     bind : function(ds){
17793         ds.on("beforeload", this.beforeLoad, this);
17794         ds.on("load", this.onLoad, this);
17795         ds.on("loadexception", this.onLoadError, this);
17796         ds.on("remove", this.updateInfo, this);
17797         ds.on("add", this.updateInfo, this);
17798         this.ds = ds;
17799     }
17800 });/*
17801  * - LGPL
17802  *
17803  * element
17804  * 
17805  */
17806
17807 /**
17808  * @class Roo.bootstrap.MessageBar
17809  * @extends Roo.bootstrap.Component
17810  * Bootstrap MessageBar class
17811  * @cfg {String} html contents of the MessageBar
17812  * @cfg {String} weight (info | success | warning | danger) default info
17813  * @cfg {String} beforeClass insert the bar before the given class
17814  * @cfg {Boolean} closable (true | false) default false
17815  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17816  * 
17817  * @constructor
17818  * Create a new Element
17819  * @param {Object} config The config object
17820  */
17821
17822 Roo.bootstrap.MessageBar = function(config){
17823     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17824 };
17825
17826 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
17827     
17828     html: '',
17829     weight: 'info',
17830     closable: false,
17831     fixed: false,
17832     beforeClass: 'bootstrap-sticky-wrap',
17833     
17834     getAutoCreate : function(){
17835         
17836         var cfg = {
17837             tag: 'div',
17838             cls: 'alert alert-dismissable alert-' + this.weight,
17839             cn: [
17840                 {
17841                     tag: 'span',
17842                     cls: 'message',
17843                     html: this.html || ''
17844                 }
17845             ]
17846         }
17847         
17848         if(this.fixed){
17849             cfg.cls += ' alert-messages-fixed';
17850         }
17851         
17852         if(this.closable){
17853             cfg.cn.push({
17854                 tag: 'button',
17855                 cls: 'close',
17856                 html: 'x'
17857             });
17858         }
17859         
17860         return cfg;
17861     },
17862     
17863     onRender : function(ct, position)
17864     {
17865         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17866         
17867         if(!this.el){
17868             var cfg = Roo.apply({},  this.getAutoCreate());
17869             cfg.id = Roo.id();
17870             
17871             if (this.cls) {
17872                 cfg.cls += ' ' + this.cls;
17873             }
17874             if (this.style) {
17875                 cfg.style = this.style;
17876             }
17877             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17878             
17879             this.el.setVisibilityMode(Roo.Element.DISPLAY);
17880         }
17881         
17882         this.el.select('>button.close').on('click', this.hide, this);
17883         
17884     },
17885     
17886     show : function()
17887     {
17888         if (!this.rendered) {
17889             this.render();
17890         }
17891         
17892         this.el.show();
17893         
17894         this.fireEvent('show', this);
17895         
17896     },
17897     
17898     hide : function()
17899     {
17900         if (!this.rendered) {
17901             this.render();
17902         }
17903         
17904         this.el.hide();
17905         
17906         this.fireEvent('hide', this);
17907     },
17908     
17909     update : function()
17910     {
17911 //        var e = this.el.dom.firstChild;
17912 //        
17913 //        if(this.closable){
17914 //            e = e.nextSibling;
17915 //        }
17916 //        
17917 //        e.data = this.html || '';
17918
17919         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
17920     }
17921    
17922 });
17923
17924  
17925
17926