examples/bootstrap/dashboard3.bjs
[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     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * 
3003  * @constructor
3004  * Create a new Sidebar
3005  * @param {Object} config The config object
3006  */
3007
3008
3009 Roo.bootstrap.NavHeaderbar = function(config){
3010     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3011 };
3012
3013 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3014     
3015     position: '',
3016     brand: '',
3017     brand_href: false,
3018     
3019     
3020     getAutoCreate : function(){
3021         
3022         
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: [
3029                 {
3030                     tag: 'div',
3031                     cls: 'navbar-header',
3032                     cn: [
3033                         {
3034                         tag: 'button',
3035                         type: 'button',
3036                         cls: 'navbar-toggle',
3037                         'data-toggle': 'collapse',
3038                         cn: [
3039                             {
3040                                 tag: 'span',
3041                                 cls: 'sr-only',
3042                                 html: 'Toggle navigation'
3043                             },
3044                             {
3045                                 tag: 'span',
3046                                 cls: 'icon-bar'
3047                             },
3048                             {
3049                                 tag: 'span',
3050                                 cls: 'icon-bar'
3051                             },
3052                             {
3053                                 tag: 'span',
3054                                 cls: 'icon-bar'
3055                             }
3056                         ]
3057                         }
3058                     ]
3059                 },
3060                 {
3061                 tag: 'div',
3062                 cls: 'collapse navbar-collapse'
3063                 }
3064             ]
3065         };
3066         
3067         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3068         
3069         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3070             cfg.cls += ' navbar-' + this.position;
3071             
3072             // tag can override this..
3073             
3074             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3075         }
3076         
3077         if (this.brand !== '') {
3078             cfg.cn[0].cn.push({
3079                 tag: 'a',
3080                 href: this.brand_href ? this.brand_href : '#',
3081                 cls: 'navbar-brand',
3082                 cn: [
3083                 this.brand
3084                 ]
3085             });
3086         }
3087         
3088         if(this.main){
3089             cfg.cls += ' main-nav';
3090         }
3091         
3092         
3093         return cfg;
3094
3095         
3096     }
3097     
3098     
3099     
3100 });
3101
3102
3103
3104  
3105
3106  /*
3107  * - LGPL
3108  *
3109  * navbar
3110  * 
3111  */
3112
3113 /**
3114  * @class Roo.bootstrap.NavSidebar
3115  * @extends Roo.bootstrap.Navbar
3116  * Bootstrap Sidebar class
3117  * 
3118  * @constructor
3119  * Create a new Sidebar
3120  * @param {Object} config The config object
3121  */
3122
3123
3124 Roo.bootstrap.NavSidebar = function(config){
3125     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3126 };
3127
3128 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3129     
3130     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3131     
3132     getAutoCreate : function(){
3133         
3134         
3135         return  {
3136             tag: 'div',
3137             cls: 'sidebar sidebar-nav'
3138         };
3139     
3140         
3141     }
3142     
3143     
3144     
3145 });
3146
3147
3148
3149  
3150
3151  /*
3152  * - LGPL
3153  *
3154  * nav group
3155  * 
3156  */
3157
3158 /**
3159  * @class Roo.bootstrap.NavGroup
3160  * @extends Roo.bootstrap.Component
3161  * Bootstrap NavGroup class
3162  * @cfg {String} align left | right
3163  * @cfg {Boolean} inverse false | true
3164  * @cfg {String} type (nav|pills|tab) default nav
3165  * @cfg {String} navId - reference Id for navbar.
3166
3167  * 
3168  * @constructor
3169  * Create a new nav group
3170  * @param {Object} config The config object
3171  */
3172
3173 Roo.bootstrap.NavGroup = function(config){
3174     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3175     this.navItems = [];
3176     Roo.bootstrap.NavGroup.register(this);
3177      this.addEvents({
3178         /**
3179              * @event changed
3180              * Fires when the active item changes
3181              * @param {Roo.bootstrap.NavGroup} this
3182              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3183              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3184          */
3185         'changed': true
3186      });
3187     
3188 };
3189
3190 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3191     
3192     align: '',
3193     inverse: false,
3194     form: false,
3195     type: 'nav',
3196     navId : '',
3197     // private
3198     
3199     navItems : false,
3200     
3201     getAutoCreate : function()
3202     {
3203         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3204         
3205         cfg = {
3206             tag : 'ul',
3207             cls: 'nav' 
3208         }
3209         
3210         if (['tabs','pills'].indexOf(this.type)!==-1) {
3211             cfg.cls += ' nav-' + this.type
3212         } else {
3213             if (this.type!=='nav') {
3214                 Roo.log('nav type must be nav/tabs/pills')
3215             }
3216             cfg.cls += ' navbar-nav'
3217         }
3218         
3219         if (this.parent().sidebar) {
3220             cfg = {
3221                 tag: 'ul',
3222                 cls: 'dashboard-menu sidebar-menu'
3223             }
3224             
3225             return cfg;
3226         }
3227         
3228         if (this.form === true) {
3229             cfg = {
3230                 tag: 'form',
3231                 cls: 'navbar-form'
3232             }
3233             
3234             if (this.align === 'right') {
3235                 cfg.cls += ' navbar-right';
3236             } else {
3237                 cfg.cls += ' navbar-left';
3238             }
3239         }
3240         
3241         if (this.align === 'right') {
3242             cfg.cls += ' navbar-right';
3243         }
3244         
3245         if (this.inverse) {
3246             cfg.cls += ' navbar-inverse';
3247             
3248         }
3249         
3250         
3251         return cfg;
3252     },
3253     
3254     setActiveItem : function(item)
3255     {
3256         var prev = false;
3257         Roo.each(this.navItems, function(v){
3258             if (v == item) {
3259                 return ;
3260             }
3261             if (v.isActive()) {
3262                 v.setActive(false, true);
3263                 prev = v;
3264                 
3265             }
3266             
3267         });
3268
3269         item.setActive(true, true);
3270         this.fireEvent('changed', this, item, prev);
3271         
3272         
3273     },
3274     
3275     addItem : function(cfg)
3276     {
3277         var cn = new Roo.bootstrap.NavItem(cfg);
3278         this.register(cn);
3279         cn.parentId = this.id;
3280         cn.onRender(this.el, null);
3281         return cn;
3282     },
3283     
3284     register : function(item)
3285     {
3286         this.navItems.push( item);
3287         item.navId = this.navId;
3288     
3289     },
3290     getNavItem: function(tabId)
3291     {
3292         var ret = false;
3293         Roo.each(this.navItems, function(e) {
3294             if (e.tabId == tabId) {
3295                ret =  e;
3296                return false;
3297             }
3298             return true;
3299             
3300         });
3301         return ret;
3302     }
3303     
3304     
3305     
3306     
3307 });
3308
3309  
3310 Roo.apply(Roo.bootstrap.NavGroup, {
3311     
3312     groups: {},
3313     
3314     register : function(navgrp)
3315     {
3316         this.groups[navgrp.navId] = navgrp;
3317         
3318     },
3319     get: function(navId) {
3320         return this.groups[navId];
3321     }
3322     
3323     
3324     
3325 });
3326
3327  /*
3328  * - LGPL
3329  *
3330  * row
3331  * 
3332  */
3333
3334 /**
3335  * @class Roo.bootstrap.NavItem
3336  * @extends Roo.bootstrap.Component
3337  * Bootstrap Navbar.NavItem class
3338  * @cfg {String} href  link to
3339  * @cfg {String} html content of button
3340  * @cfg {String} badge text inside badge
3341  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3342  * @cfg {String} glyphicon name of glyphicon
3343  * @cfg {String} icon name of font awesome icon
3344  * @cfg {Boolean} active Is item active
3345  * @cfg {Boolean} disabled Is item disabled
3346  
3347  * @cfg {Boolean} preventDefault (true | false) default false
3348  * @cfg {String} tabId the tab that this item activates.
3349  * @cfg {String} tagtype (a|span) render as a href or span?
3350   
3351  * @constructor
3352  * Create a new Navbar Item
3353  * @param {Object} config The config object
3354  */
3355 Roo.bootstrap.NavItem = function(config){
3356     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3357     this.addEvents({
3358         // raw events
3359         /**
3360          * @event click
3361          * The raw click event for the entire grid.
3362          * @param {Roo.EventObject} e
3363          */
3364         "click" : true,
3365          /**
3366             * @event changed
3367             * Fires when the active item active state changes
3368             * @param {Roo.bootstrap.NavItem} this
3369             * @param {boolean} state the new state
3370              
3371          */
3372         'changed': true
3373     });
3374    
3375 };
3376
3377 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3378     
3379     href: false,
3380     html: '',
3381     badge: '',
3382     icon: false,
3383     glyphicon: false,
3384     active: false,
3385     preventDefault : false,
3386     tabId : false,
3387     tagtype : 'a',
3388     disabled : false,
3389     
3390     getAutoCreate : function(){
3391          
3392         var cfg = {
3393             tag: 'li',
3394             cls: 'nav-item',
3395             cn : [
3396                 {
3397                     tag: this.tagtype,
3398                     href : this.href || "#",
3399                     html: this.html || ''
3400                 }
3401             ]
3402         }
3403             
3404         if (this.active) {
3405             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3406         }
3407             
3408         // glyphicon and icon go before content..
3409         if (this.glyphicon || this.icon) {
3410              if (this.icon) {
3411                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3412             } else {
3413                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3414             }
3415         }
3416         
3417         
3418         
3419         if (this.menu) {
3420             
3421             cfg.cn[0].html += " <span class='caret'></span>";
3422          
3423         }
3424         
3425         if (this.badge !== '') {
3426              
3427             cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3428         }
3429         if (this.disabled) {
3430             cfg.cls += ' disabled';
3431         }
3432         
3433         
3434         return cfg;
3435     },
3436     initEvents: function() {
3437        // Roo.log('init events?');
3438        // Roo.log(this.el.dom);
3439         if (typeof (this.menu) != 'undefined') {
3440             this.menu.parentType = this.xtype;
3441             this.menu.triggerEl = this.el;
3442             this.addxtype(Roo.apply({}, this.menu));
3443         }
3444
3445        
3446         this.el.select('a',true).on('click', this.onClick, this);
3447         // at this point parent should be available..
3448         this.parent().register(this);
3449     },
3450     
3451     onClick : function(e)
3452     {
3453          
3454         if(this.preventDefault){
3455             e.preventDefault();
3456         }
3457         if (this.disabled) {
3458             return;
3459         }
3460         Roo.log("fire event clicked");
3461         if(this.fireEvent('click', this, e) === false){
3462             return;
3463         };
3464         
3465         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3466             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3467                 this.parent().setActiveItem(this);
3468             }
3469             
3470             
3471             
3472         } 
3473     },
3474     
3475     isActive: function () {
3476         return this.active
3477     },
3478     setActive : function(state, fire)
3479     {
3480         this.active = state;
3481         if (!state ) {
3482             this.el.removeClass('active');
3483         } else if (!this.el.hasClass('active')) {
3484             this.el.addClass('active');
3485         }
3486         if (fire) {
3487             this.fireEvent('changed', this, state);
3488         }
3489         
3490         
3491     },
3492      // this should not be here...
3493     setDisabled : function(state)
3494     {
3495         this.disabled = state;
3496         if (!state ) {
3497             this.el.removeClass('disabled');
3498         } else if (!this.el.hasClass('disabled')) {
3499             this.el.addClass('disabled');
3500         }
3501         
3502     }
3503 });
3504  
3505
3506  /*
3507  * - LGPL
3508  *
3509  * sidebar item
3510  *
3511  *  li
3512  *    <span> icon </span>
3513  *    <span> text </span>
3514  *    <span>badge </span>
3515  */
3516
3517 /**
3518  * @class Roo.bootstrap.NavSidebarItem
3519  * @extends Roo.bootstrap.NavItem
3520  * Bootstrap Navbar.NavSidebarItem class
3521  * @constructor
3522  * Create a new Navbar Button
3523  * @param {Object} config The config object
3524  */
3525 Roo.bootstrap.NavSidebarItem = function(config){
3526     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3527     this.addEvents({
3528         // raw events
3529         /**
3530          * @event click
3531          * The raw click event for the entire grid.
3532          * @param {Roo.EventObject} e
3533          */
3534         "click" : true,
3535          /**
3536             * @event changed
3537             * Fires when the active item active state changes
3538             * @param {Roo.bootstrap.NavSidebarItem} this
3539             * @param {boolean} state the new state
3540              
3541          */
3542         'changed': true
3543     });
3544    
3545 };
3546
3547 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3548     
3549     
3550     getAutoCreate : function(){
3551         
3552         
3553         var a = {
3554                 tag: 'a',
3555                 href : this.href || '#',
3556                 cls: '',
3557                 html : '',
3558                 cn : []
3559         };
3560         var cfg = {
3561             tag: 'li',
3562             cls: '',
3563             cn: [ a ]
3564         }
3565         var span = {
3566             tag: 'span',
3567             html : this.html || ''
3568         }
3569         
3570         
3571         if (this.active) {
3572             cfg.cls += ' active';
3573         }
3574         
3575         // left icon..
3576         if (this.glyphicon || this.icon) {
3577             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3578             a.cn.push({ tag : 'i', cls : c }) ;
3579         }
3580         // html..
3581         a.cn.push(span);
3582         // then badge..
3583         if (this.badge !== '') {
3584             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3585         }
3586         // fi
3587         if (this.menu) {
3588             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3589             a.cls += 'dropdown-toggle treeview' ;
3590             
3591         }
3592         
3593         
3594         
3595         return cfg;
3596          
3597            
3598     }
3599    
3600      
3601  
3602 });
3603  
3604
3605  /*
3606  * - LGPL
3607  *
3608  * row
3609  * 
3610  */
3611
3612 /**
3613  * @class Roo.bootstrap.Row
3614  * @extends Roo.bootstrap.Component
3615  * Bootstrap Row class (contains columns...)
3616  * 
3617  * @constructor
3618  * Create a new Row
3619  * @param {Object} config The config object
3620  */
3621
3622 Roo.bootstrap.Row = function(config){
3623     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3624 };
3625
3626 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3627     
3628     getAutoCreate : function(){
3629        return {
3630             cls: 'row clearfix'
3631        };
3632     }
3633     
3634     
3635 });
3636
3637  
3638
3639  /*
3640  * - LGPL
3641  *
3642  * element
3643  * 
3644  */
3645
3646 /**
3647  * @class Roo.bootstrap.Element
3648  * @extends Roo.bootstrap.Component
3649  * Bootstrap Element class
3650  * @cfg {String} html contents of the element
3651  * @cfg {String} tag tag of the element
3652  * @cfg {String} cls class of the element
3653  * 
3654  * @constructor
3655  * Create a new Element
3656  * @param {Object} config The config object
3657  */
3658
3659 Roo.bootstrap.Element = function(config){
3660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3661 };
3662
3663 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3664     
3665     tag: 'div',
3666     cls: '',
3667     html: '',
3668      
3669     
3670     getAutoCreate : function(){
3671         
3672         var cfg = {
3673             tag: this.tag,
3674             cls: this.cls,
3675             html: this.html
3676         }
3677         
3678         
3679         
3680         return cfg;
3681     }
3682    
3683 });
3684
3685  
3686
3687  /*
3688  * - LGPL
3689  *
3690  * pagination
3691  * 
3692  */
3693
3694 /**
3695  * @class Roo.bootstrap.Pagination
3696  * @extends Roo.bootstrap.Component
3697  * Bootstrap Pagination class
3698  * @cfg {String} size xs | sm | md | lg
3699  * @cfg {Boolean} inverse false | true
3700  * 
3701  * @constructor
3702  * Create a new Pagination
3703  * @param {Object} config The config object
3704  */
3705
3706 Roo.bootstrap.Pagination = function(config){
3707     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3708 };
3709
3710 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3711     
3712     cls: false,
3713     size: false,
3714     inverse: false,
3715     
3716     getAutoCreate : function(){
3717         var cfg = {
3718             tag: 'ul',
3719                 cls: 'pagination'
3720         };
3721         if (this.inverse) {
3722             cfg.cls += ' inverse';
3723         }
3724         if (this.html) {
3725             cfg.html=this.html;
3726         }
3727         if (this.cls) {
3728             cfg.cls += " " + this.cls;
3729         }
3730         return cfg;
3731     }
3732    
3733 });
3734
3735  
3736
3737  /*
3738  * - LGPL
3739  *
3740  * Pagination item
3741  * 
3742  */
3743
3744
3745 /**
3746  * @class Roo.bootstrap.PaginationItem
3747  * @extends Roo.bootstrap.Component
3748  * Bootstrap PaginationItem class
3749  * @cfg {String} html text
3750  * @cfg {String} href the link
3751  * @cfg {Boolean} preventDefault (true | false) default true
3752  * @cfg {Boolean} active (true | false) default false
3753  * 
3754  * 
3755  * @constructor
3756  * Create a new PaginationItem
3757  * @param {Object} config The config object
3758  */
3759
3760
3761 Roo.bootstrap.PaginationItem = function(config){
3762     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3763     this.addEvents({
3764         // raw events
3765         /**
3766          * @event click
3767          * The raw click event for the entire grid.
3768          * @param {Roo.EventObject} e
3769          */
3770         "click" : true
3771     });
3772 };
3773
3774 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3775     
3776     href : false,
3777     html : false,
3778     preventDefault: true,
3779     active : false,
3780     cls : false,
3781     
3782     getAutoCreate : function(){
3783         var cfg= {
3784             tag: 'li',
3785             cn: [
3786                 {
3787                     tag : 'a',
3788                     href : this.href ? this.href : '#',
3789                     html : this.html ? this.html : ''
3790                 }
3791             ]
3792         };
3793         
3794         if(this.cls){
3795             cfg.cls = this.cls;
3796         }
3797         
3798         if(this.active){
3799             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3800         }
3801         
3802         return cfg;
3803     },
3804     
3805     initEvents: function() {
3806         
3807         this.el.on('click', this.onClick, this);
3808         
3809     },
3810     onClick : function(e)
3811     {
3812         Roo.log('PaginationItem on click ');
3813         if(this.preventDefault){
3814             e.preventDefault();
3815         }
3816         
3817         this.fireEvent('click', this, e);
3818     }
3819    
3820 });
3821
3822  
3823
3824  /*
3825  * - LGPL
3826  *
3827  * slider
3828  * 
3829  */
3830
3831
3832 /**
3833  * @class Roo.bootstrap.Slider
3834  * @extends Roo.bootstrap.Component
3835  * Bootstrap Slider class
3836  *    
3837  * @constructor
3838  * Create a new Slider
3839  * @param {Object} config The config object
3840  */
3841
3842 Roo.bootstrap.Slider = function(config){
3843     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3844 };
3845
3846 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3847     
3848     getAutoCreate : function(){
3849         
3850         var cfg = {
3851             tag: 'div',
3852             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3853             cn: [
3854                 {
3855                     tag: 'a',
3856                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3857                 }
3858             ]
3859         }
3860         
3861         return cfg;
3862     }
3863    
3864 });
3865
3866  /*
3867  * Based on:
3868  * Ext JS Library 1.1.1
3869  * Copyright(c) 2006-2007, Ext JS, LLC.
3870  *
3871  * Originally Released Under LGPL - original licence link has changed is not relivant.
3872  *
3873  * Fork - LGPL
3874  * <script type="text/javascript">
3875  */
3876  
3877
3878 /**
3879  * @class Roo.grid.ColumnModel
3880  * @extends Roo.util.Observable
3881  * This is the default implementation of a ColumnModel used by the Grid. It defines
3882  * the columns in the grid.
3883  * <br>Usage:<br>
3884  <pre><code>
3885  var colModel = new Roo.grid.ColumnModel([
3886         {header: "Ticker", width: 60, sortable: true, locked: true},
3887         {header: "Company Name", width: 150, sortable: true},
3888         {header: "Market Cap.", width: 100, sortable: true},
3889         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3890         {header: "Employees", width: 100, sortable: true, resizable: false}
3891  ]);
3892  </code></pre>
3893  * <p>
3894  
3895  * The config options listed for this class are options which may appear in each
3896  * individual column definition.
3897  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3898  * @constructor
3899  * @param {Object} config An Array of column config objects. See this class's
3900  * config objects for details.
3901 */
3902 Roo.grid.ColumnModel = function(config){
3903         /**
3904      * The config passed into the constructor
3905      */
3906     this.config = config;
3907     this.lookup = {};
3908
3909     // if no id, create one
3910     // if the column does not have a dataIndex mapping,
3911     // map it to the order it is in the config
3912     for(var i = 0, len = config.length; i < len; i++){
3913         var c = config[i];
3914         if(typeof c.dataIndex == "undefined"){
3915             c.dataIndex = i;
3916         }
3917         if(typeof c.renderer == "string"){
3918             c.renderer = Roo.util.Format[c.renderer];
3919         }
3920         if(typeof c.id == "undefined"){
3921             c.id = Roo.id();
3922         }
3923         if(c.editor && c.editor.xtype){
3924             c.editor  = Roo.factory(c.editor, Roo.grid);
3925         }
3926         if(c.editor && c.editor.isFormField){
3927             c.editor = new Roo.grid.GridEditor(c.editor);
3928         }
3929         this.lookup[c.id] = c;
3930     }
3931
3932     /**
3933      * The width of columns which have no width specified (defaults to 100)
3934      * @type Number
3935      */
3936     this.defaultWidth = 100;
3937
3938     /**
3939      * Default sortable of columns which have no sortable specified (defaults to false)
3940      * @type Boolean
3941      */
3942     this.defaultSortable = false;
3943
3944     this.addEvents({
3945         /**
3946              * @event widthchange
3947              * Fires when the width of a column changes.
3948              * @param {ColumnModel} this
3949              * @param {Number} columnIndex The column index
3950              * @param {Number} newWidth The new width
3951              */
3952             "widthchange": true,
3953         /**
3954              * @event headerchange
3955              * Fires when the text of a header changes.
3956              * @param {ColumnModel} this
3957              * @param {Number} columnIndex The column index
3958              * @param {Number} newText The new header text
3959              */
3960             "headerchange": true,
3961         /**
3962              * @event hiddenchange
3963              * Fires when a column is hidden or "unhidden".
3964              * @param {ColumnModel} this
3965              * @param {Number} columnIndex The column index
3966              * @param {Boolean} hidden true if hidden, false otherwise
3967              */
3968             "hiddenchange": true,
3969             /**
3970          * @event columnmoved
3971          * Fires when a column is moved.
3972          * @param {ColumnModel} this
3973          * @param {Number} oldIndex
3974          * @param {Number} newIndex
3975          */
3976         "columnmoved" : true,
3977         /**
3978          * @event columlockchange
3979          * Fires when a column's locked state is changed
3980          * @param {ColumnModel} this
3981          * @param {Number} colIndex
3982          * @param {Boolean} locked true if locked
3983          */
3984         "columnlockchange" : true
3985     });
3986     Roo.grid.ColumnModel.superclass.constructor.call(this);
3987 };
3988 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3989     /**
3990      * @cfg {String} header The header text to display in the Grid view.
3991      */
3992     /**
3993      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3994      * {@link Roo.data.Record} definition from which to draw the column's value. If not
3995      * specified, the column's index is used as an index into the Record's data Array.
3996      */
3997     /**
3998      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
3999      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4000      */
4001     /**
4002      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4003      * Defaults to the value of the {@link #defaultSortable} property.
4004      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4005      */
4006     /**
4007      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4008      */
4009     /**
4010      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4011      */
4012     /**
4013      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4014      */
4015     /**
4016      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4017      */
4018     /**
4019      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4020      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4021      * default renderer uses the raw data value.
4022      */
4023        /**
4024      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4025      */
4026     /**
4027      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4028      */
4029
4030     /**
4031      * Returns the id of the column at the specified index.
4032      * @param {Number} index The column index
4033      * @return {String} the id
4034      */
4035     getColumnId : function(index){
4036         return this.config[index].id;
4037     },
4038
4039     /**
4040      * Returns the column for a specified id.
4041      * @param {String} id The column id
4042      * @return {Object} the column
4043      */
4044     getColumnById : function(id){
4045         return this.lookup[id];
4046     },
4047
4048     
4049     /**
4050      * Returns the column for a specified dataIndex.
4051      * @param {String} dataIndex The column dataIndex
4052      * @return {Object|Boolean} the column or false if not found
4053      */
4054     getColumnByDataIndex: function(dataIndex){
4055         var index = this.findColumnIndex(dataIndex);
4056         return index > -1 ? this.config[index] : false;
4057     },
4058     
4059     /**
4060      * Returns the index for a specified column id.
4061      * @param {String} id The column id
4062      * @return {Number} the index, or -1 if not found
4063      */
4064     getIndexById : function(id){
4065         for(var i = 0, len = this.config.length; i < len; i++){
4066             if(this.config[i].id == id){
4067                 return i;
4068             }
4069         }
4070         return -1;
4071     },
4072     
4073     /**
4074      * Returns the index for a specified column dataIndex.
4075      * @param {String} dataIndex The column dataIndex
4076      * @return {Number} the index, or -1 if not found
4077      */
4078     
4079     findColumnIndex : function(dataIndex){
4080         for(var i = 0, len = this.config.length; i < len; i++){
4081             if(this.config[i].dataIndex == dataIndex){
4082                 return i;
4083             }
4084         }
4085         return -1;
4086     },
4087     
4088     
4089     moveColumn : function(oldIndex, newIndex){
4090         var c = this.config[oldIndex];
4091         this.config.splice(oldIndex, 1);
4092         this.config.splice(newIndex, 0, c);
4093         this.dataMap = null;
4094         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4095     },
4096
4097     isLocked : function(colIndex){
4098         return this.config[colIndex].locked === true;
4099     },
4100
4101     setLocked : function(colIndex, value, suppressEvent){
4102         if(this.isLocked(colIndex) == value){
4103             return;
4104         }
4105         this.config[colIndex].locked = value;
4106         if(!suppressEvent){
4107             this.fireEvent("columnlockchange", this, colIndex, value);
4108         }
4109     },
4110
4111     getTotalLockedWidth : function(){
4112         var totalWidth = 0;
4113         for(var i = 0; i < this.config.length; i++){
4114             if(this.isLocked(i) && !this.isHidden(i)){
4115                 this.totalWidth += this.getColumnWidth(i);
4116             }
4117         }
4118         return totalWidth;
4119     },
4120
4121     getLockedCount : function(){
4122         for(var i = 0, len = this.config.length; i < len; i++){
4123             if(!this.isLocked(i)){
4124                 return i;
4125             }
4126         }
4127     },
4128
4129     /**
4130      * Returns the number of columns.
4131      * @return {Number}
4132      */
4133     getColumnCount : function(visibleOnly){
4134         if(visibleOnly === true){
4135             var c = 0;
4136             for(var i = 0, len = this.config.length; i < len; i++){
4137                 if(!this.isHidden(i)){
4138                     c++;
4139                 }
4140             }
4141             return c;
4142         }
4143         return this.config.length;
4144     },
4145
4146     /**
4147      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4148      * @param {Function} fn
4149      * @param {Object} scope (optional)
4150      * @return {Array} result
4151      */
4152     getColumnsBy : function(fn, scope){
4153         var r = [];
4154         for(var i = 0, len = this.config.length; i < len; i++){
4155             var c = this.config[i];
4156             if(fn.call(scope||this, c, i) === true){
4157                 r[r.length] = c;
4158             }
4159         }
4160         return r;
4161     },
4162
4163     /**
4164      * Returns true if the specified column is sortable.
4165      * @param {Number} col The column index
4166      * @return {Boolean}
4167      */
4168     isSortable : function(col){
4169         if(typeof this.config[col].sortable == "undefined"){
4170             return this.defaultSortable;
4171         }
4172         return this.config[col].sortable;
4173     },
4174
4175     /**
4176      * Returns the rendering (formatting) function defined for the column.
4177      * @param {Number} col The column index.
4178      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4179      */
4180     getRenderer : function(col){
4181         if(!this.config[col].renderer){
4182             return Roo.grid.ColumnModel.defaultRenderer;
4183         }
4184         return this.config[col].renderer;
4185     },
4186
4187     /**
4188      * Sets the rendering (formatting) function for a column.
4189      * @param {Number} col The column index
4190      * @param {Function} fn The function to use to process the cell's raw data
4191      * to return HTML markup for the grid view. The render function is called with
4192      * the following parameters:<ul>
4193      * <li>Data value.</li>
4194      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4195      * <li>css A CSS style string to apply to the table cell.</li>
4196      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4197      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4198      * <li>Row index</li>
4199      * <li>Column index</li>
4200      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4201      */
4202     setRenderer : function(col, fn){
4203         this.config[col].renderer = fn;
4204     },
4205
4206     /**
4207      * Returns the width for the specified column.
4208      * @param {Number} col The column index
4209      * @return {Number}
4210      */
4211     getColumnWidth : function(col){
4212         return this.config[col].width * 1 || this.defaultWidth;
4213     },
4214
4215     /**
4216      * Sets the width for a column.
4217      * @param {Number} col The column index
4218      * @param {Number} width The new width
4219      */
4220     setColumnWidth : function(col, width, suppressEvent){
4221         this.config[col].width = width;
4222         this.totalWidth = null;
4223         if(!suppressEvent){
4224              this.fireEvent("widthchange", this, col, width);
4225         }
4226     },
4227
4228     /**
4229      * Returns the total width of all columns.
4230      * @param {Boolean} includeHidden True to include hidden column widths
4231      * @return {Number}
4232      */
4233     getTotalWidth : function(includeHidden){
4234         if(!this.totalWidth){
4235             this.totalWidth = 0;
4236             for(var i = 0, len = this.config.length; i < len; i++){
4237                 if(includeHidden || !this.isHidden(i)){
4238                     this.totalWidth += this.getColumnWidth(i);
4239                 }
4240             }
4241         }
4242         return this.totalWidth;
4243     },
4244
4245     /**
4246      * Returns the header for the specified column.
4247      * @param {Number} col The column index
4248      * @return {String}
4249      */
4250     getColumnHeader : function(col){
4251         return this.config[col].header;
4252     },
4253
4254     /**
4255      * Sets the header for a column.
4256      * @param {Number} col The column index
4257      * @param {String} header The new header
4258      */
4259     setColumnHeader : function(col, header){
4260         this.config[col].header = header;
4261         this.fireEvent("headerchange", this, col, header);
4262     },
4263
4264     /**
4265      * Returns the tooltip for the specified column.
4266      * @param {Number} col The column index
4267      * @return {String}
4268      */
4269     getColumnTooltip : function(col){
4270             return this.config[col].tooltip;
4271     },
4272     /**
4273      * Sets the tooltip for a column.
4274      * @param {Number} col The column index
4275      * @param {String} tooltip The new tooltip
4276      */
4277     setColumnTooltip : function(col, tooltip){
4278             this.config[col].tooltip = tooltip;
4279     },
4280
4281     /**
4282      * Returns the dataIndex for the specified column.
4283      * @param {Number} col The column index
4284      * @return {Number}
4285      */
4286     getDataIndex : function(col){
4287         return this.config[col].dataIndex;
4288     },
4289
4290     /**
4291      * Sets the dataIndex for a column.
4292      * @param {Number} col The column index
4293      * @param {Number} dataIndex The new dataIndex
4294      */
4295     setDataIndex : function(col, dataIndex){
4296         this.config[col].dataIndex = dataIndex;
4297     },
4298
4299     
4300     
4301     /**
4302      * Returns true if the cell is editable.
4303      * @param {Number} colIndex The column index
4304      * @param {Number} rowIndex The row index
4305      * @return {Boolean}
4306      */
4307     isCellEditable : function(colIndex, rowIndex){
4308         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4309     },
4310
4311     /**
4312      * Returns the editor defined for the cell/column.
4313      * return false or null to disable editing.
4314      * @param {Number} colIndex The column index
4315      * @param {Number} rowIndex The row index
4316      * @return {Object}
4317      */
4318     getCellEditor : function(colIndex, rowIndex){
4319         return this.config[colIndex].editor;
4320     },
4321
4322     /**
4323      * Sets if a column is editable.
4324      * @param {Number} col The column index
4325      * @param {Boolean} editable True if the column is editable
4326      */
4327     setEditable : function(col, editable){
4328         this.config[col].editable = editable;
4329     },
4330
4331
4332     /**
4333      * Returns true if the column is hidden.
4334      * @param {Number} colIndex The column index
4335      * @return {Boolean}
4336      */
4337     isHidden : function(colIndex){
4338         return this.config[colIndex].hidden;
4339     },
4340
4341
4342     /**
4343      * Returns true if the column width cannot be changed
4344      */
4345     isFixed : function(colIndex){
4346         return this.config[colIndex].fixed;
4347     },
4348
4349     /**
4350      * Returns true if the column can be resized
4351      * @return {Boolean}
4352      */
4353     isResizable : function(colIndex){
4354         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4355     },
4356     /**
4357      * Sets if a column is hidden.
4358      * @param {Number} colIndex The column index
4359      * @param {Boolean} hidden True if the column is hidden
4360      */
4361     setHidden : function(colIndex, hidden){
4362         this.config[colIndex].hidden = hidden;
4363         this.totalWidth = null;
4364         this.fireEvent("hiddenchange", this, colIndex, hidden);
4365     },
4366
4367     /**
4368      * Sets the editor for a column.
4369      * @param {Number} col The column index
4370      * @param {Object} editor The editor object
4371      */
4372     setEditor : function(col, editor){
4373         this.config[col].editor = editor;
4374     }
4375 });
4376
4377 Roo.grid.ColumnModel.defaultRenderer = function(value){
4378         if(typeof value == "string" && value.length < 1){
4379             return "&#160;";
4380         }
4381         return value;
4382 };
4383
4384 // Alias for backwards compatibility
4385 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4386 /*
4387  * Based on:
4388  * Ext JS Library 1.1.1
4389  * Copyright(c) 2006-2007, Ext JS, LLC.
4390  *
4391  * Originally Released Under LGPL - original licence link has changed is not relivant.
4392  *
4393  * Fork - LGPL
4394  * <script type="text/javascript">
4395  */
4396  
4397 /**
4398  * @class Roo.LoadMask
4399  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4400  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4401  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4402  * element's UpdateManager load indicator and will be destroyed after the initial load.
4403  * @constructor
4404  * Create a new LoadMask
4405  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4406  * @param {Object} config The config object
4407  */
4408 Roo.LoadMask = function(el, config){
4409     this.el = Roo.get(el);
4410     Roo.apply(this, config);
4411     if(this.store){
4412         this.store.on('beforeload', this.onBeforeLoad, this);
4413         this.store.on('load', this.onLoad, this);
4414         this.store.on('loadexception', this.onLoadException, this);
4415         this.removeMask = false;
4416     }else{
4417         var um = this.el.getUpdateManager();
4418         um.showLoadIndicator = false; // disable the default indicator
4419         um.on('beforeupdate', this.onBeforeLoad, this);
4420         um.on('update', this.onLoad, this);
4421         um.on('failure', this.onLoad, this);
4422         this.removeMask = true;
4423     }
4424 };
4425
4426 Roo.LoadMask.prototype = {
4427     /**
4428      * @cfg {Boolean} removeMask
4429      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4430      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4431      */
4432     /**
4433      * @cfg {String} msg
4434      * The text to display in a centered loading message box (defaults to 'Loading...')
4435      */
4436     msg : 'Loading...',
4437     /**
4438      * @cfg {String} msgCls
4439      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4440      */
4441     msgCls : 'x-mask-loading',
4442
4443     /**
4444      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4445      * @type Boolean
4446      */
4447     disabled: false,
4448
4449     /**
4450      * Disables the mask to prevent it from being displayed
4451      */
4452     disable : function(){
4453        this.disabled = true;
4454     },
4455
4456     /**
4457      * Enables the mask so that it can be displayed
4458      */
4459     enable : function(){
4460         this.disabled = false;
4461     },
4462     
4463     onLoadException : function()
4464     {
4465         Roo.log(arguments);
4466         
4467         if (typeof(arguments[3]) != 'undefined') {
4468             Roo.MessageBox.alert("Error loading",arguments[3]);
4469         } 
4470         /*
4471         try {
4472             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4473                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4474             }   
4475         } catch(e) {
4476             
4477         }
4478         */
4479     
4480         
4481         
4482         this.el.unmask(this.removeMask);
4483     },
4484     // private
4485     onLoad : function()
4486     {
4487         this.el.unmask(this.removeMask);
4488     },
4489
4490     // private
4491     onBeforeLoad : function(){
4492         if(!this.disabled){
4493             this.el.mask(this.msg, this.msgCls);
4494         }
4495     },
4496
4497     // private
4498     destroy : function(){
4499         if(this.store){
4500             this.store.un('beforeload', this.onBeforeLoad, this);
4501             this.store.un('load', this.onLoad, this);
4502             this.store.un('loadexception', this.onLoadException, this);
4503         }else{
4504             var um = this.el.getUpdateManager();
4505             um.un('beforeupdate', this.onBeforeLoad, this);
4506             um.un('update', this.onLoad, this);
4507             um.un('failure', this.onLoad, this);
4508         }
4509     }
4510 };/*
4511  * - LGPL
4512  *
4513  * table
4514  * 
4515  */
4516
4517 /**
4518  * @class Roo.bootstrap.Table
4519  * @extends Roo.bootstrap.Component
4520  * Bootstrap Table class
4521  * @cfg {String} cls table class
4522  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4523  * @cfg {String} bgcolor Specifies the background color for a table
4524  * @cfg {Number} border Specifies whether the table cells should have borders or not
4525  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4526  * @cfg {Number} cellspacing Specifies the space between cells
4527  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4528  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4529  * @cfg {String} sortable Specifies that the table should be sortable
4530  * @cfg {String} summary Specifies a summary of the content of a table
4531  * @cfg {Number} width Specifies the width of a table
4532  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4533  * 
4534  * @cfg {boolean} striped Should the rows be alternative striped
4535  * @cfg {boolean} bordered Add borders to the table
4536  * @cfg {boolean} hover Add hover highlighting
4537  * @cfg {boolean} condensed Format condensed
4538  * @cfg {boolean} responsive Format condensed
4539  * @cfg {Boolean} loadMask (true|false) default false
4540  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4541  * @cfg {Boolean} thead (true|false) generate thead, default true
4542  * @cfg {Boolean} RowSelection (true|false) default false
4543  * @cfg {Boolean} CellSelection (true|false) default false
4544  *
4545  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4546  
4547  * 
4548  * @constructor
4549  * Create a new Table
4550  * @param {Object} config The config object
4551  */
4552
4553 Roo.bootstrap.Table = function(config){
4554     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4555     
4556     if (this.sm) {
4557         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4558         this.sm = this.selModel;
4559         this.sm.xmodule = this.xmodule || false;
4560     }
4561     if (this.cm && typeof(this.cm.config) == 'undefined') {
4562         this.colModel = new Roo.grid.ColumnModel(this.cm);
4563         this.cm = this.colModel;
4564         this.cm.xmodule = this.xmodule || false;
4565     }
4566     if (this.store) {
4567         this.store= Roo.factory(this.store, Roo.data);
4568         this.ds = this.store;
4569         this.ds.xmodule = this.xmodule || false;
4570          
4571     }
4572     if (this.footer && this.store) {
4573         this.footer.dataSource = this.ds;
4574         this.footer = Roo.factory(this.footer);
4575     }
4576     
4577     /** @private */
4578     this.addEvents({
4579         /**
4580          * @event cellclick
4581          * Fires when a cell is clicked
4582          * @param {Roo.bootstrap.Table} this
4583          * @param {Roo.Element} el
4584          * @param {Number} rowIndex
4585          * @param {Number} columnIndex
4586          * @param {Roo.EventObject} e
4587          */
4588         "cellclick" : true,
4589         /**
4590          * @event celldblclick
4591          * Fires when a cell is double clicked
4592          * @param {Roo.bootstrap.Table} this
4593          * @param {Roo.Element} el
4594          * @param {Number} rowIndex
4595          * @param {Number} columnIndex
4596          * @param {Roo.EventObject} e
4597          */
4598         "celldblclick" : true,
4599         /**
4600          * @event rowclick
4601          * Fires when a row is clicked
4602          * @param {Roo.bootstrap.Table} this
4603          * @param {Roo.Element} el
4604          * @param {Number} rowIndex
4605          * @param {Roo.EventObject} e
4606          */
4607         "rowclick" : true,
4608         /**
4609          * @event rowdblclick
4610          * Fires when a row is double clicked
4611          * @param {Roo.bootstrap.Table} this
4612          * @param {Roo.Element} el
4613          * @param {Number} rowIndex
4614          * @param {Roo.EventObject} e
4615          */
4616         "rowdblclick" : true,
4617         /**
4618          * @event rowclass
4619          * Fires when a row is rendered, so you can change add a style to it.
4620          * @param {Roo.bootstrap.Table} this
4621          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4622          */
4623         'rowclass' : true
4624         
4625     });
4626 };
4627
4628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4629     
4630     cls: false,
4631     align: false,
4632     bgcolor: false,
4633     border: false,
4634     cellpadding: false,
4635     cellspacing: false,
4636     frame: false,
4637     rules: false,
4638     sortable: false,
4639     summary: false,
4640     width: false,
4641     striped : false,
4642     bordered: false,
4643     hover:  false,
4644     condensed : false,
4645     responsive : false,
4646     sm : false,
4647     cm : false,
4648     store : false,
4649     loadMask : false,
4650     tfoot : true,
4651     thead : true,
4652     RowSelection : false,
4653     CellSelection : false,
4654     layout : false,
4655     
4656     
4657     getAutoCreate : function(){
4658         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4659         
4660         cfg = {
4661             tag: 'table',
4662             cls : 'table',
4663             cn : []
4664         }
4665             
4666         if (this.striped) {
4667             cfg.cls += ' table-striped';
4668         }
4669         
4670         if (this.hover) {
4671             cfg.cls += ' table-hover';
4672         }
4673         if (this.bordered) {
4674             cfg.cls += ' table-bordered';
4675         }
4676         if (this.condensed) {
4677             cfg.cls += ' table-condensed';
4678         }
4679         if (this.responsive) {
4680             cfg.cls += ' table-responsive';
4681         }
4682         
4683         if (this.cls) {
4684             cfg.cls+=  ' ' +this.cls;
4685         }
4686         
4687         // this lot should be simplifed...
4688         
4689         if (this.align) {
4690             cfg.align=this.align;
4691         }
4692         if (this.bgcolor) {
4693             cfg.bgcolor=this.bgcolor;
4694         }
4695         if (this.border) {
4696             cfg.border=this.border;
4697         }
4698         if (this.cellpadding) {
4699             cfg.cellpadding=this.cellpadding;
4700         }
4701         if (this.cellspacing) {
4702             cfg.cellspacing=this.cellspacing;
4703         }
4704         if (this.frame) {
4705             cfg.frame=this.frame;
4706         }
4707         if (this.rules) {
4708             cfg.rules=this.rules;
4709         }
4710         if (this.sortable) {
4711             cfg.sortable=this.sortable;
4712         }
4713         if (this.summary) {
4714             cfg.summary=this.summary;
4715         }
4716         if (this.width) {
4717             cfg.width=this.width;
4718         }
4719         if (this.layout) {
4720             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4721         }
4722         
4723         if(this.store || this.cm){
4724             if(this.thead){
4725                 cfg.cn.push(this.renderHeader());
4726             }
4727             
4728             cfg.cn.push(this.renderBody());
4729             
4730             if(this.tfoot){
4731                 cfg.cn.push(this.renderFooter());
4732             }
4733             
4734             cfg.cls+=  ' TableGrid';
4735         }
4736         
4737         return { cn : [ cfg ] };
4738     },
4739     
4740     initEvents : function()
4741     {   
4742         if(!this.store || !this.cm){
4743             return;
4744         }
4745         
4746         Roo.log('initEvents with ds!!!!');
4747         
4748         var _this = this;
4749         
4750         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4751             e.on('click', _this.sort, _this);
4752         });
4753         
4754         this.el.on("click", this.onClick, this);
4755         this.el.on("dblclick", this.onDblClick, this);
4756         
4757         this.parent().el.setStyle('position', 'relative');
4758         if (this.footer) {
4759             this.footer.parentId = this.id;
4760             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4761         }
4762         
4763         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4764         
4765         this.store.on('load', this.onLoad, this);
4766         this.store.on('beforeload', this.onBeforeLoad, this);
4767         
4768     },
4769     
4770     onClick : function(e, el)
4771     {
4772         var cell = Roo.get(el);
4773         var row = cell.findParent('tr', false, true);
4774         var cellIndex = cell.dom.cellIndex;
4775         var rowIndex = row.dom.rowIndex;
4776         
4777         if(this.CellSelection){
4778             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4779         }
4780         
4781         if(this.RowSelection){
4782             this.fireEvent('rowclick', this, row, rowIndex, e);
4783         }
4784         
4785         
4786     },
4787     
4788     onDblClick : function(e,el)
4789     {
4790         var cell = Roo.get(el);;
4791         var row = cell.findParent('tr', false, true);
4792         var cellIndex = cell.dom.cellIndex;
4793         var rowIndex = row.dom.rowIndex;
4794         
4795         if(this.CellSelection){
4796             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4797         }
4798         
4799         if(this.RowSelection){
4800             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4801         }
4802     },
4803     
4804     sort : function(e,el)
4805     {
4806         var col = Roo.get(el)
4807         
4808         if(!col.hasClass('sortable')){
4809             return;
4810         }
4811         
4812         var sort = col.attr('sort');
4813         var dir = 'ASC';
4814         
4815         if(col.hasClass('glyphicon-arrow-up')){
4816             dir = 'DESC';
4817         }
4818         
4819         this.store.sortInfo = {field : sort, direction : dir};
4820         
4821         if (this.footer) {
4822             Roo.log("calling footer first");
4823             this.footer.onClick('first');
4824         } else {
4825         
4826             this.store.load({ params : { start : 0 } });
4827         }
4828     },
4829     
4830     renderHeader : function()
4831     {
4832         var header = {
4833             tag: 'thead',
4834             cn : []
4835         };
4836         
4837         var cm = this.cm;
4838         
4839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4840             
4841             var config = cm.config[i];
4842             
4843             if(typeof(config.hidden) != 'undefined' && config.hidden){
4844                 continue;
4845             }
4846                     
4847             var c = {
4848                 tag: 'th',
4849                 style : '',
4850                 html: cm.getColumnHeader(i)
4851             };
4852             
4853             if(typeof(config.dataIndex) != 'undefined'){
4854                 c.sort = config.dataIndex;
4855             }
4856             
4857             if(typeof(config.sortable) != 'undefined' && config.sortable){
4858                 c.cls = 'sortable';
4859             }
4860             
4861 //            if(typeof(config.align) != 'undefined' && config.align.length){
4862 //                c.style += ' text-align:' + config.align + ';';
4863 //            }
4864             
4865             if(typeof(config.width) != 'undefined'){
4866                 c.style += ' width:' + config.width + 'px;';
4867             }
4868             
4869             header.cn.push(c)
4870         }
4871         
4872         return header;
4873     },
4874     
4875     renderBody : function()
4876     {
4877         var body = {
4878             tag: 'tbody',
4879             cn : [
4880                 {
4881                     tag: 'tr',
4882                     cn : [
4883                         {
4884                             tag : 'td',
4885                             colspan :  this.cm.getColumnCount()
4886                         }
4887                     ]
4888                 }
4889             ]
4890         };
4891         
4892         return body;
4893     },
4894     
4895     renderFooter : function()
4896     {
4897         var footer = {
4898             tag: 'tfoot',
4899             cn : [
4900                 {
4901                     tag: 'tr',
4902                     cn : [
4903                         {
4904                             tag : 'td',
4905                             colspan :  this.cm.getColumnCount()
4906                         }
4907                     ]
4908                 }
4909             ]
4910         };
4911         
4912         return footer;
4913     },
4914     
4915     onLoad : function()
4916     {
4917         Roo.log('ds onload');
4918         this.clear();
4919         
4920         var _this = this;
4921         var cm = this.cm;
4922         
4923         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4924             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4925             
4926             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4927                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4928             }
4929             
4930             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4931                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4932             }
4933         });
4934         
4935         var tbody = this.el.select('tbody', true).first();
4936         
4937         var renders = [];
4938                     
4939         if(this.store.getCount() > 0){
4940             this.store.data.each(function(d,rowIndex){
4941                 var row = {
4942                     tag : 'tr',
4943                     cn : []
4944                 };
4945                 
4946                 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4947                     var config = cm.config[i];
4948                     
4949                     if(typeof(config.hidden) != 'undefined' && config.hidden){
4950                         continue;
4951                     }
4952                     
4953                     var renderer = cm.getRenderer(i);
4954                     var value = '';
4955                     var id = Roo.id();
4956                     
4957                     if(typeof(renderer) !== 'undefined'){
4958                         value = renderer(d.data[cm.getDataIndex(i)], false, d);
4959                     }
4960                     
4961                     if(typeof(value) === 'object'){
4962                         renders.push({
4963                             id : id,
4964                             cfg : value 
4965                         })
4966                     }
4967                     
4968                     var rowcfg = {
4969                         record: d,
4970                         rowIndex : rowIndex,
4971                         colIndex : i,
4972                         rowClass : ''
4973                     }
4974
4975                     _this.fireEvent('rowclass', this, rowcfg);
4976                     
4977                     var td = {
4978                         tag: 'td',
4979                         id: id,
4980                         cls : rowcfg.rowClass,
4981                         style: '',
4982                         html: (typeof(value) === 'object') ? '' : value
4983                     };
4984                     
4985                     if(typeof(config.align) != 'undefined' && config.align.length){
4986                         td.style += ' text-align:' + config.align + ';';
4987                     }
4988                     
4989                     if(typeof(config.width) != 'undefined'){
4990                         td.style += ' width:' +  config.width + 'px;';
4991                     }
4992                     
4993                     
4994                     row.cn.push(td);
4995                    
4996                 }
4997                 
4998                 tbody.createChild(row);
4999                 
5000             });
5001         }
5002         
5003         
5004         if(renders.length){
5005             var _this = this;
5006             Roo.each(renders, function(r){
5007                 _this.renderColumn(r);
5008             })
5009         }
5010
5011         //if(this.loadMask){
5012         //    this.maskEl.hide();
5013         //}
5014     },
5015     
5016     onBeforeLoad : function()
5017     {
5018         //Roo.log('ds onBeforeLoad');
5019         
5020         //this.clear();
5021         
5022         //if(this.loadMask){
5023         //    this.maskEl.show();
5024         //}
5025     },
5026     
5027     clear : function()
5028     {
5029         this.el.select('tbody', true).first().dom.innerHTML = '';
5030     },
5031     
5032     getSelectionModel : function(){
5033         if(!this.selModel){
5034             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5035         }
5036         return this.selModel;
5037     },
5038     
5039     renderColumn : function(r)
5040     {
5041         var _this = this;
5042         r.cfg.render(Roo.get(r.id));
5043         
5044         if(r.cfg.cn){
5045             Roo.each(r.cfg.cn, function(c){
5046                 var child = {
5047                     id: r.id,
5048                     cfg: c
5049                 }
5050                 _this.renderColumn(child);
5051             })
5052         }
5053     }
5054    
5055 });
5056
5057  
5058
5059  /*
5060  * - LGPL
5061  *
5062  * table cell
5063  * 
5064  */
5065
5066 /**
5067  * @class Roo.bootstrap.TableCell
5068  * @extends Roo.bootstrap.Component
5069  * Bootstrap TableCell class
5070  * @cfg {String} html cell contain text
5071  * @cfg {String} cls cell class
5072  * @cfg {String} tag cell tag (td|th) default td
5073  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5074  * @cfg {String} align Aligns the content in a cell
5075  * @cfg {String} axis Categorizes cells
5076  * @cfg {String} bgcolor Specifies the background color of a cell
5077  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5078  * @cfg {Number} colspan Specifies the number of columns a cell should span
5079  * @cfg {String} headers Specifies one or more header cells a cell is related to
5080  * @cfg {Number} height Sets the height of a cell
5081  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5082  * @cfg {Number} rowspan Sets the number of rows a cell should span
5083  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5084  * @cfg {String} valign Vertical aligns the content in a cell
5085  * @cfg {Number} width Specifies the width of a cell
5086  * 
5087  * @constructor
5088  * Create a new TableCell
5089  * @param {Object} config The config object
5090  */
5091
5092 Roo.bootstrap.TableCell = function(config){
5093     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5094 };
5095
5096 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5097     
5098     html: false,
5099     cls: false,
5100     tag: false,
5101     abbr: false,
5102     align: false,
5103     axis: false,
5104     bgcolor: false,
5105     charoff: false,
5106     colspan: false,
5107     headers: false,
5108     height: false,
5109     nowrap: false,
5110     rowspan: false,
5111     scope: false,
5112     valign: false,
5113     width: false,
5114     
5115     
5116     getAutoCreate : function(){
5117         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5118         
5119         cfg = {
5120             tag: 'td'
5121         }
5122         
5123         if(this.tag){
5124             cfg.tag = this.tag;
5125         }
5126         
5127         if (this.html) {
5128             cfg.html=this.html
5129         }
5130         if (this.cls) {
5131             cfg.cls=this.cls
5132         }
5133         if (this.abbr) {
5134             cfg.abbr=this.abbr
5135         }
5136         if (this.align) {
5137             cfg.align=this.align
5138         }
5139         if (this.axis) {
5140             cfg.axis=this.axis
5141         }
5142         if (this.bgcolor) {
5143             cfg.bgcolor=this.bgcolor
5144         }
5145         if (this.charoff) {
5146             cfg.charoff=this.charoff
5147         }
5148         if (this.colspan) {
5149             cfg.colspan=this.colspan
5150         }
5151         if (this.headers) {
5152             cfg.headers=this.headers
5153         }
5154         if (this.height) {
5155             cfg.height=this.height
5156         }
5157         if (this.nowrap) {
5158             cfg.nowrap=this.nowrap
5159         }
5160         if (this.rowspan) {
5161             cfg.rowspan=this.rowspan
5162         }
5163         if (this.scope) {
5164             cfg.scope=this.scope
5165         }
5166         if (this.valign) {
5167             cfg.valign=this.valign
5168         }
5169         if (this.width) {
5170             cfg.width=this.width
5171         }
5172         
5173         
5174         return cfg;
5175     }
5176    
5177 });
5178
5179  
5180
5181  /*
5182  * - LGPL
5183  *
5184  * table row
5185  * 
5186  */
5187
5188 /**
5189  * @class Roo.bootstrap.TableRow
5190  * @extends Roo.bootstrap.Component
5191  * Bootstrap TableRow class
5192  * @cfg {String} cls row class
5193  * @cfg {String} align Aligns the content in a table row
5194  * @cfg {String} bgcolor Specifies a background color for a table row
5195  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5196  * @cfg {String} valign Vertical aligns the content in a table row
5197  * 
5198  * @constructor
5199  * Create a new TableRow
5200  * @param {Object} config The config object
5201  */
5202
5203 Roo.bootstrap.TableRow = function(config){
5204     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5205 };
5206
5207 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5208     
5209     cls: false,
5210     align: false,
5211     bgcolor: false,
5212     charoff: false,
5213     valign: false,
5214     
5215     getAutoCreate : function(){
5216         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5217         
5218         cfg = {
5219             tag: 'tr'
5220         }
5221             
5222         if(this.cls){
5223             cfg.cls = this.cls;
5224         }
5225         if(this.align){
5226             cfg.align = this.align;
5227         }
5228         if(this.bgcolor){
5229             cfg.bgcolor = this.bgcolor;
5230         }
5231         if(this.charoff){
5232             cfg.charoff = this.charoff;
5233         }
5234         if(this.valign){
5235             cfg.valign = this.valign;
5236         }
5237         
5238         return cfg;
5239     }
5240    
5241 });
5242
5243  
5244
5245  /*
5246  * - LGPL
5247  *
5248  * table body
5249  * 
5250  */
5251
5252 /**
5253  * @class Roo.bootstrap.TableBody
5254  * @extends Roo.bootstrap.Component
5255  * Bootstrap TableBody class
5256  * @cfg {String} cls element class
5257  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5258  * @cfg {String} align Aligns the content inside the element
5259  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5260  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5261  * 
5262  * @constructor
5263  * Create a new TableBody
5264  * @param {Object} config The config object
5265  */
5266
5267 Roo.bootstrap.TableBody = function(config){
5268     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5269 };
5270
5271 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5272     
5273     cls: false,
5274     tag: false,
5275     align: false,
5276     charoff: false,
5277     valign: false,
5278     
5279     getAutoCreate : function(){
5280         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5281         
5282         cfg = {
5283             tag: 'tbody'
5284         }
5285             
5286         if (this.cls) {
5287             cfg.cls=this.cls
5288         }
5289         if(this.tag){
5290             cfg.tag = this.tag;
5291         }
5292         
5293         if(this.align){
5294             cfg.align = this.align;
5295         }
5296         if(this.charoff){
5297             cfg.charoff = this.charoff;
5298         }
5299         if(this.valign){
5300             cfg.valign = this.valign;
5301         }
5302         
5303         return cfg;
5304     }
5305     
5306     
5307 //    initEvents : function()
5308 //    {
5309 //        
5310 //        if(!this.store){
5311 //            return;
5312 //        }
5313 //        
5314 //        this.store = Roo.factory(this.store, Roo.data);
5315 //        this.store.on('load', this.onLoad, this);
5316 //        
5317 //        this.store.load();
5318 //        
5319 //    },
5320 //    
5321 //    onLoad: function () 
5322 //    {   
5323 //        this.fireEvent('load', this);
5324 //    }
5325 //    
5326 //   
5327 });
5328
5329  
5330
5331  /*
5332  * Based on:
5333  * Ext JS Library 1.1.1
5334  * Copyright(c) 2006-2007, Ext JS, LLC.
5335  *
5336  * Originally Released Under LGPL - original licence link has changed is not relivant.
5337  *
5338  * Fork - LGPL
5339  * <script type="text/javascript">
5340  */
5341
5342 // as we use this in bootstrap.
5343 Roo.namespace('Roo.form');
5344  /**
5345  * @class Roo.form.Action
5346  * Internal Class used to handle form actions
5347  * @constructor
5348  * @param {Roo.form.BasicForm} el The form element or its id
5349  * @param {Object} config Configuration options
5350  */
5351
5352  
5353  
5354 // define the action interface
5355 Roo.form.Action = function(form, options){
5356     this.form = form;
5357     this.options = options || {};
5358 };
5359 /**
5360  * Client Validation Failed
5361  * @const 
5362  */
5363 Roo.form.Action.CLIENT_INVALID = 'client';
5364 /**
5365  * Server Validation Failed
5366  * @const 
5367  */
5368 Roo.form.Action.SERVER_INVALID = 'server';
5369  /**
5370  * Connect to Server Failed
5371  * @const 
5372  */
5373 Roo.form.Action.CONNECT_FAILURE = 'connect';
5374 /**
5375  * Reading Data from Server Failed
5376  * @const 
5377  */
5378 Roo.form.Action.LOAD_FAILURE = 'load';
5379
5380 Roo.form.Action.prototype = {
5381     type : 'default',
5382     failureType : undefined,
5383     response : undefined,
5384     result : undefined,
5385
5386     // interface method
5387     run : function(options){
5388
5389     },
5390
5391     // interface method
5392     success : function(response){
5393
5394     },
5395
5396     // interface method
5397     handleResponse : function(response){
5398
5399     },
5400
5401     // default connection failure
5402     failure : function(response){
5403         
5404         this.response = response;
5405         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5406         this.form.afterAction(this, false);
5407     },
5408
5409     processResponse : function(response){
5410         this.response = response;
5411         if(!response.responseText){
5412             return true;
5413         }
5414         this.result = this.handleResponse(response);
5415         return this.result;
5416     },
5417
5418     // utility functions used internally
5419     getUrl : function(appendParams){
5420         var url = this.options.url || this.form.url || this.form.el.dom.action;
5421         if(appendParams){
5422             var p = this.getParams();
5423             if(p){
5424                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5425             }
5426         }
5427         return url;
5428     },
5429
5430     getMethod : function(){
5431         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5432     },
5433
5434     getParams : function(){
5435         var bp = this.form.baseParams;
5436         var p = this.options.params;
5437         if(p){
5438             if(typeof p == "object"){
5439                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5440             }else if(typeof p == 'string' && bp){
5441                 p += '&' + Roo.urlEncode(bp);
5442             }
5443         }else if(bp){
5444             p = Roo.urlEncode(bp);
5445         }
5446         return p;
5447     },
5448
5449     createCallback : function(){
5450         return {
5451             success: this.success,
5452             failure: this.failure,
5453             scope: this,
5454             timeout: (this.form.timeout*1000),
5455             upload: this.form.fileUpload ? this.success : undefined
5456         };
5457     }
5458 };
5459
5460 Roo.form.Action.Submit = function(form, options){
5461     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5462 };
5463
5464 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5465     type : 'submit',
5466
5467     haveProgress : false,
5468     uploadComplete : false,
5469     
5470     // uploadProgress indicator.
5471     uploadProgress : function()
5472     {
5473         if (!this.form.progressUrl) {
5474             return;
5475         }
5476         
5477         if (!this.haveProgress) {
5478             Roo.MessageBox.progress("Uploading", "Uploading");
5479         }
5480         if (this.uploadComplete) {
5481            Roo.MessageBox.hide();
5482            return;
5483         }
5484         
5485         this.haveProgress = true;
5486    
5487         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5488         
5489         var c = new Roo.data.Connection();
5490         c.request({
5491             url : this.form.progressUrl,
5492             params: {
5493                 id : uid
5494             },
5495             method: 'GET',
5496             success : function(req){
5497                //console.log(data);
5498                 var rdata = false;
5499                 var edata;
5500                 try  {
5501                    rdata = Roo.decode(req.responseText)
5502                 } catch (e) {
5503                     Roo.log("Invalid data from server..");
5504                     Roo.log(edata);
5505                     return;
5506                 }
5507                 if (!rdata || !rdata.success) {
5508                     Roo.log(rdata);
5509                     Roo.MessageBox.alert(Roo.encode(rdata));
5510                     return;
5511                 }
5512                 var data = rdata.data;
5513                 
5514                 if (this.uploadComplete) {
5515                    Roo.MessageBox.hide();
5516                    return;
5517                 }
5518                    
5519                 if (data){
5520                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5521                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5522                     );
5523                 }
5524                 this.uploadProgress.defer(2000,this);
5525             },
5526        
5527             failure: function(data) {
5528                 Roo.log('progress url failed ');
5529                 Roo.log(data);
5530             },
5531             scope : this
5532         });
5533            
5534     },
5535     
5536     
5537     run : function()
5538     {
5539         // run get Values on the form, so it syncs any secondary forms.
5540         this.form.getValues();
5541         
5542         var o = this.options;
5543         var method = this.getMethod();
5544         var isPost = method == 'POST';
5545         if(o.clientValidation === false || this.form.isValid()){
5546             
5547             if (this.form.progressUrl) {
5548                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5549                     (new Date() * 1) + '' + Math.random());
5550                     
5551             } 
5552             
5553             
5554             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5555                 form:this.form.el.dom,
5556                 url:this.getUrl(!isPost),
5557                 method: method,
5558                 params:isPost ? this.getParams() : null,
5559                 isUpload: this.form.fileUpload
5560             }));
5561             
5562             this.uploadProgress();
5563
5564         }else if (o.clientValidation !== false){ // client validation failed
5565             this.failureType = Roo.form.Action.CLIENT_INVALID;
5566             this.form.afterAction(this, false);
5567         }
5568     },
5569
5570     success : function(response)
5571     {
5572         this.uploadComplete= true;
5573         if (this.haveProgress) {
5574             Roo.MessageBox.hide();
5575         }
5576         
5577         
5578         var result = this.processResponse(response);
5579         if(result === true || result.success){
5580             this.form.afterAction(this, true);
5581             return;
5582         }
5583         if(result.errors){
5584             this.form.markInvalid(result.errors);
5585             this.failureType = Roo.form.Action.SERVER_INVALID;
5586         }
5587         this.form.afterAction(this, false);
5588     },
5589     failure : function(response)
5590     {
5591         this.uploadComplete= true;
5592         if (this.haveProgress) {
5593             Roo.MessageBox.hide();
5594         }
5595         
5596         this.response = response;
5597         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5598         this.form.afterAction(this, false);
5599     },
5600     
5601     handleResponse : function(response){
5602         if(this.form.errorReader){
5603             var rs = this.form.errorReader.read(response);
5604             var errors = [];
5605             if(rs.records){
5606                 for(var i = 0, len = rs.records.length; i < len; i++) {
5607                     var r = rs.records[i];
5608                     errors[i] = r.data;
5609                 }
5610             }
5611             if(errors.length < 1){
5612                 errors = null;
5613             }
5614             return {
5615                 success : rs.success,
5616                 errors : errors
5617             };
5618         }
5619         var ret = false;
5620         try {
5621             ret = Roo.decode(response.responseText);
5622         } catch (e) {
5623             ret = {
5624                 success: false,
5625                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5626                 errors : []
5627             };
5628         }
5629         return ret;
5630         
5631     }
5632 });
5633
5634
5635 Roo.form.Action.Load = function(form, options){
5636     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5637     this.reader = this.form.reader;
5638 };
5639
5640 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5641     type : 'load',
5642
5643     run : function(){
5644         
5645         Roo.Ajax.request(Roo.apply(
5646                 this.createCallback(), {
5647                     method:this.getMethod(),
5648                     url:this.getUrl(false),
5649                     params:this.getParams()
5650         }));
5651     },
5652
5653     success : function(response){
5654         
5655         var result = this.processResponse(response);
5656         if(result === true || !result.success || !result.data){
5657             this.failureType = Roo.form.Action.LOAD_FAILURE;
5658             this.form.afterAction(this, false);
5659             return;
5660         }
5661         this.form.clearInvalid();
5662         this.form.setValues(result.data);
5663         this.form.afterAction(this, true);
5664     },
5665
5666     handleResponse : function(response){
5667         if(this.form.reader){
5668             var rs = this.form.reader.read(response);
5669             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5670             return {
5671                 success : rs.success,
5672                 data : data
5673             };
5674         }
5675         return Roo.decode(response.responseText);
5676     }
5677 });
5678
5679 Roo.form.Action.ACTION_TYPES = {
5680     'load' : Roo.form.Action.Load,
5681     'submit' : Roo.form.Action.Submit
5682 };/*
5683  * - LGPL
5684  *
5685  * form
5686  * 
5687  */
5688
5689 /**
5690  * @class Roo.bootstrap.Form
5691  * @extends Roo.bootstrap.Component
5692  * Bootstrap Form class
5693  * @cfg {String} method  GET | POST (default POST)
5694  * @cfg {String} labelAlign top | left (default top)
5695   * @cfg {String} align left  | right - for navbars
5696
5697  * 
5698  * @constructor
5699  * Create a new Form
5700  * @param {Object} config The config object
5701  */
5702
5703
5704 Roo.bootstrap.Form = function(config){
5705     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5706     this.addEvents({
5707         /**
5708          * @event clientvalidation
5709          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5710          * @param {Form} this
5711          * @param {Boolean} valid true if the form has passed client-side validation
5712          */
5713         clientvalidation: true,
5714         /**
5715          * @event beforeaction
5716          * Fires before any action is performed. Return false to cancel the action.
5717          * @param {Form} this
5718          * @param {Action} action The action to be performed
5719          */
5720         beforeaction: true,
5721         /**
5722          * @event actionfailed
5723          * Fires when an action fails.
5724          * @param {Form} this
5725          * @param {Action} action The action that failed
5726          */
5727         actionfailed : true,
5728         /**
5729          * @event actioncomplete
5730          * Fires when an action is completed.
5731          * @param {Form} this
5732          * @param {Action} action The action that completed
5733          */
5734         actioncomplete : true
5735     });
5736     
5737 };
5738
5739 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5740       
5741      /**
5742      * @cfg {String} method
5743      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5744      */
5745     method : 'POST',
5746     /**
5747      * @cfg {String} url
5748      * The URL to use for form actions if one isn't supplied in the action options.
5749      */
5750     /**
5751      * @cfg {Boolean} fileUpload
5752      * Set to true if this form is a file upload.
5753      */
5754      
5755     /**
5756      * @cfg {Object} baseParams
5757      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5758      */
5759       
5760     /**
5761      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5762      */
5763     timeout: 30,
5764     /**
5765      * @cfg {Sting} align (left|right) for navbar forms
5766      */
5767     align : 'left',
5768
5769     // private
5770     activeAction : null,
5771  
5772     /**
5773      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5774      * element by passing it or its id or mask the form itself by passing in true.
5775      * @type Mixed
5776      */
5777     waitMsgTarget : false,
5778     
5779      
5780     
5781     /**
5782      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5783      * element by passing it or its id or mask the form itself by passing in true.
5784      * @type Mixed
5785      */
5786     
5787     getAutoCreate : function(){
5788         
5789         var cfg = {
5790             tag: 'form',
5791             method : this.method || 'POST',
5792             id : this.id || Roo.id(),
5793             cls : ''
5794         }
5795         if (this.parent().xtype.match(/^Nav/)) {
5796             cfg.cls = 'navbar-form navbar-' + this.align;
5797             
5798         }
5799         
5800         if (this.labelAlign == 'left' ) {
5801             cfg.cls += ' form-horizontal';
5802         }
5803         
5804         
5805         return cfg;
5806     },
5807     initEvents : function()
5808     {
5809         this.el.on('submit', this.onSubmit, this);
5810         this.el.on('keypress', function(e) {
5811             if (e.getCharCode() != 13) {
5812                 return true;
5813             }
5814             e.preventDefault();
5815             return false;
5816         });
5817         
5818     },
5819     // private
5820     onSubmit : function(e){
5821         e.stopEvent();
5822     },
5823     
5824      /**
5825      * Returns true if client-side validation on the form is successful.
5826      * @return Boolean
5827      */
5828     isValid : function(){
5829         var items = this.getItems();
5830         var valid = true;
5831         items.each(function(f){
5832            if(!f.validate()){
5833                valid = false;
5834                
5835            }
5836         });
5837         return valid;
5838     },
5839     /**
5840      * Returns true if any fields in this form have changed since their original load.
5841      * @return Boolean
5842      */
5843     isDirty : function(){
5844         var dirty = false;
5845         var items = this.getItems();
5846         items.each(function(f){
5847            if(f.isDirty()){
5848                dirty = true;
5849                return false;
5850            }
5851            return true;
5852         });
5853         return dirty;
5854     },
5855      /**
5856      * Performs a predefined action (submit or load) or custom actions you define on this form.
5857      * @param {String} actionName The name of the action type
5858      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
5859      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5860      * accept other config options):
5861      * <pre>
5862 Property          Type             Description
5863 ----------------  ---------------  ----------------------------------------------------------------------------------
5864 url               String           The url for the action (defaults to the form's url)
5865 method            String           The form method to use (defaults to the form's method, or POST if not defined)
5866 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
5867 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
5868                                    validate the form on the client (defaults to false)
5869      * </pre>
5870      * @return {BasicForm} this
5871      */
5872     doAction : function(action, options){
5873         if(typeof action == 'string'){
5874             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5875         }
5876         if(this.fireEvent('beforeaction', this, action) !== false){
5877             this.beforeAction(action);
5878             action.run.defer(100, action);
5879         }
5880         return this;
5881     },
5882     
5883     // private
5884     beforeAction : function(action){
5885         var o = action.options;
5886         
5887         // not really supported yet.. ??
5888         
5889         //if(this.waitMsgTarget === true){
5890             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5891         //}else if(this.waitMsgTarget){
5892         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5893         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5894         //}else {
5895         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5896        // }
5897          
5898     },
5899
5900     // private
5901     afterAction : function(action, success){
5902         this.activeAction = null;
5903         var o = action.options;
5904         
5905         //if(this.waitMsgTarget === true){
5906             this.el.unmask();
5907         //}else if(this.waitMsgTarget){
5908         //    this.waitMsgTarget.unmask();
5909         //}else{
5910         //    Roo.MessageBox.updateProgress(1);
5911         //    Roo.MessageBox.hide();
5912        // }
5913         // 
5914         if(success){
5915             if(o.reset){
5916                 this.reset();
5917             }
5918             Roo.callback(o.success, o.scope, [this, action]);
5919             this.fireEvent('actioncomplete', this, action);
5920             
5921         }else{
5922             
5923             // failure condition..
5924             // we have a scenario where updates need confirming.
5925             // eg. if a locking scenario exists..
5926             // we look for { errors : { needs_confirm : true }} in the response.
5927             if (
5928                 (typeof(action.result) != 'undefined')  &&
5929                 (typeof(action.result.errors) != 'undefined')  &&
5930                 (typeof(action.result.errors.needs_confirm) != 'undefined')
5931            ){
5932                 var _t = this;
5933                 Roo.log("not supported yet");
5934                  /*
5935                 
5936                 Roo.MessageBox.confirm(
5937                     "Change requires confirmation",
5938                     action.result.errorMsg,
5939                     function(r) {
5940                         if (r != 'yes') {
5941                             return;
5942                         }
5943                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
5944                     }
5945                     
5946                 );
5947                 */
5948                 
5949                 
5950                 return;
5951             }
5952             
5953             Roo.callback(o.failure, o.scope, [this, action]);
5954             // show an error message if no failed handler is set..
5955             if (!this.hasListener('actionfailed')) {
5956                 Roo.log("need to add dialog support");
5957                 /*
5958                 Roo.MessageBox.alert("Error",
5959                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5960                         action.result.errorMsg :
5961                         "Saving Failed, please check your entries or try again"
5962                 );
5963                 */
5964             }
5965             
5966             this.fireEvent('actionfailed', this, action);
5967         }
5968         
5969     },
5970     /**
5971      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5972      * @param {String} id The value to search for
5973      * @return Field
5974      */
5975     findField : function(id){
5976         var items = this.getItems();
5977         var field = items.get(id);
5978         if(!field){
5979              items.each(function(f){
5980                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5981                     field = f;
5982                     return false;
5983                 }
5984                 return true;
5985             });
5986         }
5987         return field || null;
5988     },
5989      /**
5990      * Mark fields in this form invalid in bulk.
5991      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5992      * @return {BasicForm} this
5993      */
5994     markInvalid : function(errors){
5995         if(errors instanceof Array){
5996             for(var i = 0, len = errors.length; i < len; i++){
5997                 var fieldError = errors[i];
5998                 var f = this.findField(fieldError.id);
5999                 if(f){
6000                     f.markInvalid(fieldError.msg);
6001                 }
6002             }
6003         }else{
6004             var field, id;
6005             for(id in errors){
6006                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6007                     field.markInvalid(errors[id]);
6008                 }
6009             }
6010         }
6011         //Roo.each(this.childForms || [], function (f) {
6012         //    f.markInvalid(errors);
6013         //});
6014         
6015         return this;
6016     },
6017
6018     /**
6019      * Set values for fields in this form in bulk.
6020      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6021      * @return {BasicForm} this
6022      */
6023     setValues : function(values){
6024         if(values instanceof Array){ // array of objects
6025             for(var i = 0, len = values.length; i < len; i++){
6026                 var v = values[i];
6027                 var f = this.findField(v.id);
6028                 if(f){
6029                     f.setValue(v.value);
6030                     if(this.trackResetOnLoad){
6031                         f.originalValue = f.getValue();
6032                     }
6033                 }
6034             }
6035         }else{ // object hash
6036             var field, id;
6037             for(id in values){
6038                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6039                     
6040                     if (field.setFromData && 
6041                         field.valueField && 
6042                         field.displayField &&
6043                         // combos' with local stores can 
6044                         // be queried via setValue()
6045                         // to set their value..
6046                         (field.store && !field.store.isLocal)
6047                         ) {
6048                         // it's a combo
6049                         var sd = { };
6050                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6051                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6052                         field.setFromData(sd);
6053                         
6054                     } else {
6055                         field.setValue(values[id]);
6056                     }
6057                     
6058                     
6059                     if(this.trackResetOnLoad){
6060                         field.originalValue = field.getValue();
6061                     }
6062                 }
6063             }
6064         }
6065          
6066         //Roo.each(this.childForms || [], function (f) {
6067         //    f.setValues(values);
6068         //});
6069                 
6070         return this;
6071     },
6072
6073     /**
6074      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6075      * they are returned as an array.
6076      * @param {Boolean} asString
6077      * @return {Object}
6078      */
6079     getValues : function(asString){
6080         //if (this.childForms) {
6081             // copy values from the child forms
6082         //    Roo.each(this.childForms, function (f) {
6083         //        this.setValues(f.getValues());
6084         //    }, this);
6085         //}
6086         
6087         
6088         
6089         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6090         if(asString === true){
6091             return fs;
6092         }
6093         return Roo.urlDecode(fs);
6094     },
6095     
6096     /**
6097      * Returns the fields in this form as an object with key/value pairs. 
6098      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6099      * @return {Object}
6100      */
6101     getFieldValues : function(with_hidden)
6102     {
6103         var items = this.getItems();
6104         var ret = {};
6105         items.each(function(f){
6106             if (!f.getName()) {
6107                 return;
6108             }
6109             var v = f.getValue();
6110             if (f.inputType =='radio') {
6111                 if (typeof(ret[f.getName()]) == 'undefined') {
6112                     ret[f.getName()] = ''; // empty..
6113                 }
6114                 
6115                 if (!f.el.dom.checked) {
6116                     return;
6117                     
6118                 }
6119                 v = f.el.dom.value;
6120                 
6121             }
6122             
6123             // not sure if this supported any more..
6124             if ((typeof(v) == 'object') && f.getRawValue) {
6125                 v = f.getRawValue() ; // dates..
6126             }
6127             // combo boxes where name != hiddenName...
6128             if (f.name != f.getName()) {
6129                 ret[f.name] = f.getRawValue();
6130             }
6131             ret[f.getName()] = v;
6132         });
6133         
6134         return ret;
6135     },
6136
6137     /**
6138      * Clears all invalid messages in this form.
6139      * @return {BasicForm} this
6140      */
6141     clearInvalid : function(){
6142         var items = this.getItems();
6143         
6144         items.each(function(f){
6145            f.clearInvalid();
6146         });
6147         
6148         
6149         
6150         return this;
6151     },
6152
6153     /**
6154      * Resets this form.
6155      * @return {BasicForm} this
6156      */
6157     reset : function(){
6158         var items = this.getItems();
6159         items.each(function(f){
6160             f.reset();
6161         });
6162         
6163         Roo.each(this.childForms || [], function (f) {
6164             f.reset();
6165         });
6166        
6167         
6168         return this;
6169     },
6170     getItems : function()
6171     {
6172         var r=new Roo.util.MixedCollection(false, function(o){
6173             return o.id || (o.id = Roo.id());
6174         });
6175         var iter = function(el) {
6176             if (el.inputEl) {
6177                 r.add(el);
6178             }
6179             if (!el.items) {
6180                 return;
6181             }
6182             Roo.each(el.items,function(e) {
6183                 iter(e);
6184             });
6185             
6186             
6187         };
6188         iter(this);
6189         return r;
6190         
6191         
6192         
6193         
6194     }
6195     
6196 });
6197
6198  
6199 /*
6200  * Based on:
6201  * Ext JS Library 1.1.1
6202  * Copyright(c) 2006-2007, Ext JS, LLC.
6203  *
6204  * Originally Released Under LGPL - original licence link has changed is not relivant.
6205  *
6206  * Fork - LGPL
6207  * <script type="text/javascript">
6208  */
6209 /**
6210  * @class Roo.form.VTypes
6211  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6212  * @singleton
6213  */
6214 Roo.form.VTypes = function(){
6215     // closure these in so they are only created once.
6216     var alpha = /^[a-zA-Z_]+$/;
6217     var alphanum = /^[a-zA-Z0-9_]+$/;
6218     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6219     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6220
6221     // All these messages and functions are configurable
6222     return {
6223         /**
6224          * The function used to validate email addresses
6225          * @param {String} value The email address
6226          */
6227         'email' : function(v){
6228             return email.test(v);
6229         },
6230         /**
6231          * The error text to display when the email validation function returns false
6232          * @type String
6233          */
6234         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6235         /**
6236          * The keystroke filter mask to be applied on email input
6237          * @type RegExp
6238          */
6239         'emailMask' : /[a-z0-9_\.\-@]/i,
6240
6241         /**
6242          * The function used to validate URLs
6243          * @param {String} value The URL
6244          */
6245         'url' : function(v){
6246             return url.test(v);
6247         },
6248         /**
6249          * The error text to display when the url validation function returns false
6250          * @type String
6251          */
6252         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6253         
6254         /**
6255          * The function used to validate alpha values
6256          * @param {String} value The value
6257          */
6258         'alpha' : function(v){
6259             return alpha.test(v);
6260         },
6261         /**
6262          * The error text to display when the alpha validation function returns false
6263          * @type String
6264          */
6265         'alphaText' : 'This field should only contain letters and _',
6266         /**
6267          * The keystroke filter mask to be applied on alpha input
6268          * @type RegExp
6269          */
6270         'alphaMask' : /[a-z_]/i,
6271
6272         /**
6273          * The function used to validate alphanumeric values
6274          * @param {String} value The value
6275          */
6276         'alphanum' : function(v){
6277             return alphanum.test(v);
6278         },
6279         /**
6280          * The error text to display when the alphanumeric validation function returns false
6281          * @type String
6282          */
6283         'alphanumText' : 'This field should only contain letters, numbers and _',
6284         /**
6285          * The keystroke filter mask to be applied on alphanumeric input
6286          * @type RegExp
6287          */
6288         'alphanumMask' : /[a-z0-9_]/i
6289     };
6290 }();/*
6291  * - LGPL
6292  *
6293  * Input
6294  * 
6295  */
6296
6297 /**
6298  * @class Roo.bootstrap.Input
6299  * @extends Roo.bootstrap.Component
6300  * Bootstrap Input class
6301  * @cfg {Boolean} disabled is it disabled
6302  * @cfg {String} fieldLabel - the label associated
6303  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6304  * @cfg {String} name name of the input
6305  * @cfg {string} fieldLabel - the label associated
6306  * @cfg {string}  inputType - input / file submit ...
6307  * @cfg {string} placeholder - placeholder to put in text.
6308  * @cfg {string}  before - input group add on before
6309  * @cfg {string} after - input group add on after
6310  * @cfg {string} size - (lg|sm) or leave empty..
6311  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6312  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6313  * @cfg {Number} md colspan out of 12 for computer-sized screens
6314  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6315  * @cfg {string} value default value of the input
6316  * @cfg {Number} labelWidth set the width of label (0-12)
6317  * @cfg {String} labelAlign (top|left)
6318  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6319  * @cfg {String} align (left|center|right) Default left
6320  * 
6321  * 
6322  * @constructor
6323  * Create a new Input
6324  * @param {Object} config The config object
6325  */
6326
6327 Roo.bootstrap.Input = function(config){
6328     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6329    
6330         this.addEvents({
6331             /**
6332              * @event focus
6333              * Fires when this field receives input focus.
6334              * @param {Roo.form.Field} this
6335              */
6336             focus : true,
6337             /**
6338              * @event blur
6339              * Fires when this field loses input focus.
6340              * @param {Roo.form.Field} this
6341              */
6342             blur : true,
6343             /**
6344              * @event specialkey
6345              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6346              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6347              * @param {Roo.form.Field} this
6348              * @param {Roo.EventObject} e The event object
6349              */
6350             specialkey : true,
6351             /**
6352              * @event change
6353              * Fires just before the field blurs if the field value has changed.
6354              * @param {Roo.form.Field} this
6355              * @param {Mixed} newValue The new value
6356              * @param {Mixed} oldValue The original value
6357              */
6358             change : true,
6359             /**
6360              * @event invalid
6361              * Fires after the field has been marked as invalid.
6362              * @param {Roo.form.Field} this
6363              * @param {String} msg The validation message
6364              */
6365             invalid : true,
6366             /**
6367              * @event valid
6368              * Fires after the field has been validated with no errors.
6369              * @param {Roo.form.Field} this
6370              */
6371             valid : true,
6372              /**
6373              * @event keyup
6374              * Fires after the key up
6375              * @param {Roo.form.Field} this
6376              * @param {Roo.EventObject}  e The event Object
6377              */
6378             keyup : true
6379         });
6380 };
6381
6382 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6383      /**
6384      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6385       automatic validation (defaults to "keyup").
6386      */
6387     validationEvent : "keyup",
6388      /**
6389      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6390      */
6391     validateOnBlur : true,
6392     /**
6393      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6394      */
6395     validationDelay : 250,
6396      /**
6397      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6398      */
6399     focusClass : "x-form-focus",  // not needed???
6400     
6401        
6402     /**
6403      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6404      */
6405     invalidClass : "has-error",
6406     
6407     /**
6408      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6409      */
6410     selectOnFocus : false,
6411     
6412      /**
6413      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6414      */
6415     maskRe : null,
6416        /**
6417      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6418      */
6419     vtype : null,
6420     
6421       /**
6422      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6423      */
6424     disableKeyFilter : false,
6425     
6426        /**
6427      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6428      */
6429     disabled : false,
6430      /**
6431      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6432      */
6433     allowBlank : true,
6434     /**
6435      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6436      */
6437     blankText : "This field is required",
6438     
6439      /**
6440      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6441      */
6442     minLength : 0,
6443     /**
6444      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6445      */
6446     maxLength : Number.MAX_VALUE,
6447     /**
6448      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6449      */
6450     minLengthText : "The minimum length for this field is {0}",
6451     /**
6452      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6453      */
6454     maxLengthText : "The maximum length for this field is {0}",
6455   
6456     
6457     /**
6458      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6459      * If available, this function will be called only after the basic validators all return true, and will be passed the
6460      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6461      */
6462     validator : null,
6463     /**
6464      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6465      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6466      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6467      */
6468     regex : null,
6469     /**
6470      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6471      */
6472     regexText : "",
6473     
6474     
6475     
6476     fieldLabel : '',
6477     inputType : 'text',
6478     
6479     name : false,
6480     placeholder: false,
6481     before : false,
6482     after : false,
6483     size : false,
6484     // private
6485     hasFocus : false,
6486     preventMark: false,
6487     isFormField : true,
6488     value : '',
6489     labelWidth : 2,
6490     labelAlign : false,
6491     readOnly : false,
6492     align : false,
6493     
6494     parentLabelAlign : function()
6495     {
6496         var parent = this;
6497         while (parent.parent()) {
6498             parent = parent.parent();
6499             if (typeof(parent.labelAlign) !='undefined') {
6500                 return parent.labelAlign;
6501             }
6502         }
6503         return 'left';
6504         
6505     },
6506     
6507     getAutoCreate : function(){
6508         
6509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6510         
6511         var id = Roo.id();
6512         
6513         var cfg = {};
6514         
6515         if(this.inputType != 'hidden'){
6516             cfg.cls = 'form-group' //input-group
6517         }
6518         
6519         var input =  {
6520             tag: 'input',
6521             id : id,
6522             type : this.inputType,
6523             value : this.value,
6524             cls : 'form-control',
6525             placeholder : this.placeholder || ''
6526             
6527         };
6528         
6529         if(this.align){
6530             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6531         }
6532         
6533         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6534             input.maxLength = this.maxLength;
6535         }
6536         
6537         if (this.disabled) {
6538             input.disabled=true;
6539         }
6540         
6541         if (this.readOnly) {
6542             input.readonly=true;
6543         }
6544         
6545         if (this.name) {
6546             input.name = this.name;
6547         }
6548         if (this.size) {
6549             input.cls += ' input-' + this.size;
6550         }
6551         var settings=this;
6552         ['xs','sm','md','lg'].map(function(size){
6553             if (settings[size]) {
6554                 cfg.cls += ' col-' + size + '-' + settings[size];
6555             }
6556         });
6557         
6558         var inputblock = input;
6559         
6560         if (this.before || this.after) {
6561             
6562             inputblock = {
6563                 cls : 'input-group',
6564                 cn :  [] 
6565             };
6566             if (this.before && typeof(this.before) == 'string') {
6567                 
6568                 inputblock.cn.push({
6569                     tag :'span',
6570                     cls : 'roo-input-before input-group-addon',
6571                     html : this.before
6572                 });
6573             }
6574             if (this.before && typeof(this.before) == 'object') {
6575                 this.before = Roo.factory(this.before);
6576                 Roo.log(this.before);
6577                 inputblock.cn.push({
6578                     tag :'span',
6579                     cls : 'roo-input-before input-group-' +
6580                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6581                 });
6582             }
6583             
6584             inputblock.cn.push(input);
6585             
6586             if (this.after && typeof(this.after) == 'string') {
6587                 inputblock.cn.push({
6588                     tag :'span',
6589                     cls : 'roo-input-after input-group-addon',
6590                     html : this.after
6591                 });
6592             }
6593             if (this.after && typeof(this.after) == 'object') {
6594                 this.after = Roo.factory(this.after);
6595                 Roo.log(this.after);
6596                 inputblock.cn.push({
6597                     tag :'span',
6598                     cls : 'roo-input-after input-group-' +
6599                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6600                 });
6601             }
6602         };
6603         
6604         if (align ==='left' && this.fieldLabel.length) {
6605                 Roo.log("left and has label");
6606                 cfg.cn = [
6607                     
6608                     {
6609                         tag: 'label',
6610                         'for' :  id,
6611                         cls : 'control-label col-sm-' + this.labelWidth,
6612                         html : this.fieldLabel
6613                         
6614                     },
6615                     {
6616                         cls : "col-sm-" + (12 - this.labelWidth), 
6617                         cn: [
6618                             inputblock
6619                         ]
6620                     }
6621                     
6622                 ];
6623         } else if ( this.fieldLabel.length) {
6624                 Roo.log(" label");
6625                  cfg.cn = [
6626                    
6627                     {
6628                         tag: 'label',
6629                         //cls : 'input-group-addon',
6630                         html : this.fieldLabel
6631                         
6632                     },
6633                     
6634                     inputblock
6635                     
6636                 ];
6637
6638         } else {
6639             
6640                 Roo.log(" no label && no align");
6641                 cfg.cn = [
6642                     
6643                         inputblock
6644                     
6645                 ];
6646                 
6647                 
6648         };
6649         Roo.log('input-parentType: ' + this.parentType);
6650         
6651         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6652            cfg.cls += ' navbar-form';
6653            Roo.log(cfg);
6654         }
6655         
6656         return cfg;
6657         
6658     },
6659     /**
6660      * return the real input element.
6661      */
6662     inputEl: function ()
6663     {
6664         return this.el.select('input.form-control',true).first();
6665     },
6666     setDisabled : function(v)
6667     {
6668         var i  = this.inputEl().dom;
6669         if (!v) {
6670             i.removeAttribute('disabled');
6671             return;
6672             
6673         }
6674         i.setAttribute('disabled','true');
6675     },
6676     initEvents : function()
6677     {
6678         
6679         this.inputEl().on("keydown" , this.fireKey,  this);
6680         this.inputEl().on("focus", this.onFocus,  this);
6681         this.inputEl().on("blur", this.onBlur,  this);
6682         
6683         this.inputEl().relayEvent('keyup', this);
6684
6685         // reference to original value for reset
6686         this.originalValue = this.getValue();
6687         //Roo.form.TextField.superclass.initEvents.call(this);
6688         if(this.validationEvent == 'keyup'){
6689             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6690             this.inputEl().on('keyup', this.filterValidation, this);
6691         }
6692         else if(this.validationEvent !== false){
6693             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6694         }
6695         
6696         if(this.selectOnFocus){
6697             this.on("focus", this.preFocus, this);
6698             
6699         }
6700         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6701             this.inputEl().on("keypress", this.filterKeys, this);
6702         }
6703        /* if(this.grow){
6704             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6705             this.el.on("click", this.autoSize,  this);
6706         }
6707         */
6708         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6709             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6710         }
6711         
6712         if (typeof(this.before) == 'object') {
6713             this.before.render(this.el.select('.roo-input-before',true).first());
6714         }
6715         if (typeof(this.after) == 'object') {
6716             this.after.render(this.el.select('.roo-input-after',true).first());
6717         }
6718         
6719         
6720     },
6721     filterValidation : function(e){
6722         if(!e.isNavKeyPress()){
6723             this.validationTask.delay(this.validationDelay);
6724         }
6725     },
6726      /**
6727      * Validates the field value
6728      * @return {Boolean} True if the value is valid, else false
6729      */
6730     validate : function(){
6731         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6732         if(this.disabled || this.validateValue(this.getRawValue())){
6733             this.clearInvalid();
6734             return true;
6735         }
6736         return false;
6737     },
6738     
6739     
6740     /**
6741      * Validates a value according to the field's validation rules and marks the field as invalid
6742      * if the validation fails
6743      * @param {Mixed} value The value to validate
6744      * @return {Boolean} True if the value is valid, else false
6745      */
6746     validateValue : function(value){
6747         if(value.length < 1)  { // if it's blank
6748              if(this.allowBlank){
6749                 this.clearInvalid();
6750                 return true;
6751              }else{
6752                 this.markInvalid(this.blankText);
6753                 return false;
6754              }
6755         }
6756         if(value.length < this.minLength){
6757             this.markInvalid(String.format(this.minLengthText, this.minLength));
6758             return false;
6759         }
6760         if(value.length > this.maxLength){
6761             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6762             return false;
6763         }
6764         if(this.vtype){
6765             var vt = Roo.form.VTypes;
6766             if(!vt[this.vtype](value, this)){
6767                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6768                 return false;
6769             }
6770         }
6771         if(typeof this.validator == "function"){
6772             var msg = this.validator(value);
6773             if(msg !== true){
6774                 this.markInvalid(msg);
6775                 return false;
6776             }
6777         }
6778         if(this.regex && !this.regex.test(value)){
6779             this.markInvalid(this.regexText);
6780             return false;
6781         }
6782         return true;
6783     },
6784
6785     
6786     
6787      // private
6788     fireKey : function(e){
6789         //Roo.log('field ' + e.getKey());
6790         if(e.isNavKeyPress()){
6791             this.fireEvent("specialkey", this, e);
6792         }
6793     },
6794     focus : function (selectText){
6795         if(this.rendered){
6796             this.inputEl().focus();
6797             if(selectText === true){
6798                 this.inputEl().dom.select();
6799             }
6800         }
6801         return this;
6802     } ,
6803     
6804     onFocus : function(){
6805         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6806            // this.el.addClass(this.focusClass);
6807         }
6808         if(!this.hasFocus){
6809             this.hasFocus = true;
6810             this.startValue = this.getValue();
6811             this.fireEvent("focus", this);
6812         }
6813     },
6814     
6815     beforeBlur : Roo.emptyFn,
6816
6817     
6818     // private
6819     onBlur : function(){
6820         this.beforeBlur();
6821         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6822             //this.el.removeClass(this.focusClass);
6823         }
6824         this.hasFocus = false;
6825         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6826             this.validate();
6827         }
6828         var v = this.getValue();
6829         if(String(v) !== String(this.startValue)){
6830             this.fireEvent('change', this, v, this.startValue);
6831         }
6832         this.fireEvent("blur", this);
6833     },
6834     
6835     /**
6836      * Resets the current field value to the originally loaded value and clears any validation messages
6837      */
6838     reset : function(){
6839         this.setValue(this.originalValue);
6840         this.clearInvalid();
6841     },
6842      /**
6843      * Returns the name of the field
6844      * @return {Mixed} name The name field
6845      */
6846     getName: function(){
6847         return this.name;
6848     },
6849      /**
6850      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
6851      * @return {Mixed} value The field value
6852      */
6853     getValue : function(){
6854         return this.inputEl().getValue();
6855     },
6856     /**
6857      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
6858      * @return {Mixed} value The field value
6859      */
6860     getRawValue : function(){
6861         var v = this.inputEl().getValue();
6862         
6863         return v;
6864     },
6865     
6866     /**
6867      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
6868      * @param {Mixed} value The value to set
6869      */
6870     setRawValue : function(v){
6871         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6872     },
6873     
6874     selectText : function(start, end){
6875         var v = this.getRawValue();
6876         if(v.length > 0){
6877             start = start === undefined ? 0 : start;
6878             end = end === undefined ? v.length : end;
6879             var d = this.inputEl().dom;
6880             if(d.setSelectionRange){
6881                 d.setSelectionRange(start, end);
6882             }else if(d.createTextRange){
6883                 var range = d.createTextRange();
6884                 range.moveStart("character", start);
6885                 range.moveEnd("character", v.length-end);
6886                 range.select();
6887             }
6888         }
6889     },
6890     
6891     /**
6892      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
6893      * @param {Mixed} value The value to set
6894      */
6895     setValue : function(v){
6896         this.value = v;
6897         if(this.rendered){
6898             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6899             this.validate();
6900         }
6901     },
6902     
6903     /*
6904     processValue : function(value){
6905         if(this.stripCharsRe){
6906             var newValue = value.replace(this.stripCharsRe, '');
6907             if(newValue !== value){
6908                 this.setRawValue(newValue);
6909                 return newValue;
6910             }
6911         }
6912         return value;
6913     },
6914   */
6915     preFocus : function(){
6916         
6917         if(this.selectOnFocus){
6918             this.inputEl().dom.select();
6919         }
6920     },
6921     filterKeys : function(e){
6922         var k = e.getKey();
6923         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6924             return;
6925         }
6926         var c = e.getCharCode(), cc = String.fromCharCode(c);
6927         if(Roo.isIE && (e.isSpecialKey() || !cc)){
6928             return;
6929         }
6930         if(!this.maskRe.test(cc)){
6931             e.stopEvent();
6932         }
6933     },
6934      /**
6935      * Clear any invalid styles/messages for this field
6936      */
6937     clearInvalid : function(){
6938         
6939         if(!this.el || this.preventMark){ // not rendered
6940             return;
6941         }
6942         this.el.removeClass(this.invalidClass);
6943         /*
6944         switch(this.msgTarget){
6945             case 'qtip':
6946                 this.el.dom.qtip = '';
6947                 break;
6948             case 'title':
6949                 this.el.dom.title = '';
6950                 break;
6951             case 'under':
6952                 if(this.errorEl){
6953                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6954                 }
6955                 break;
6956             case 'side':
6957                 if(this.errorIcon){
6958                     this.errorIcon.dom.qtip = '';
6959                     this.errorIcon.hide();
6960                     this.un('resize', this.alignErrorIcon, this);
6961                 }
6962                 break;
6963             default:
6964                 var t = Roo.getDom(this.msgTarget);
6965                 t.innerHTML = '';
6966                 t.style.display = 'none';
6967                 break;
6968         }
6969         */
6970         this.fireEvent('valid', this);
6971     },
6972      /**
6973      * Mark this field as invalid
6974      * @param {String} msg The validation message
6975      */
6976     markInvalid : function(msg){
6977         if(!this.el  || this.preventMark){ // not rendered
6978             return;
6979         }
6980         this.el.addClass(this.invalidClass);
6981         /*
6982         msg = msg || this.invalidText;
6983         switch(this.msgTarget){
6984             case 'qtip':
6985                 this.el.dom.qtip = msg;
6986                 this.el.dom.qclass = 'x-form-invalid-tip';
6987                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6988                     Roo.QuickTips.enable();
6989                 }
6990                 break;
6991             case 'title':
6992                 this.el.dom.title = msg;
6993                 break;
6994             case 'under':
6995                 if(!this.errorEl){
6996                     var elp = this.el.findParent('.x-form-element', 5, true);
6997                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6998                     this.errorEl.setWidth(elp.getWidth(true)-20);
6999                 }
7000                 this.errorEl.update(msg);
7001                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7002                 break;
7003             case 'side':
7004                 if(!this.errorIcon){
7005                     var elp = this.el.findParent('.x-form-element', 5, true);
7006                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7007                 }
7008                 this.alignErrorIcon();
7009                 this.errorIcon.dom.qtip = msg;
7010                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7011                 this.errorIcon.show();
7012                 this.on('resize', this.alignErrorIcon, this);
7013                 break;
7014             default:
7015                 var t = Roo.getDom(this.msgTarget);
7016                 t.innerHTML = msg;
7017                 t.style.display = this.msgDisplay;
7018                 break;
7019         }
7020         */
7021         this.fireEvent('invalid', this, msg);
7022     },
7023     // private
7024     SafariOnKeyDown : function(event)
7025     {
7026         // this is a workaround for a password hang bug on chrome/ webkit.
7027         
7028         var isSelectAll = false;
7029         
7030         if(this.inputEl().dom.selectionEnd > 0){
7031             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7032         }
7033         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7034             event.preventDefault();
7035             this.setValue('');
7036             return;
7037         }
7038         
7039         if(isSelectAll){ // backspace and delete key
7040             
7041             event.preventDefault();
7042             // this is very hacky as keydown always get's upper case.
7043             //
7044             var cc = String.fromCharCode(event.getCharCode());
7045             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7046             
7047         }
7048     },
7049     adjustWidth : function(tag, w){
7050         tag = tag.toLowerCase();
7051         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7052             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7053                 if(tag == 'input'){
7054                     return w + 2;
7055                 }
7056                 if(tag == 'textarea'){
7057                     return w-2;
7058                 }
7059             }else if(Roo.isOpera){
7060                 if(tag == 'input'){
7061                     return w + 2;
7062                 }
7063                 if(tag == 'textarea'){
7064                     return w-2;
7065                 }
7066             }
7067         }
7068         return w;
7069     }
7070     
7071 });
7072
7073  
7074 /*
7075  * - LGPL
7076  *
7077  * Input
7078  * 
7079  */
7080
7081 /**
7082  * @class Roo.bootstrap.TextArea
7083  * @extends Roo.bootstrap.Input
7084  * Bootstrap TextArea class
7085  * @cfg {Number} cols Specifies the visible width of a text area
7086  * @cfg {Number} rows Specifies the visible number of lines in a text area
7087  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7088  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7089  * @cfg {string} html text
7090  * 
7091  * @constructor
7092  * Create a new TextArea
7093  * @param {Object} config The config object
7094  */
7095
7096 Roo.bootstrap.TextArea = function(config){
7097     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7098    
7099 };
7100
7101 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7102      
7103     cols : false,
7104     rows : 5,
7105     readOnly : false,
7106     warp : 'soft',
7107     resize : false,
7108     value: false,
7109     html: false,
7110     
7111     getAutoCreate : function(){
7112         
7113         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7114         
7115         var id = Roo.id();
7116         
7117         var cfg = {};
7118         
7119         var input =  {
7120             tag: 'textarea',
7121             id : id,
7122             warp : this.warp,
7123             rows : this.rows,
7124             value : this.value || '',
7125             html: this.html || '',
7126             cls : 'form-control',
7127             placeholder : this.placeholder || '' 
7128             
7129         };
7130         
7131         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7132             input.maxLength = this.maxLength;
7133         }
7134         
7135         if(this.resize){
7136             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7137         }
7138         
7139         if(this.cols){
7140             input.cols = this.cols;
7141         }
7142         
7143         if (this.readOnly) {
7144             input.readonly = true;
7145         }
7146         
7147         if (this.name) {
7148             input.name = this.name;
7149         }
7150         
7151         if (this.size) {
7152             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7153         }
7154         
7155         var settings=this;
7156         ['xs','sm','md','lg'].map(function(size){
7157             if (settings[size]) {
7158                 cfg.cls += ' col-' + size + '-' + settings[size];
7159             }
7160         });
7161         
7162         var inputblock = input;
7163         
7164         if (this.before || this.after) {
7165             
7166             inputblock = {
7167                 cls : 'input-group',
7168                 cn :  [] 
7169             };
7170             if (this.before) {
7171                 inputblock.cn.push({
7172                     tag :'span',
7173                     cls : 'input-group-addon',
7174                     html : this.before
7175                 });
7176             }
7177             inputblock.cn.push(input);
7178             if (this.after) {
7179                 inputblock.cn.push({
7180                     tag :'span',
7181                     cls : 'input-group-addon',
7182                     html : this.after
7183                 });
7184             }
7185             
7186         }
7187         
7188         if (align ==='left' && this.fieldLabel.length) {
7189                 Roo.log("left and has label");
7190                 cfg.cn = [
7191                     
7192                     {
7193                         tag: 'label',
7194                         'for' :  id,
7195                         cls : 'control-label col-sm-' + this.labelWidth,
7196                         html : this.fieldLabel
7197                         
7198                     },
7199                     {
7200                         cls : "col-sm-" + (12 - this.labelWidth), 
7201                         cn: [
7202                             inputblock
7203                         ]
7204                     }
7205                     
7206                 ];
7207         } else if ( this.fieldLabel.length) {
7208                 Roo.log(" label");
7209                  cfg.cn = [
7210                    
7211                     {
7212                         tag: 'label',
7213                         //cls : 'input-group-addon',
7214                         html : this.fieldLabel
7215                         
7216                     },
7217                     
7218                     inputblock
7219                     
7220                 ];
7221
7222         } else {
7223             
7224                    Roo.log(" no label && no align");
7225                 cfg.cn = [
7226                     
7227                         inputblock
7228                     
7229                 ];
7230                 
7231                 
7232         }
7233         
7234         if (this.disabled) {
7235             input.disabled=true;
7236         }
7237         
7238         return cfg;
7239         
7240     },
7241     /**
7242      * return the real textarea element.
7243      */
7244     inputEl: function ()
7245     {
7246         return this.el.select('textarea.form-control',true).first();
7247     }
7248 });
7249
7250  
7251 /*
7252  * - LGPL
7253  *
7254  * trigger field - base class for combo..
7255  * 
7256  */
7257  
7258 /**
7259  * @class Roo.bootstrap.TriggerField
7260  * @extends Roo.bootstrap.Input
7261  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7262  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7263  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7264  * for which you can provide a custom implementation.  For example:
7265  * <pre><code>
7266 var trigger = new Roo.bootstrap.TriggerField();
7267 trigger.onTriggerClick = myTriggerFn;
7268 trigger.applyTo('my-field');
7269 </code></pre>
7270  *
7271  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7272  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7273  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7274  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7275  * @constructor
7276  * Create a new TriggerField.
7277  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7278  * to the base TextField)
7279  */
7280 Roo.bootstrap.TriggerField = function(config){
7281     this.mimicing = false;
7282     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7283 };
7284
7285 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7286     /**
7287      * @cfg {String} triggerClass A CSS class to apply to the trigger
7288      */
7289      /**
7290      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7291      */
7292     hideTrigger:false,
7293
7294     /** @cfg {Boolean} grow @hide */
7295     /** @cfg {Number} growMin @hide */
7296     /** @cfg {Number} growMax @hide */
7297
7298     /**
7299      * @hide 
7300      * @method
7301      */
7302     autoSize: Roo.emptyFn,
7303     // private
7304     monitorTab : true,
7305     // private
7306     deferHeight : true,
7307
7308     
7309     actionMode : 'wrap',
7310     
7311     
7312     
7313     getAutoCreate : function(){
7314        
7315         var parent = this.parent();
7316         
7317         var align = this.labelAlign || this.parentLabelAlign();
7318         
7319         var id = Roo.id();
7320         
7321         var cfg = {
7322             cls: 'form-group' //input-group
7323         };
7324         
7325         
7326         var input =  {
7327             tag: 'input',
7328             id : id,
7329             type : this.inputType,
7330             cls : 'form-control',
7331             autocomplete: 'off',
7332             placeholder : this.placeholder || '' 
7333             
7334         };
7335         if (this.name) {
7336             input.name = this.name;
7337         }
7338         if (this.size) {
7339             input.cls += ' input-' + this.size;
7340         }
7341         
7342         if (this.disabled) {
7343             input.disabled=true;
7344         }
7345         
7346         var inputblock = input;
7347         
7348         if (this.before || this.after) {
7349             
7350             inputblock = {
7351                 cls : 'input-group',
7352                 cn :  [] 
7353             };
7354             if (this.before) {
7355                 inputblock.cn.push({
7356                     tag :'span',
7357                     cls : 'input-group-addon',
7358                     html : this.before
7359                 });
7360             }
7361             inputblock.cn.push(input);
7362             if (this.after) {
7363                 inputblock.cn.push({
7364                     tag :'span',
7365                     cls : 'input-group-addon',
7366                     html : this.after
7367                 });
7368             }
7369             
7370         };
7371         
7372         var box = {
7373             tag: 'div',
7374             cn: [
7375                 {
7376                     tag: 'input',
7377                     type : 'hidden',
7378                     cls: 'form-hidden-field'
7379                 },
7380                 inputblock
7381             ]
7382             
7383         };
7384         
7385         if(this.multiple){
7386             Roo.log('multiple');
7387             
7388             box = {
7389                 tag: 'div',
7390                 cn: [
7391                     {
7392                         tag: 'input',
7393                         type : 'hidden',
7394                         cls: 'form-hidden-field'
7395                     },
7396                     {
7397                         tag: 'ul',
7398                         cls: 'select2-choices',
7399                         cn:[
7400                             {
7401                                 tag: 'li',
7402                                 cls: 'select2-search-field',
7403                                 cn: [
7404
7405                                     inputblock
7406                                 ]
7407                             }
7408                         ]
7409                     }
7410                 ]
7411             }
7412         };
7413         
7414         var combobox = {
7415             cls: 'select2-container input-group',
7416             cn: [
7417                 box,
7418                 {
7419                     tag: 'ul',
7420                     cls: 'typeahead typeahead-long dropdown-menu',
7421                     style: 'display:none'
7422                 }
7423             ]
7424         };
7425         
7426         if(!this.multiple){
7427             combobox.cn.push({
7428                 tag :'span',
7429                 cls : 'input-group-addon btn dropdown-toggle',
7430                 cn : [
7431                     {
7432                         tag: 'span',
7433                         cls: 'caret'
7434                     },
7435                     {
7436                         tag: 'span',
7437                         cls: 'combobox-clear',
7438                         cn  : [
7439                             {
7440                                 tag : 'i',
7441                                 cls: 'icon-remove'
7442                             }
7443                         ]
7444                     }
7445                 ]
7446
7447             })
7448         }
7449         
7450         if(this.multiple){
7451             combobox.cls += ' select2-container-multi';
7452         }
7453         
7454         if (align ==='left' && this.fieldLabel.length) {
7455             
7456                 Roo.log("left and has label");
7457                 cfg.cn = [
7458                     
7459                     {
7460                         tag: 'label',
7461                         'for' :  id,
7462                         cls : 'control-label col-sm-' + this.labelWidth,
7463                         html : this.fieldLabel
7464                         
7465                     },
7466                     {
7467                         cls : "col-sm-" + (12 - this.labelWidth), 
7468                         cn: [
7469                             combobox
7470                         ]
7471                     }
7472                     
7473                 ];
7474         } else if ( this.fieldLabel.length) {
7475                 Roo.log(" label");
7476                  cfg.cn = [
7477                    
7478                     {
7479                         tag: 'label',
7480                         //cls : 'input-group-addon',
7481                         html : this.fieldLabel
7482                         
7483                     },
7484                     
7485                     combobox
7486                     
7487                 ];
7488
7489         } else {
7490             
7491                 Roo.log(" no label && no align");
7492                 cfg = combobox
7493                      
7494                 
7495         }
7496          
7497         var settings=this;
7498         ['xs','sm','md','lg'].map(function(size){
7499             if (settings[size]) {
7500                 cfg.cls += ' col-' + size + '-' + settings[size];
7501             }
7502         });
7503         
7504         return cfg;
7505         
7506     },
7507     
7508     
7509     
7510     // private
7511     onResize : function(w, h){
7512 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7513 //        if(typeof w == 'number'){
7514 //            var x = w - this.trigger.getWidth();
7515 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7516 //            this.trigger.setStyle('left', x+'px');
7517 //        }
7518     },
7519
7520     // private
7521     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7522
7523     // private
7524     getResizeEl : function(){
7525         return this.inputEl();
7526     },
7527
7528     // private
7529     getPositionEl : function(){
7530         return this.inputEl();
7531     },
7532
7533     // private
7534     alignErrorIcon : function(){
7535         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7536     },
7537
7538     // private
7539     initEvents : function(){
7540         
7541         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7542         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7543         if(!this.multiple){
7544             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7545             if(this.hideTrigger){
7546                 this.trigger.setDisplayed(false);
7547             }
7548             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7549         }
7550         
7551         if(this.multiple){
7552             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7553         }
7554         
7555         //this.trigger.addClassOnOver('x-form-trigger-over');
7556         //this.trigger.addClassOnClick('x-form-trigger-click');
7557         
7558         //if(!this.width){
7559         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7560         //}
7561     },
7562
7563     // private
7564     initTrigger : function(){
7565        
7566     },
7567
7568     // private
7569     onDestroy : function(){
7570         if(this.trigger){
7571             this.trigger.removeAllListeners();
7572           //  this.trigger.remove();
7573         }
7574         //if(this.wrap){
7575         //    this.wrap.remove();
7576         //}
7577         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7578     },
7579
7580     // private
7581     onFocus : function(){
7582         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7583         /*
7584         if(!this.mimicing){
7585             this.wrap.addClass('x-trigger-wrap-focus');
7586             this.mimicing = true;
7587             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7588             if(this.monitorTab){
7589                 this.el.on("keydown", this.checkTab, this);
7590             }
7591         }
7592         */
7593     },
7594
7595     // private
7596     checkTab : function(e){
7597         if(e.getKey() == e.TAB){
7598             this.triggerBlur();
7599         }
7600     },
7601
7602     // private
7603     onBlur : function(){
7604         // do nothing
7605     },
7606
7607     // private
7608     mimicBlur : function(e, t){
7609         /*
7610         if(!this.wrap.contains(t) && this.validateBlur()){
7611             this.triggerBlur();
7612         }
7613         */
7614     },
7615
7616     // private
7617     triggerBlur : function(){
7618         this.mimicing = false;
7619         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7620         if(this.monitorTab){
7621             this.el.un("keydown", this.checkTab, this);
7622         }
7623         //this.wrap.removeClass('x-trigger-wrap-focus');
7624         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7625     },
7626
7627     // private
7628     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7629     validateBlur : function(e, t){
7630         return true;
7631     },
7632
7633     // private
7634     onDisable : function(){
7635         this.inputEl().dom.disabled = true;
7636         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7637         //if(this.wrap){
7638         //    this.wrap.addClass('x-item-disabled');
7639         //}
7640     },
7641
7642     // private
7643     onEnable : function(){
7644         this.inputEl().dom.disabled = false;
7645         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7646         //if(this.wrap){
7647         //    this.el.removeClass('x-item-disabled');
7648         //}
7649     },
7650
7651     // private
7652     onShow : function(){
7653         var ae = this.getActionEl();
7654         
7655         if(ae){
7656             ae.dom.style.display = '';
7657             ae.dom.style.visibility = 'visible';
7658         }
7659     },
7660
7661     // private
7662     
7663     onHide : function(){
7664         var ae = this.getActionEl();
7665         ae.dom.style.display = 'none';
7666     },
7667
7668     /**
7669      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7670      * by an implementing function.
7671      * @method
7672      * @param {EventObject} e
7673      */
7674     onTriggerClick : Roo.emptyFn
7675 });
7676  /*
7677  * Based on:
7678  * Ext JS Library 1.1.1
7679  * Copyright(c) 2006-2007, Ext JS, LLC.
7680  *
7681  * Originally Released Under LGPL - original licence link has changed is not relivant.
7682  *
7683  * Fork - LGPL
7684  * <script type="text/javascript">
7685  */
7686
7687
7688 /**
7689  * @class Roo.data.SortTypes
7690  * @singleton
7691  * Defines the default sorting (casting?) comparison functions used when sorting data.
7692  */
7693 Roo.data.SortTypes = {
7694     /**
7695      * Default sort that does nothing
7696      * @param {Mixed} s The value being converted
7697      * @return {Mixed} The comparison value
7698      */
7699     none : function(s){
7700         return s;
7701     },
7702     
7703     /**
7704      * The regular expression used to strip tags
7705      * @type {RegExp}
7706      * @property
7707      */
7708     stripTagsRE : /<\/?[^>]+>/gi,
7709     
7710     /**
7711      * Strips all HTML tags to sort on text only
7712      * @param {Mixed} s The value being converted
7713      * @return {String} The comparison value
7714      */
7715     asText : function(s){
7716         return String(s).replace(this.stripTagsRE, "");
7717     },
7718     
7719     /**
7720      * Strips all HTML tags to sort on text only - Case insensitive
7721      * @param {Mixed} s The value being converted
7722      * @return {String} The comparison value
7723      */
7724     asUCText : function(s){
7725         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7726     },
7727     
7728     /**
7729      * Case insensitive string
7730      * @param {Mixed} s The value being converted
7731      * @return {String} The comparison value
7732      */
7733     asUCString : function(s) {
7734         return String(s).toUpperCase();
7735     },
7736     
7737     /**
7738      * Date sorting
7739      * @param {Mixed} s The value being converted
7740      * @return {Number} The comparison value
7741      */
7742     asDate : function(s) {
7743         if(!s){
7744             return 0;
7745         }
7746         if(s instanceof Date){
7747             return s.getTime();
7748         }
7749         return Date.parse(String(s));
7750     },
7751     
7752     /**
7753      * Float sorting
7754      * @param {Mixed} s The value being converted
7755      * @return {Float} The comparison value
7756      */
7757     asFloat : function(s) {
7758         var val = parseFloat(String(s).replace(/,/g, ""));
7759         if(isNaN(val)) val = 0;
7760         return val;
7761     },
7762     
7763     /**
7764      * Integer sorting
7765      * @param {Mixed} s The value being converted
7766      * @return {Number} The comparison value
7767      */
7768     asInt : function(s) {
7769         var val = parseInt(String(s).replace(/,/g, ""));
7770         if(isNaN(val)) val = 0;
7771         return val;
7772     }
7773 };/*
7774  * Based on:
7775  * Ext JS Library 1.1.1
7776  * Copyright(c) 2006-2007, Ext JS, LLC.
7777  *
7778  * Originally Released Under LGPL - original licence link has changed is not relivant.
7779  *
7780  * Fork - LGPL
7781  * <script type="text/javascript">
7782  */
7783
7784 /**
7785 * @class Roo.data.Record
7786  * Instances of this class encapsulate both record <em>definition</em> information, and record
7787  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7788  * to access Records cached in an {@link Roo.data.Store} object.<br>
7789  * <p>
7790  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7791  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7792  * objects.<br>
7793  * <p>
7794  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7795  * @constructor
7796  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7797  * {@link #create}. The parameters are the same.
7798  * @param {Array} data An associative Array of data values keyed by the field name.
7799  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7800  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7801  * not specified an integer id is generated.
7802  */
7803 Roo.data.Record = function(data, id){
7804     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7805     this.data = data;
7806 };
7807
7808 /**
7809  * Generate a constructor for a specific record layout.
7810  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7811  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7812  * Each field definition object may contain the following properties: <ul>
7813  * <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,
7814  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7815  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7816  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7817  * is being used, then this is a string containing the javascript expression to reference the data relative to 
7818  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7819  * to the data item relative to the record element. If the mapping expression is the same as the field name,
7820  * this may be omitted.</p></li>
7821  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7822  * <ul><li>auto (Default, implies no conversion)</li>
7823  * <li>string</li>
7824  * <li>int</li>
7825  * <li>float</li>
7826  * <li>boolean</li>
7827  * <li>date</li></ul></p></li>
7828  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7829  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7830  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7831  * by the Reader into an object that will be stored in the Record. It is passed the
7832  * following parameters:<ul>
7833  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7834  * </ul></p></li>
7835  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7836  * </ul>
7837  * <br>usage:<br><pre><code>
7838 var TopicRecord = Roo.data.Record.create(
7839     {name: 'title', mapping: 'topic_title'},
7840     {name: 'author', mapping: 'username'},
7841     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7842     {name: 'lastPost', mapping: 'post_time', type: 'date'},
7843     {name: 'lastPoster', mapping: 'user2'},
7844     {name: 'excerpt', mapping: 'post_text'}
7845 );
7846
7847 var myNewRecord = new TopicRecord({
7848     title: 'Do my job please',
7849     author: 'noobie',
7850     totalPosts: 1,
7851     lastPost: new Date(),
7852     lastPoster: 'Animal',
7853     excerpt: 'No way dude!'
7854 });
7855 myStore.add(myNewRecord);
7856 </code></pre>
7857  * @method create
7858  * @static
7859  */
7860 Roo.data.Record.create = function(o){
7861     var f = function(){
7862         f.superclass.constructor.apply(this, arguments);
7863     };
7864     Roo.extend(f, Roo.data.Record);
7865     var p = f.prototype;
7866     p.fields = new Roo.util.MixedCollection(false, function(field){
7867         return field.name;
7868     });
7869     for(var i = 0, len = o.length; i < len; i++){
7870         p.fields.add(new Roo.data.Field(o[i]));
7871     }
7872     f.getField = function(name){
7873         return p.fields.get(name);  
7874     };
7875     return f;
7876 };
7877
7878 Roo.data.Record.AUTO_ID = 1000;
7879 Roo.data.Record.EDIT = 'edit';
7880 Roo.data.Record.REJECT = 'reject';
7881 Roo.data.Record.COMMIT = 'commit';
7882
7883 Roo.data.Record.prototype = {
7884     /**
7885      * Readonly flag - true if this record has been modified.
7886      * @type Boolean
7887      */
7888     dirty : false,
7889     editing : false,
7890     error: null,
7891     modified: null,
7892
7893     // private
7894     join : function(store){
7895         this.store = store;
7896     },
7897
7898     /**
7899      * Set the named field to the specified value.
7900      * @param {String} name The name of the field to set.
7901      * @param {Object} value The value to set the field to.
7902      */
7903     set : function(name, value){
7904         if(this.data[name] == value){
7905             return;
7906         }
7907         this.dirty = true;
7908         if(!this.modified){
7909             this.modified = {};
7910         }
7911         if(typeof this.modified[name] == 'undefined'){
7912             this.modified[name] = this.data[name];
7913         }
7914         this.data[name] = value;
7915         if(!this.editing && this.store){
7916             this.store.afterEdit(this);
7917         }       
7918     },
7919
7920     /**
7921      * Get the value of the named field.
7922      * @param {String} name The name of the field to get the value of.
7923      * @return {Object} The value of the field.
7924      */
7925     get : function(name){
7926         return this.data[name]; 
7927     },
7928
7929     // private
7930     beginEdit : function(){
7931         this.editing = true;
7932         this.modified = {}; 
7933     },
7934
7935     // private
7936     cancelEdit : function(){
7937         this.editing = false;
7938         delete this.modified;
7939     },
7940
7941     // private
7942     endEdit : function(){
7943         this.editing = false;
7944         if(this.dirty && this.store){
7945             this.store.afterEdit(this);
7946         }
7947     },
7948
7949     /**
7950      * Usually called by the {@link Roo.data.Store} which owns the Record.
7951      * Rejects all changes made to the Record since either creation, or the last commit operation.
7952      * Modified fields are reverted to their original values.
7953      * <p>
7954      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7955      * of reject operations.
7956      */
7957     reject : function(){
7958         var m = this.modified;
7959         for(var n in m){
7960             if(typeof m[n] != "function"){
7961                 this.data[n] = m[n];
7962             }
7963         }
7964         this.dirty = false;
7965         delete this.modified;
7966         this.editing = false;
7967         if(this.store){
7968             this.store.afterReject(this);
7969         }
7970     },
7971
7972     /**
7973      * Usually called by the {@link Roo.data.Store} which owns the Record.
7974      * Commits all changes made to the Record since either creation, or the last commit operation.
7975      * <p>
7976      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7977      * of commit operations.
7978      */
7979     commit : function(){
7980         this.dirty = false;
7981         delete this.modified;
7982         this.editing = false;
7983         if(this.store){
7984             this.store.afterCommit(this);
7985         }
7986     },
7987
7988     // private
7989     hasError : function(){
7990         return this.error != null;
7991     },
7992
7993     // private
7994     clearError : function(){
7995         this.error = null;
7996     },
7997
7998     /**
7999      * Creates a copy of this record.
8000      * @param {String} id (optional) A new record id if you don't want to use this record's id
8001      * @return {Record}
8002      */
8003     copy : function(newId) {
8004         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8005     }
8006 };/*
8007  * Based on:
8008  * Ext JS Library 1.1.1
8009  * Copyright(c) 2006-2007, Ext JS, LLC.
8010  *
8011  * Originally Released Under LGPL - original licence link has changed is not relivant.
8012  *
8013  * Fork - LGPL
8014  * <script type="text/javascript">
8015  */
8016
8017
8018
8019 /**
8020  * @class Roo.data.Store
8021  * @extends Roo.util.Observable
8022  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8023  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8024  * <p>
8025  * 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
8026  * has no knowledge of the format of the data returned by the Proxy.<br>
8027  * <p>
8028  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8029  * instances from the data object. These records are cached and made available through accessor functions.
8030  * @constructor
8031  * Creates a new Store.
8032  * @param {Object} config A config object containing the objects needed for the Store to access data,
8033  * and read the data into Records.
8034  */
8035 Roo.data.Store = function(config){
8036     this.data = new Roo.util.MixedCollection(false);
8037     this.data.getKey = function(o){
8038         return o.id;
8039     };
8040     this.baseParams = {};
8041     // private
8042     this.paramNames = {
8043         "start" : "start",
8044         "limit" : "limit",
8045         "sort" : "sort",
8046         "dir" : "dir",
8047         "multisort" : "_multisort"
8048     };
8049
8050     if(config && config.data){
8051         this.inlineData = config.data;
8052         delete config.data;
8053     }
8054
8055     Roo.apply(this, config);
8056     
8057     if(this.reader){ // reader passed
8058         this.reader = Roo.factory(this.reader, Roo.data);
8059         this.reader.xmodule = this.xmodule || false;
8060         if(!this.recordType){
8061             this.recordType = this.reader.recordType;
8062         }
8063         if(this.reader.onMetaChange){
8064             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8065         }
8066     }
8067
8068     if(this.recordType){
8069         this.fields = this.recordType.prototype.fields;
8070     }
8071     this.modified = [];
8072
8073     this.addEvents({
8074         /**
8075          * @event datachanged
8076          * Fires when the data cache has changed, and a widget which is using this Store
8077          * as a Record cache should refresh its view.
8078          * @param {Store} this
8079          */
8080         datachanged : true,
8081         /**
8082          * @event metachange
8083          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8084          * @param {Store} this
8085          * @param {Object} meta The JSON metadata
8086          */
8087         metachange : true,
8088         /**
8089          * @event add
8090          * Fires when Records have been added to the Store
8091          * @param {Store} this
8092          * @param {Roo.data.Record[]} records The array of Records added
8093          * @param {Number} index The index at which the record(s) were added
8094          */
8095         add : true,
8096         /**
8097          * @event remove
8098          * Fires when a Record has been removed from the Store
8099          * @param {Store} this
8100          * @param {Roo.data.Record} record The Record that was removed
8101          * @param {Number} index The index at which the record was removed
8102          */
8103         remove : true,
8104         /**
8105          * @event update
8106          * Fires when a Record has been updated
8107          * @param {Store} this
8108          * @param {Roo.data.Record} record The Record that was updated
8109          * @param {String} operation The update operation being performed.  Value may be one of:
8110          * <pre><code>
8111  Roo.data.Record.EDIT
8112  Roo.data.Record.REJECT
8113  Roo.data.Record.COMMIT
8114          * </code></pre>
8115          */
8116         update : true,
8117         /**
8118          * @event clear
8119          * Fires when the data cache has been cleared.
8120          * @param {Store} this
8121          */
8122         clear : true,
8123         /**
8124          * @event beforeload
8125          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8126          * the load action will be canceled.
8127          * @param {Store} this
8128          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8129          */
8130         beforeload : true,
8131         /**
8132          * @event beforeloadadd
8133          * Fires after a new set of Records has been loaded.
8134          * @param {Store} this
8135          * @param {Roo.data.Record[]} records The Records that were loaded
8136          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8137          */
8138         beforeloadadd : true,
8139         /**
8140          * @event load
8141          * Fires after a new set of Records has been loaded, before they are added to the store.
8142          * @param {Store} this
8143          * @param {Roo.data.Record[]} records The Records that were loaded
8144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8145          * @params {Object} return from reader
8146          */
8147         load : true,
8148         /**
8149          * @event loadexception
8150          * Fires if an exception occurs in the Proxy during loading.
8151          * Called with the signature of the Proxy's "loadexception" event.
8152          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8153          * 
8154          * @param {Proxy} 
8155          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8156          * @param {Object} load options 
8157          * @param {Object} jsonData from your request (normally this contains the Exception)
8158          */
8159         loadexception : true
8160     });
8161     
8162     if(this.proxy){
8163         this.proxy = Roo.factory(this.proxy, Roo.data);
8164         this.proxy.xmodule = this.xmodule || false;
8165         this.relayEvents(this.proxy,  ["loadexception"]);
8166     }
8167     this.sortToggle = {};
8168     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8169
8170     Roo.data.Store.superclass.constructor.call(this);
8171
8172     if(this.inlineData){
8173         this.loadData(this.inlineData);
8174         delete this.inlineData;
8175     }
8176 };
8177
8178 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8179      /**
8180     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8181     * without a remote query - used by combo/forms at present.
8182     */
8183     
8184     /**
8185     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8186     */
8187     /**
8188     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8189     */
8190     /**
8191     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8192     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8193     */
8194     /**
8195     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8196     * on any HTTP request
8197     */
8198     /**
8199     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8200     */
8201     /**
8202     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8203     */
8204     multiSort: false,
8205     /**
8206     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8207     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8208     */
8209     remoteSort : false,
8210
8211     /**
8212     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8213      * loaded or when a record is removed. (defaults to false).
8214     */
8215     pruneModifiedRecords : false,
8216
8217     // private
8218     lastOptions : null,
8219
8220     /**
8221      * Add Records to the Store and fires the add event.
8222      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8223      */
8224     add : function(records){
8225         records = [].concat(records);
8226         for(var i = 0, len = records.length; i < len; i++){
8227             records[i].join(this);
8228         }
8229         var index = this.data.length;
8230         this.data.addAll(records);
8231         this.fireEvent("add", this, records, index);
8232     },
8233
8234     /**
8235      * Remove a Record from the Store and fires the remove event.
8236      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8237      */
8238     remove : function(record){
8239         var index = this.data.indexOf(record);
8240         this.data.removeAt(index);
8241         if(this.pruneModifiedRecords){
8242             this.modified.remove(record);
8243         }
8244         this.fireEvent("remove", this, record, index);
8245     },
8246
8247     /**
8248      * Remove all Records from the Store and fires the clear event.
8249      */
8250     removeAll : function(){
8251         this.data.clear();
8252         if(this.pruneModifiedRecords){
8253             this.modified = [];
8254         }
8255         this.fireEvent("clear", this);
8256     },
8257
8258     /**
8259      * Inserts Records to the Store at the given index and fires the add event.
8260      * @param {Number} index The start index at which to insert the passed Records.
8261      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8262      */
8263     insert : function(index, records){
8264         records = [].concat(records);
8265         for(var i = 0, len = records.length; i < len; i++){
8266             this.data.insert(index, records[i]);
8267             records[i].join(this);
8268         }
8269         this.fireEvent("add", this, records, index);
8270     },
8271
8272     /**
8273      * Get the index within the cache of the passed Record.
8274      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8275      * @return {Number} The index of the passed Record. Returns -1 if not found.
8276      */
8277     indexOf : function(record){
8278         return this.data.indexOf(record);
8279     },
8280
8281     /**
8282      * Get the index within the cache of the Record with the passed id.
8283      * @param {String} id The id of the Record to find.
8284      * @return {Number} The index of the Record. Returns -1 if not found.
8285      */
8286     indexOfId : function(id){
8287         return this.data.indexOfKey(id);
8288     },
8289
8290     /**
8291      * Get the Record with the specified id.
8292      * @param {String} id The id of the Record to find.
8293      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8294      */
8295     getById : function(id){
8296         return this.data.key(id);
8297     },
8298
8299     /**
8300      * Get the Record at the specified index.
8301      * @param {Number} index The index of the Record to find.
8302      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8303      */
8304     getAt : function(index){
8305         return this.data.itemAt(index);
8306     },
8307
8308     /**
8309      * Returns a range of Records between specified indices.
8310      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8311      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8312      * @return {Roo.data.Record[]} An array of Records
8313      */
8314     getRange : function(start, end){
8315         return this.data.getRange(start, end);
8316     },
8317
8318     // private
8319     storeOptions : function(o){
8320         o = Roo.apply({}, o);
8321         delete o.callback;
8322         delete o.scope;
8323         this.lastOptions = o;
8324     },
8325
8326     /**
8327      * Loads the Record cache from the configured Proxy using the configured Reader.
8328      * <p>
8329      * If using remote paging, then the first load call must specify the <em>start</em>
8330      * and <em>limit</em> properties in the options.params property to establish the initial
8331      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8332      * <p>
8333      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8334      * and this call will return before the new data has been loaded. Perform any post-processing
8335      * in a callback function, or in a "load" event handler.</strong>
8336      * <p>
8337      * @param {Object} options An object containing properties which control loading options:<ul>
8338      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8339      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8340      * passed the following arguments:<ul>
8341      * <li>r : Roo.data.Record[]</li>
8342      * <li>options: Options object from the load call</li>
8343      * <li>success: Boolean success indicator</li></ul></li>
8344      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8345      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8346      * </ul>
8347      */
8348     load : function(options){
8349         options = options || {};
8350         if(this.fireEvent("beforeload", this, options) !== false){
8351             this.storeOptions(options);
8352             var p = Roo.apply(options.params || {}, this.baseParams);
8353             // if meta was not loaded from remote source.. try requesting it.
8354             if (!this.reader.metaFromRemote) {
8355                 p._requestMeta = 1;
8356             }
8357             if(this.sortInfo && this.remoteSort){
8358                 var pn = this.paramNames;
8359                 p[pn["sort"]] = this.sortInfo.field;
8360                 p[pn["dir"]] = this.sortInfo.direction;
8361             }
8362             if (this.multiSort) {
8363                 var pn = this.paramNames;
8364                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8365             }
8366             
8367             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8368         }
8369     },
8370
8371     /**
8372      * Reloads the Record cache from the configured Proxy using the configured Reader and
8373      * the options from the last load operation performed.
8374      * @param {Object} options (optional) An object containing properties which may override the options
8375      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8376      * the most recently used options are reused).
8377      */
8378     reload : function(options){
8379         this.load(Roo.applyIf(options||{}, this.lastOptions));
8380     },
8381
8382     // private
8383     // Called as a callback by the Reader during a load operation.
8384     loadRecords : function(o, options, success){
8385         if(!o || success === false){
8386             if(success !== false){
8387                 this.fireEvent("load", this, [], options, o);
8388             }
8389             if(options.callback){
8390                 options.callback.call(options.scope || this, [], options, false);
8391             }
8392             return;
8393         }
8394         // if data returned failure - throw an exception.
8395         if (o.success === false) {
8396             // show a message if no listener is registered.
8397             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8398                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8399             }
8400             // loadmask wil be hooked into this..
8401             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8402             return;
8403         }
8404         var r = o.records, t = o.totalRecords || r.length;
8405         
8406         this.fireEvent("beforeloadadd", this, r, options, o);
8407         
8408         if(!options || options.add !== true){
8409             if(this.pruneModifiedRecords){
8410                 this.modified = [];
8411             }
8412             for(var i = 0, len = r.length; i < len; i++){
8413                 r[i].join(this);
8414             }
8415             if(this.snapshot){
8416                 this.data = this.snapshot;
8417                 delete this.snapshot;
8418             }
8419             this.data.clear();
8420             this.data.addAll(r);
8421             this.totalLength = t;
8422             this.applySort();
8423             this.fireEvent("datachanged", this);
8424         }else{
8425             this.totalLength = Math.max(t, this.data.length+r.length);
8426             this.add(r);
8427         }
8428         this.fireEvent("load", this, r, options, o);
8429         if(options.callback){
8430             options.callback.call(options.scope || this, r, options, true);
8431         }
8432     },
8433
8434
8435     /**
8436      * Loads data from a passed data block. A Reader which understands the format of the data
8437      * must have been configured in the constructor.
8438      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8439      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8440      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8441      */
8442     loadData : function(o, append){
8443         var r = this.reader.readRecords(o);
8444         this.loadRecords(r, {add: append}, true);
8445     },
8446
8447     /**
8448      * Gets the number of cached records.
8449      * <p>
8450      * <em>If using paging, this may not be the total size of the dataset. If the data object
8451      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8452      * the data set size</em>
8453      */
8454     getCount : function(){
8455         return this.data.length || 0;
8456     },
8457
8458     /**
8459      * Gets the total number of records in the dataset as returned by the server.
8460      * <p>
8461      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8462      * the dataset size</em>
8463      */
8464     getTotalCount : function(){
8465         return this.totalLength || 0;
8466     },
8467
8468     /**
8469      * Returns the sort state of the Store as an object with two properties:
8470      * <pre><code>
8471  field {String} The name of the field by which the Records are sorted
8472  direction {String} The sort order, "ASC" or "DESC"
8473      * </code></pre>
8474      */
8475     getSortState : function(){
8476         return this.sortInfo;
8477     },
8478
8479     // private
8480     applySort : function(){
8481         if(this.sortInfo && !this.remoteSort){
8482             var s = this.sortInfo, f = s.field;
8483             var st = this.fields.get(f).sortType;
8484             var fn = function(r1, r2){
8485                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8486                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8487             };
8488             this.data.sort(s.direction, fn);
8489             if(this.snapshot && this.snapshot != this.data){
8490                 this.snapshot.sort(s.direction, fn);
8491             }
8492         }
8493     },
8494
8495     /**
8496      * Sets the default sort column and order to be used by the next load operation.
8497      * @param {String} fieldName The name of the field to sort by.
8498      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8499      */
8500     setDefaultSort : function(field, dir){
8501         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8502     },
8503
8504     /**
8505      * Sort the Records.
8506      * If remote sorting is used, the sort is performed on the server, and the cache is
8507      * reloaded. If local sorting is used, the cache is sorted internally.
8508      * @param {String} fieldName The name of the field to sort by.
8509      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8510      */
8511     sort : function(fieldName, dir){
8512         var f = this.fields.get(fieldName);
8513         if(!dir){
8514             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8515             
8516             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8517                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8518             }else{
8519                 dir = f.sortDir;
8520             }
8521         }
8522         this.sortToggle[f.name] = dir;
8523         this.sortInfo = {field: f.name, direction: dir};
8524         if(!this.remoteSort){
8525             this.applySort();
8526             this.fireEvent("datachanged", this);
8527         }else{
8528             this.load(this.lastOptions);
8529         }
8530     },
8531
8532     /**
8533      * Calls the specified function for each of the Records in the cache.
8534      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8535      * Returning <em>false</em> aborts and exits the iteration.
8536      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8537      */
8538     each : function(fn, scope){
8539         this.data.each(fn, scope);
8540     },
8541
8542     /**
8543      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8544      * (e.g., during paging).
8545      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8546      */
8547     getModifiedRecords : function(){
8548         return this.modified;
8549     },
8550
8551     // private
8552     createFilterFn : function(property, value, anyMatch){
8553         if(!value.exec){ // not a regex
8554             value = String(value);
8555             if(value.length == 0){
8556                 return false;
8557             }
8558             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8559         }
8560         return function(r){
8561             return value.test(r.data[property]);
8562         };
8563     },
8564
8565     /**
8566      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8567      * @param {String} property A field on your records
8568      * @param {Number} start The record index to start at (defaults to 0)
8569      * @param {Number} end The last record index to include (defaults to length - 1)
8570      * @return {Number} The sum
8571      */
8572     sum : function(property, start, end){
8573         var rs = this.data.items, v = 0;
8574         start = start || 0;
8575         end = (end || end === 0) ? end : rs.length-1;
8576
8577         for(var i = start; i <= end; i++){
8578             v += (rs[i].data[property] || 0);
8579         }
8580         return v;
8581     },
8582
8583     /**
8584      * Filter the records by a specified property.
8585      * @param {String} field A field on your records
8586      * @param {String/RegExp} value Either a string that the field
8587      * should start with or a RegExp to test against the field
8588      * @param {Boolean} anyMatch True to match any part not just the beginning
8589      */
8590     filter : function(property, value, anyMatch){
8591         var fn = this.createFilterFn(property, value, anyMatch);
8592         return fn ? this.filterBy(fn) : this.clearFilter();
8593     },
8594
8595     /**
8596      * Filter by a function. The specified function will be called with each
8597      * record in this data source. If the function returns true the record is included,
8598      * otherwise it is filtered.
8599      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8600      * @param {Object} scope (optional) The scope of the function (defaults to this)
8601      */
8602     filterBy : function(fn, scope){
8603         this.snapshot = this.snapshot || this.data;
8604         this.data = this.queryBy(fn, scope||this);
8605         this.fireEvent("datachanged", this);
8606     },
8607
8608     /**
8609      * Query the records by a specified property.
8610      * @param {String} field A field on your records
8611      * @param {String/RegExp} value Either a string that the field
8612      * should start with or a RegExp to test against the field
8613      * @param {Boolean} anyMatch True to match any part not just the beginning
8614      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8615      */
8616     query : function(property, value, anyMatch){
8617         var fn = this.createFilterFn(property, value, anyMatch);
8618         return fn ? this.queryBy(fn) : this.data.clone();
8619     },
8620
8621     /**
8622      * Query by a function. The specified function will be called with each
8623      * record in this data source. If the function returns true the record is included
8624      * in the results.
8625      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8626      * @param {Object} scope (optional) The scope of the function (defaults to this)
8627       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8628      **/
8629     queryBy : function(fn, scope){
8630         var data = this.snapshot || this.data;
8631         return data.filterBy(fn, scope||this);
8632     },
8633
8634     /**
8635      * Collects unique values for a particular dataIndex from this store.
8636      * @param {String} dataIndex The property to collect
8637      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8638      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8639      * @return {Array} An array of the unique values
8640      **/
8641     collect : function(dataIndex, allowNull, bypassFilter){
8642         var d = (bypassFilter === true && this.snapshot) ?
8643                 this.snapshot.items : this.data.items;
8644         var v, sv, r = [], l = {};
8645         for(var i = 0, len = d.length; i < len; i++){
8646             v = d[i].data[dataIndex];
8647             sv = String(v);
8648             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8649                 l[sv] = true;
8650                 r[r.length] = v;
8651             }
8652         }
8653         return r;
8654     },
8655
8656     /**
8657      * Revert to a view of the Record cache with no filtering applied.
8658      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8659      */
8660     clearFilter : function(suppressEvent){
8661         if(this.snapshot && this.snapshot != this.data){
8662             this.data = this.snapshot;
8663             delete this.snapshot;
8664             if(suppressEvent !== true){
8665                 this.fireEvent("datachanged", this);
8666             }
8667         }
8668     },
8669
8670     // private
8671     afterEdit : function(record){
8672         if(this.modified.indexOf(record) == -1){
8673             this.modified.push(record);
8674         }
8675         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8676     },
8677     
8678     // private
8679     afterReject : function(record){
8680         this.modified.remove(record);
8681         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8682     },
8683
8684     // private
8685     afterCommit : function(record){
8686         this.modified.remove(record);
8687         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8688     },
8689
8690     /**
8691      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8692      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8693      */
8694     commitChanges : function(){
8695         var m = this.modified.slice(0);
8696         this.modified = [];
8697         for(var i = 0, len = m.length; i < len; i++){
8698             m[i].commit();
8699         }
8700     },
8701
8702     /**
8703      * Cancel outstanding changes on all changed records.
8704      */
8705     rejectChanges : function(){
8706         var m = this.modified.slice(0);
8707         this.modified = [];
8708         for(var i = 0, len = m.length; i < len; i++){
8709             m[i].reject();
8710         }
8711     },
8712
8713     onMetaChange : function(meta, rtype, o){
8714         this.recordType = rtype;
8715         this.fields = rtype.prototype.fields;
8716         delete this.snapshot;
8717         this.sortInfo = meta.sortInfo || this.sortInfo;
8718         this.modified = [];
8719         this.fireEvent('metachange', this, this.reader.meta);
8720     },
8721     
8722     moveIndex : function(data, type)
8723     {
8724         var index = this.indexOf(data);
8725         
8726         var newIndex = index + type;
8727         
8728         this.remove(data);
8729         
8730         this.insert(newIndex, data);
8731         
8732     }
8733 });/*
8734  * Based on:
8735  * Ext JS Library 1.1.1
8736  * Copyright(c) 2006-2007, Ext JS, LLC.
8737  *
8738  * Originally Released Under LGPL - original licence link has changed is not relivant.
8739  *
8740  * Fork - LGPL
8741  * <script type="text/javascript">
8742  */
8743
8744 /**
8745  * @class Roo.data.SimpleStore
8746  * @extends Roo.data.Store
8747  * Small helper class to make creating Stores from Array data easier.
8748  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8749  * @cfg {Array} fields An array of field definition objects, or field name strings.
8750  * @cfg {Array} data The multi-dimensional array of data
8751  * @constructor
8752  * @param {Object} config
8753  */
8754 Roo.data.SimpleStore = function(config){
8755     Roo.data.SimpleStore.superclass.constructor.call(this, {
8756         isLocal : true,
8757         reader: new Roo.data.ArrayReader({
8758                 id: config.id
8759             },
8760             Roo.data.Record.create(config.fields)
8761         ),
8762         proxy : new Roo.data.MemoryProxy(config.data)
8763     });
8764     this.load();
8765 };
8766 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8767  * Based on:
8768  * Ext JS Library 1.1.1
8769  * Copyright(c) 2006-2007, Ext JS, LLC.
8770  *
8771  * Originally Released Under LGPL - original licence link has changed is not relivant.
8772  *
8773  * Fork - LGPL
8774  * <script type="text/javascript">
8775  */
8776
8777 /**
8778 /**
8779  * @extends Roo.data.Store
8780  * @class Roo.data.JsonStore
8781  * Small helper class to make creating Stores for JSON data easier. <br/>
8782 <pre><code>
8783 var store = new Roo.data.JsonStore({
8784     url: 'get-images.php',
8785     root: 'images',
8786     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8787 });
8788 </code></pre>
8789  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8790  * JsonReader and HttpProxy (unless inline data is provided).</b>
8791  * @cfg {Array} fields An array of field definition objects, or field name strings.
8792  * @constructor
8793  * @param {Object} config
8794  */
8795 Roo.data.JsonStore = function(c){
8796     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8797         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8798         reader: new Roo.data.JsonReader(c, c.fields)
8799     }));
8800 };
8801 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8802  * Based on:
8803  * Ext JS Library 1.1.1
8804  * Copyright(c) 2006-2007, Ext JS, LLC.
8805  *
8806  * Originally Released Under LGPL - original licence link has changed is not relivant.
8807  *
8808  * Fork - LGPL
8809  * <script type="text/javascript">
8810  */
8811
8812  
8813 Roo.data.Field = function(config){
8814     if(typeof config == "string"){
8815         config = {name: config};
8816     }
8817     Roo.apply(this, config);
8818     
8819     if(!this.type){
8820         this.type = "auto";
8821     }
8822     
8823     var st = Roo.data.SortTypes;
8824     // named sortTypes are supported, here we look them up
8825     if(typeof this.sortType == "string"){
8826         this.sortType = st[this.sortType];
8827     }
8828     
8829     // set default sortType for strings and dates
8830     if(!this.sortType){
8831         switch(this.type){
8832             case "string":
8833                 this.sortType = st.asUCString;
8834                 break;
8835             case "date":
8836                 this.sortType = st.asDate;
8837                 break;
8838             default:
8839                 this.sortType = st.none;
8840         }
8841     }
8842
8843     // define once
8844     var stripRe = /[\$,%]/g;
8845
8846     // prebuilt conversion function for this field, instead of
8847     // switching every time we're reading a value
8848     if(!this.convert){
8849         var cv, dateFormat = this.dateFormat;
8850         switch(this.type){
8851             case "":
8852             case "auto":
8853             case undefined:
8854                 cv = function(v){ return v; };
8855                 break;
8856             case "string":
8857                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8858                 break;
8859             case "int":
8860                 cv = function(v){
8861                     return v !== undefined && v !== null && v !== '' ?
8862                            parseInt(String(v).replace(stripRe, ""), 10) : '';
8863                     };
8864                 break;
8865             case "float":
8866                 cv = function(v){
8867                     return v !== undefined && v !== null && v !== '' ?
8868                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
8869                     };
8870                 break;
8871             case "bool":
8872             case "boolean":
8873                 cv = function(v){ return v === true || v === "true" || v == 1; };
8874                 break;
8875             case "date":
8876                 cv = function(v){
8877                     if(!v){
8878                         return '';
8879                     }
8880                     if(v instanceof Date){
8881                         return v;
8882                     }
8883                     if(dateFormat){
8884                         if(dateFormat == "timestamp"){
8885                             return new Date(v*1000);
8886                         }
8887                         return Date.parseDate(v, dateFormat);
8888                     }
8889                     var parsed = Date.parse(v);
8890                     return parsed ? new Date(parsed) : null;
8891                 };
8892              break;
8893             
8894         }
8895         this.convert = cv;
8896     }
8897 };
8898
8899 Roo.data.Field.prototype = {
8900     dateFormat: null,
8901     defaultValue: "",
8902     mapping: null,
8903     sortType : null,
8904     sortDir : "ASC"
8905 };/*
8906  * Based on:
8907  * Ext JS Library 1.1.1
8908  * Copyright(c) 2006-2007, Ext JS, LLC.
8909  *
8910  * Originally Released Under LGPL - original licence link has changed is not relivant.
8911  *
8912  * Fork - LGPL
8913  * <script type="text/javascript">
8914  */
8915  
8916 // Base class for reading structured data from a data source.  This class is intended to be
8917 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8918
8919 /**
8920  * @class Roo.data.DataReader
8921  * Base class for reading structured data from a data source.  This class is intended to be
8922  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8923  */
8924
8925 Roo.data.DataReader = function(meta, recordType){
8926     
8927     this.meta = meta;
8928     
8929     this.recordType = recordType instanceof Array ? 
8930         Roo.data.Record.create(recordType) : recordType;
8931 };
8932
8933 Roo.data.DataReader.prototype = {
8934      /**
8935      * Create an empty record
8936      * @param {Object} data (optional) - overlay some values
8937      * @return {Roo.data.Record} record created.
8938      */
8939     newRow :  function(d) {
8940         var da =  {};
8941         this.recordType.prototype.fields.each(function(c) {
8942             switch( c.type) {
8943                 case 'int' : da[c.name] = 0; break;
8944                 case 'date' : da[c.name] = new Date(); break;
8945                 case 'float' : da[c.name] = 0.0; break;
8946                 case 'boolean' : da[c.name] = false; break;
8947                 default : da[c.name] = ""; break;
8948             }
8949             
8950         });
8951         return new this.recordType(Roo.apply(da, d));
8952     }
8953     
8954 };/*
8955  * Based on:
8956  * Ext JS Library 1.1.1
8957  * Copyright(c) 2006-2007, Ext JS, LLC.
8958  *
8959  * Originally Released Under LGPL - original licence link has changed is not relivant.
8960  *
8961  * Fork - LGPL
8962  * <script type="text/javascript">
8963  */
8964
8965 /**
8966  * @class Roo.data.DataProxy
8967  * @extends Roo.data.Observable
8968  * This class is an abstract base class for implementations which provide retrieval of
8969  * unformatted data objects.<br>
8970  * <p>
8971  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8972  * (of the appropriate type which knows how to parse the data object) to provide a block of
8973  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8974  * <p>
8975  * Custom implementations must implement the load method as described in
8976  * {@link Roo.data.HttpProxy#load}.
8977  */
8978 Roo.data.DataProxy = function(){
8979     this.addEvents({
8980         /**
8981          * @event beforeload
8982          * Fires before a network request is made to retrieve a data object.
8983          * @param {Object} This DataProxy object.
8984          * @param {Object} params The params parameter to the load function.
8985          */
8986         beforeload : true,
8987         /**
8988          * @event load
8989          * Fires before the load method's callback is called.
8990          * @param {Object} This DataProxy object.
8991          * @param {Object} o The data object.
8992          * @param {Object} arg The callback argument object passed to the load function.
8993          */
8994         load : true,
8995         /**
8996          * @event loadexception
8997          * Fires if an Exception occurs during data retrieval.
8998          * @param {Object} This DataProxy object.
8999          * @param {Object} o The data object.
9000          * @param {Object} arg The callback argument object passed to the load function.
9001          * @param {Object} e The Exception.
9002          */
9003         loadexception : true
9004     });
9005     Roo.data.DataProxy.superclass.constructor.call(this);
9006 };
9007
9008 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9009
9010     /**
9011      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9012      */
9013 /*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023 /**
9024  * @class Roo.data.MemoryProxy
9025  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9026  * to the Reader when its load method is called.
9027  * @constructor
9028  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9029  */
9030 Roo.data.MemoryProxy = function(data){
9031     if (data.data) {
9032         data = data.data;
9033     }
9034     Roo.data.MemoryProxy.superclass.constructor.call(this);
9035     this.data = data;
9036 };
9037
9038 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9039     /**
9040      * Load data from the requested source (in this case an in-memory
9041      * data object passed to the constructor), read the data object into
9042      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9043      * process that block using the passed callback.
9044      * @param {Object} params This parameter is not used by the MemoryProxy class.
9045      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9046      * object into a block of Roo.data.Records.
9047      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9048      * The function must be passed <ul>
9049      * <li>The Record block object</li>
9050      * <li>The "arg" argument from the load function</li>
9051      * <li>A boolean success indicator</li>
9052      * </ul>
9053      * @param {Object} scope The scope in which to call the callback
9054      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9055      */
9056     load : function(params, reader, callback, scope, arg){
9057         params = params || {};
9058         var result;
9059         try {
9060             result = reader.readRecords(this.data);
9061         }catch(e){
9062             this.fireEvent("loadexception", this, arg, null, e);
9063             callback.call(scope, null, arg, false);
9064             return;
9065         }
9066         callback.call(scope, result, arg, true);
9067     },
9068     
9069     // private
9070     update : function(params, records){
9071         
9072     }
9073 });/*
9074  * Based on:
9075  * Ext JS Library 1.1.1
9076  * Copyright(c) 2006-2007, Ext JS, LLC.
9077  *
9078  * Originally Released Under LGPL - original licence link has changed is not relivant.
9079  *
9080  * Fork - LGPL
9081  * <script type="text/javascript">
9082  */
9083 /**
9084  * @class Roo.data.HttpProxy
9085  * @extends Roo.data.DataProxy
9086  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9087  * configured to reference a certain URL.<br><br>
9088  * <p>
9089  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9090  * from which the running page was served.<br><br>
9091  * <p>
9092  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9093  * <p>
9094  * Be aware that to enable the browser to parse an XML document, the server must set
9095  * the Content-Type header in the HTTP response to "text/xml".
9096  * @constructor
9097  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9098  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9099  * will be used to make the request.
9100  */
9101 Roo.data.HttpProxy = function(conn){
9102     Roo.data.HttpProxy.superclass.constructor.call(this);
9103     // is conn a conn config or a real conn?
9104     this.conn = conn;
9105     this.useAjax = !conn || !conn.events;
9106   
9107 };
9108
9109 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9110     // thse are take from connection...
9111     
9112     /**
9113      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9114      */
9115     /**
9116      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9117      * extra parameters to each request made by this object. (defaults to undefined)
9118      */
9119     /**
9120      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9121      *  to each request made by this object. (defaults to undefined)
9122      */
9123     /**
9124      * @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)
9125      */
9126     /**
9127      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9128      */
9129      /**
9130      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9131      * @type Boolean
9132      */
9133   
9134
9135     /**
9136      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9137      * @type Boolean
9138      */
9139     /**
9140      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9141      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9142      * a finer-grained basis than the DataProxy events.
9143      */
9144     getConnection : function(){
9145         return this.useAjax ? Roo.Ajax : this.conn;
9146     },
9147
9148     /**
9149      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9150      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9151      * process that block using the passed callback.
9152      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9153      * for the request to the remote server.
9154      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9155      * object into a block of Roo.data.Records.
9156      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9157      * The function must be passed <ul>
9158      * <li>The Record block object</li>
9159      * <li>The "arg" argument from the load function</li>
9160      * <li>A boolean success indicator</li>
9161      * </ul>
9162      * @param {Object} scope The scope in which to call the callback
9163      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9164      */
9165     load : function(params, reader, callback, scope, arg){
9166         if(this.fireEvent("beforeload", this, params) !== false){
9167             var  o = {
9168                 params : params || {},
9169                 request: {
9170                     callback : callback,
9171                     scope : scope,
9172                     arg : arg
9173                 },
9174                 reader: reader,
9175                 callback : this.loadResponse,
9176                 scope: this
9177             };
9178             if(this.useAjax){
9179                 Roo.applyIf(o, this.conn);
9180                 if(this.activeRequest){
9181                     Roo.Ajax.abort(this.activeRequest);
9182                 }
9183                 this.activeRequest = Roo.Ajax.request(o);
9184             }else{
9185                 this.conn.request(o);
9186             }
9187         }else{
9188             callback.call(scope||this, null, arg, false);
9189         }
9190     },
9191
9192     // private
9193     loadResponse : function(o, success, response){
9194         delete this.activeRequest;
9195         if(!success){
9196             this.fireEvent("loadexception", this, o, response);
9197             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9198             return;
9199         }
9200         var result;
9201         try {
9202             result = o.reader.read(response);
9203         }catch(e){
9204             this.fireEvent("loadexception", this, o, response, e);
9205             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9206             return;
9207         }
9208         
9209         this.fireEvent("load", this, o, o.request.arg);
9210         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9211     },
9212
9213     // private
9214     update : function(dataSet){
9215
9216     },
9217
9218     // private
9219     updateResponse : function(dataSet){
9220
9221     }
9222 });/*
9223  * Based on:
9224  * Ext JS Library 1.1.1
9225  * Copyright(c) 2006-2007, Ext JS, LLC.
9226  *
9227  * Originally Released Under LGPL - original licence link has changed is not relivant.
9228  *
9229  * Fork - LGPL
9230  * <script type="text/javascript">
9231  */
9232
9233 /**
9234  * @class Roo.data.ScriptTagProxy
9235  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9236  * other than the originating domain of the running page.<br><br>
9237  * <p>
9238  * <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
9239  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9240  * <p>
9241  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9242  * source code that is used as the source inside a &lt;script> tag.<br><br>
9243  * <p>
9244  * In order for the browser to process the returned data, the server must wrap the data object
9245  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9246  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9247  * depending on whether the callback name was passed:
9248  * <p>
9249  * <pre><code>
9250 boolean scriptTag = false;
9251 String cb = request.getParameter("callback");
9252 if (cb != null) {
9253     scriptTag = true;
9254     response.setContentType("text/javascript");
9255 } else {
9256     response.setContentType("application/x-json");
9257 }
9258 Writer out = response.getWriter();
9259 if (scriptTag) {
9260     out.write(cb + "(");
9261 }
9262 out.print(dataBlock.toJsonString());
9263 if (scriptTag) {
9264     out.write(");");
9265 }
9266 </pre></code>
9267  *
9268  * @constructor
9269  * @param {Object} config A configuration object.
9270  */
9271 Roo.data.ScriptTagProxy = function(config){
9272     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9273     Roo.apply(this, config);
9274     this.head = document.getElementsByTagName("head")[0];
9275 };
9276
9277 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9278
9279 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9280     /**
9281      * @cfg {String} url The URL from which to request the data object.
9282      */
9283     /**
9284      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9285      */
9286     timeout : 30000,
9287     /**
9288      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9289      * the server the name of the callback function set up by the load call to process the returned data object.
9290      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9291      * javascript output which calls this named function passing the data object as its only parameter.
9292      */
9293     callbackParam : "callback",
9294     /**
9295      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9296      * name to the request.
9297      */
9298     nocache : true,
9299
9300     /**
9301      * Load data from the configured URL, read the data object into
9302      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9303      * process that block using the passed callback.
9304      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9305      * for the request to the remote server.
9306      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9307      * object into a block of Roo.data.Records.
9308      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9309      * The function must be passed <ul>
9310      * <li>The Record block object</li>
9311      * <li>The "arg" argument from the load function</li>
9312      * <li>A boolean success indicator</li>
9313      * </ul>
9314      * @param {Object} scope The scope in which to call the callback
9315      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9316      */
9317     load : function(params, reader, callback, scope, arg){
9318         if(this.fireEvent("beforeload", this, params) !== false){
9319
9320             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9321
9322             var url = this.url;
9323             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9324             if(this.nocache){
9325                 url += "&_dc=" + (new Date().getTime());
9326             }
9327             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9328             var trans = {
9329                 id : transId,
9330                 cb : "stcCallback"+transId,
9331                 scriptId : "stcScript"+transId,
9332                 params : params,
9333                 arg : arg,
9334                 url : url,
9335                 callback : callback,
9336                 scope : scope,
9337                 reader : reader
9338             };
9339             var conn = this;
9340
9341             window[trans.cb] = function(o){
9342                 conn.handleResponse(o, trans);
9343             };
9344
9345             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9346
9347             if(this.autoAbort !== false){
9348                 this.abort();
9349             }
9350
9351             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9352
9353             var script = document.createElement("script");
9354             script.setAttribute("src", url);
9355             script.setAttribute("type", "text/javascript");
9356             script.setAttribute("id", trans.scriptId);
9357             this.head.appendChild(script);
9358
9359             this.trans = trans;
9360         }else{
9361             callback.call(scope||this, null, arg, false);
9362         }
9363     },
9364
9365     // private
9366     isLoading : function(){
9367         return this.trans ? true : false;
9368     },
9369
9370     /**
9371      * Abort the current server request.
9372      */
9373     abort : function(){
9374         if(this.isLoading()){
9375             this.destroyTrans(this.trans);
9376         }
9377     },
9378
9379     // private
9380     destroyTrans : function(trans, isLoaded){
9381         this.head.removeChild(document.getElementById(trans.scriptId));
9382         clearTimeout(trans.timeoutId);
9383         if(isLoaded){
9384             window[trans.cb] = undefined;
9385             try{
9386                 delete window[trans.cb];
9387             }catch(e){}
9388         }else{
9389             // if hasn't been loaded, wait for load to remove it to prevent script error
9390             window[trans.cb] = function(){
9391                 window[trans.cb] = undefined;
9392                 try{
9393                     delete window[trans.cb];
9394                 }catch(e){}
9395             };
9396         }
9397     },
9398
9399     // private
9400     handleResponse : function(o, trans){
9401         this.trans = false;
9402         this.destroyTrans(trans, true);
9403         var result;
9404         try {
9405             result = trans.reader.readRecords(o);
9406         }catch(e){
9407             this.fireEvent("loadexception", this, o, trans.arg, e);
9408             trans.callback.call(trans.scope||window, null, trans.arg, false);
9409             return;
9410         }
9411         this.fireEvent("load", this, o, trans.arg);
9412         trans.callback.call(trans.scope||window, result, trans.arg, true);
9413     },
9414
9415     // private
9416     handleFailure : function(trans){
9417         this.trans = false;
9418         this.destroyTrans(trans, false);
9419         this.fireEvent("loadexception", this, null, trans.arg);
9420         trans.callback.call(trans.scope||window, null, trans.arg, false);
9421     }
9422 });/*
9423  * Based on:
9424  * Ext JS Library 1.1.1
9425  * Copyright(c) 2006-2007, Ext JS, LLC.
9426  *
9427  * Originally Released Under LGPL - original licence link has changed is not relivant.
9428  *
9429  * Fork - LGPL
9430  * <script type="text/javascript">
9431  */
9432
9433 /**
9434  * @class Roo.data.JsonReader
9435  * @extends Roo.data.DataReader
9436  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9437  * based on mappings in a provided Roo.data.Record constructor.
9438  * 
9439  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9440  * in the reply previously. 
9441  * 
9442  * <p>
9443  * Example code:
9444  * <pre><code>
9445 var RecordDef = Roo.data.Record.create([
9446     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9447     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9448 ]);
9449 var myReader = new Roo.data.JsonReader({
9450     totalProperty: "results",    // The property which contains the total dataset size (optional)
9451     root: "rows",                // The property which contains an Array of row objects
9452     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9453 }, RecordDef);
9454 </code></pre>
9455  * <p>
9456  * This would consume a JSON file like this:
9457  * <pre><code>
9458 { 'results': 2, 'rows': [
9459     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9460     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9461 }
9462 </code></pre>
9463  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9464  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9465  * paged from the remote server.
9466  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9467  * @cfg {String} root name of the property which contains the Array of row objects.
9468  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9469  * @constructor
9470  * Create a new JsonReader
9471  * @param {Object} meta Metadata configuration options
9472  * @param {Object} recordType Either an Array of field definition objects,
9473  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9474  */
9475 Roo.data.JsonReader = function(meta, recordType){
9476     
9477     meta = meta || {};
9478     // set some defaults:
9479     Roo.applyIf(meta, {
9480         totalProperty: 'total',
9481         successProperty : 'success',
9482         root : 'data',
9483         id : 'id'
9484     });
9485     
9486     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9487 };
9488 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9489     
9490     /**
9491      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9492      * Used by Store query builder to append _requestMeta to params.
9493      * 
9494      */
9495     metaFromRemote : false,
9496     /**
9497      * This method is only used by a DataProxy which has retrieved data from a remote server.
9498      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9499      * @return {Object} data A data block which is used by an Roo.data.Store object as
9500      * a cache of Roo.data.Records.
9501      */
9502     read : function(response){
9503         var json = response.responseText;
9504        
9505         var o = /* eval:var:o */ eval("("+json+")");
9506         if(!o) {
9507             throw {message: "JsonReader.read: Json object not found"};
9508         }
9509         
9510         if(o.metaData){
9511             
9512             delete this.ef;
9513             this.metaFromRemote = true;
9514             this.meta = o.metaData;
9515             this.recordType = Roo.data.Record.create(o.metaData.fields);
9516             this.onMetaChange(this.meta, this.recordType, o);
9517         }
9518         return this.readRecords(o);
9519     },
9520
9521     // private function a store will implement
9522     onMetaChange : function(meta, recordType, o){
9523
9524     },
9525
9526     /**
9527          * @ignore
9528          */
9529     simpleAccess: function(obj, subsc) {
9530         return obj[subsc];
9531     },
9532
9533         /**
9534          * @ignore
9535          */
9536     getJsonAccessor: function(){
9537         var re = /[\[\.]/;
9538         return function(expr) {
9539             try {
9540                 return(re.test(expr))
9541                     ? new Function("obj", "return obj." + expr)
9542                     : function(obj){
9543                         return obj[expr];
9544                     };
9545             } catch(e){}
9546             return Roo.emptyFn;
9547         };
9548     }(),
9549
9550     /**
9551      * Create a data block containing Roo.data.Records from an XML document.
9552      * @param {Object} o An object which contains an Array of row objects in the property specified
9553      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9554      * which contains the total size of the dataset.
9555      * @return {Object} data A data block which is used by an Roo.data.Store object as
9556      * a cache of Roo.data.Records.
9557      */
9558     readRecords : function(o){
9559         /**
9560          * After any data loads, the raw JSON data is available for further custom processing.
9561          * @type Object
9562          */
9563         this.o = o;
9564         var s = this.meta, Record = this.recordType,
9565             f = Record.prototype.fields, fi = f.items, fl = f.length;
9566
9567 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9568         if (!this.ef) {
9569             if(s.totalProperty) {
9570                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9571                 }
9572                 if(s.successProperty) {
9573                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9574                 }
9575                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9576                 if (s.id) {
9577                         var g = this.getJsonAccessor(s.id);
9578                         this.getId = function(rec) {
9579                                 var r = g(rec);
9580                                 return (r === undefined || r === "") ? null : r;
9581                         };
9582                 } else {
9583                         this.getId = function(){return null;};
9584                 }
9585             this.ef = [];
9586             for(var jj = 0; jj < fl; jj++){
9587                 f = fi[jj];
9588                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9589                 this.ef[jj] = this.getJsonAccessor(map);
9590             }
9591         }
9592
9593         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9594         if(s.totalProperty){
9595             var vt = parseInt(this.getTotal(o), 10);
9596             if(!isNaN(vt)){
9597                 totalRecords = vt;
9598             }
9599         }
9600         if(s.successProperty){
9601             var vs = this.getSuccess(o);
9602             if(vs === false || vs === 'false'){
9603                 success = false;
9604             }
9605         }
9606         var records = [];
9607             for(var i = 0; i < c; i++){
9608                     var n = root[i];
9609                 var values = {};
9610                 var id = this.getId(n);
9611                 for(var j = 0; j < fl; j++){
9612                     f = fi[j];
9613                 var v = this.ef[j](n);
9614                 if (!f.convert) {
9615                     Roo.log('missing convert for ' + f.name);
9616                     Roo.log(f);
9617                     continue;
9618                 }
9619                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9620                 }
9621                 var record = new Record(values, id);
9622                 record.json = n;
9623                 records[i] = record;
9624             }
9625             return {
9626             raw : o,
9627                 success : success,
9628                 records : records,
9629                 totalRecords : totalRecords
9630             };
9631     }
9632 });/*
9633  * Based on:
9634  * Ext JS Library 1.1.1
9635  * Copyright(c) 2006-2007, Ext JS, LLC.
9636  *
9637  * Originally Released Under LGPL - original licence link has changed is not relivant.
9638  *
9639  * Fork - LGPL
9640  * <script type="text/javascript">
9641  */
9642
9643 /**
9644  * @class Roo.data.ArrayReader
9645  * @extends Roo.data.DataReader
9646  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9647  * Each element of that Array represents a row of data fields. The
9648  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9649  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9650  * <p>
9651  * Example code:.
9652  * <pre><code>
9653 var RecordDef = Roo.data.Record.create([
9654     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9655     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9656 ]);
9657 var myReader = new Roo.data.ArrayReader({
9658     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9659 }, RecordDef);
9660 </code></pre>
9661  * <p>
9662  * This would consume an Array like this:
9663  * <pre><code>
9664 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9665   </code></pre>
9666  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9667  * @constructor
9668  * Create a new JsonReader
9669  * @param {Object} meta Metadata configuration options.
9670  * @param {Object} recordType Either an Array of field definition objects
9671  * as specified to {@link Roo.data.Record#create},
9672  * or an {@link Roo.data.Record} object
9673  * created using {@link Roo.data.Record#create}.
9674  */
9675 Roo.data.ArrayReader = function(meta, recordType){
9676     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9677 };
9678
9679 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9680     /**
9681      * Create a data block containing Roo.data.Records from an XML document.
9682      * @param {Object} o An Array of row objects which represents the dataset.
9683      * @return {Object} data A data block which is used by an Roo.data.Store object as
9684      * a cache of Roo.data.Records.
9685      */
9686     readRecords : function(o){
9687         var sid = this.meta ? this.meta.id : null;
9688         var recordType = this.recordType, fields = recordType.prototype.fields;
9689         var records = [];
9690         var root = o;
9691             for(var i = 0; i < root.length; i++){
9692                     var n = root[i];
9693                 var values = {};
9694                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9695                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9696                 var f = fields.items[j];
9697                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9698                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9699                 v = f.convert(v);
9700                 values[f.name] = v;
9701             }
9702                 var record = new recordType(values, id);
9703                 record.json = n;
9704                 records[records.length] = record;
9705             }
9706             return {
9707                 records : records,
9708                 totalRecords : records.length
9709             };
9710     }
9711 });/*
9712  * - LGPL
9713  * * 
9714  */
9715
9716 /**
9717  * @class Roo.bootstrap.ComboBox
9718  * @extends Roo.bootstrap.TriggerField
9719  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9720  * @cfg {Boolean} append (true|false) default false
9721  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9722  * @constructor
9723  * Create a new ComboBox.
9724  * @param {Object} config Configuration options
9725  */
9726 Roo.bootstrap.ComboBox = function(config){
9727     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9728     this.addEvents({
9729         /**
9730          * @event expand
9731          * Fires when the dropdown list is expanded
9732              * @param {Roo.bootstrap.ComboBox} combo This combo box
9733              */
9734         'expand' : true,
9735         /**
9736          * @event collapse
9737          * Fires when the dropdown list is collapsed
9738              * @param {Roo.bootstrap.ComboBox} combo This combo box
9739              */
9740         'collapse' : true,
9741         /**
9742          * @event beforeselect
9743          * Fires before a list item is selected. Return false to cancel the selection.
9744              * @param {Roo.bootstrap.ComboBox} combo This combo box
9745              * @param {Roo.data.Record} record The data record returned from the underlying store
9746              * @param {Number} index The index of the selected item in the dropdown list
9747              */
9748         'beforeselect' : true,
9749         /**
9750          * @event select
9751          * Fires when a list item is selected
9752              * @param {Roo.bootstrap.ComboBox} combo This combo box
9753              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9754              * @param {Number} index The index of the selected item in the dropdown list
9755              */
9756         'select' : true,
9757         /**
9758          * @event beforequery
9759          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9760          * The event object passed has these properties:
9761              * @param {Roo.bootstrap.ComboBox} combo This combo box
9762              * @param {String} query The query
9763              * @param {Boolean} forceAll true to force "all" query
9764              * @param {Boolean} cancel true to cancel the query
9765              * @param {Object} e The query event object
9766              */
9767         'beforequery': true,
9768          /**
9769          * @event add
9770          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9771              * @param {Roo.bootstrap.ComboBox} combo This combo box
9772              */
9773         'add' : true,
9774         /**
9775          * @event edit
9776          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9777              * @param {Roo.bootstrap.ComboBox} combo This combo box
9778              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9779              */
9780         'edit' : true,
9781         /**
9782          * @event remove
9783          * Fires when the remove value from the combobox array
9784              * @param {Roo.bootstrap.ComboBox} combo This combo box
9785              */
9786         'remove' : true
9787         
9788     });
9789     
9790     
9791     this.selectedIndex = -1;
9792     if(this.mode == 'local'){
9793         if(config.queryDelay === undefined){
9794             this.queryDelay = 10;
9795         }
9796         if(config.minChars === undefined){
9797             this.minChars = 0;
9798         }
9799     }
9800 };
9801
9802 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9803      
9804     /**
9805      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9806      * rendering into an Roo.Editor, defaults to false)
9807      */
9808     /**
9809      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9810      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9811      */
9812     /**
9813      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9814      */
9815     /**
9816      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9817      * the dropdown list (defaults to undefined, with no header element)
9818      */
9819
9820      /**
9821      * @cfg {String/Roo.Template} tpl The template to use to render the output
9822      */
9823      
9824      /**
9825      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9826      */
9827     listWidth: undefined,
9828     /**
9829      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9830      * mode = 'remote' or 'text' if mode = 'local')
9831      */
9832     displayField: undefined,
9833     /**
9834      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9835      * mode = 'remote' or 'value' if mode = 'local'). 
9836      * Note: use of a valueField requires the user make a selection
9837      * in order for a value to be mapped.
9838      */
9839     valueField: undefined,
9840     
9841     
9842     /**
9843      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9844      * field's data value (defaults to the underlying DOM element's name)
9845      */
9846     hiddenName: undefined,
9847     /**
9848      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9849      */
9850     listClass: '',
9851     /**
9852      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9853      */
9854     selectedClass: 'active',
9855     
9856     /**
9857      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9858      */
9859     shadow:'sides',
9860     /**
9861      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9862      * anchor positions (defaults to 'tl-bl')
9863      */
9864     listAlign: 'tl-bl?',
9865     /**
9866      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9867      */
9868     maxHeight: 300,
9869     /**
9870      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
9871      * query specified by the allQuery config option (defaults to 'query')
9872      */
9873     triggerAction: 'query',
9874     /**
9875      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9876      * (defaults to 4, does not apply if editable = false)
9877      */
9878     minChars : 4,
9879     /**
9880      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9881      * delay (typeAheadDelay) if it matches a known value (defaults to false)
9882      */
9883     typeAhead: false,
9884     /**
9885      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9886      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9887      */
9888     queryDelay: 500,
9889     /**
9890      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9891      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
9892      */
9893     pageSize: 0,
9894     /**
9895      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
9896      * when editable = true (defaults to false)
9897      */
9898     selectOnFocus:false,
9899     /**
9900      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9901      */
9902     queryParam: 'query',
9903     /**
9904      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
9905      * when mode = 'remote' (defaults to 'Loading...')
9906      */
9907     loadingText: 'Loading...',
9908     /**
9909      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9910      */
9911     resizable: false,
9912     /**
9913      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9914      */
9915     handleHeight : 8,
9916     /**
9917      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9918      * traditional select (defaults to true)
9919      */
9920     editable: true,
9921     /**
9922      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9923      */
9924     allQuery: '',
9925     /**
9926      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9927      */
9928     mode: 'remote',
9929     /**
9930      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9931      * listWidth has a higher value)
9932      */
9933     minListWidth : 70,
9934     /**
9935      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9936      * allow the user to set arbitrary text into the field (defaults to false)
9937      */
9938     forceSelection:false,
9939     /**
9940      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9941      * if typeAhead = true (defaults to 250)
9942      */
9943     typeAheadDelay : 250,
9944     /**
9945      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9946      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9947      */
9948     valueNotFoundText : undefined,
9949     /**
9950      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9951      */
9952     blockFocus : false,
9953     
9954     /**
9955      * @cfg {Boolean} disableClear Disable showing of clear button.
9956      */
9957     disableClear : false,
9958     /**
9959      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
9960      */
9961     alwaysQuery : false,
9962     
9963     /**
9964      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
9965      */
9966     multiple : false,
9967     
9968     //private
9969     addicon : false,
9970     editicon: false,
9971     
9972     page: 0,
9973     hasQuery: false,
9974     append: false,
9975     loadNext: false,
9976     autoFocus : true,
9977     item: [],
9978     
9979     // element that contains real text value.. (when hidden is used..)
9980      
9981     // private
9982     initEvents: function(){
9983         
9984         if (!this.store) {
9985             throw "can not find store for combo";
9986         }
9987         this.store = Roo.factory(this.store, Roo.data);
9988         
9989         
9990         
9991         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9992         
9993         
9994         if(this.hiddenName){
9995             
9996             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9997             
9998             this.hiddenField.dom.value =
9999                 this.hiddenValue !== undefined ? this.hiddenValue :
10000                 this.value !== undefined ? this.value : '';
10001
10002             // prevent input submission
10003             this.el.dom.removeAttribute('name');
10004             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10005              
10006              
10007         }
10008         //if(Roo.isGecko){
10009         //    this.el.dom.setAttribute('autocomplete', 'off');
10010         //}
10011
10012         var cls = 'x-combo-list';
10013         this.list = this.el.select('ul.dropdown-menu',true).first();
10014
10015         //this.list = new Roo.Layer({
10016         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10017         //});
10018         
10019         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
10020         this.list.setWidth(lw);
10021         
10022         this.list.on('mouseover', this.onViewOver, this);
10023         this.list.on('mousemove', this.onViewMove, this);
10024         
10025         this.list.on('scroll', this.onViewScroll, this);
10026         
10027         /*
10028         this.list.swallowEvent('mousewheel');
10029         this.assetHeight = 0;
10030
10031         if(this.title){
10032             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10033             this.assetHeight += this.header.getHeight();
10034         }
10035
10036         this.innerList = this.list.createChild({cls:cls+'-inner'});
10037         this.innerList.on('mouseover', this.onViewOver, this);
10038         this.innerList.on('mousemove', this.onViewMove, this);
10039         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10040         
10041         if(this.allowBlank && !this.pageSize && !this.disableClear){
10042             this.footer = this.list.createChild({cls:cls+'-ft'});
10043             this.pageTb = new Roo.Toolbar(this.footer);
10044            
10045         }
10046         if(this.pageSize){
10047             this.footer = this.list.createChild({cls:cls+'-ft'});
10048             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10049                     {pageSize: this.pageSize});
10050             
10051         }
10052         
10053         if (this.pageTb && this.allowBlank && !this.disableClear) {
10054             var _this = this;
10055             this.pageTb.add(new Roo.Toolbar.Fill(), {
10056                 cls: 'x-btn-icon x-btn-clear',
10057                 text: '&#160;',
10058                 handler: function()
10059                 {
10060                     _this.collapse();
10061                     _this.clearValue();
10062                     _this.onSelect(false, -1);
10063                 }
10064             });
10065         }
10066         if (this.footer) {
10067             this.assetHeight += this.footer.getHeight();
10068         }
10069         */
10070             
10071         if(!this.tpl){
10072             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10073         }
10074
10075         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10076             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10077         });
10078         //this.view.wrapEl.setDisplayed(false);
10079         this.view.on('click', this.onViewClick, this);
10080         
10081         
10082         
10083         this.store.on('beforeload', this.onBeforeLoad, this);
10084         this.store.on('load', this.onLoad, this);
10085         this.store.on('loadexception', this.onLoadException, this);
10086         /*
10087         if(this.resizable){
10088             this.resizer = new Roo.Resizable(this.list,  {
10089                pinned:true, handles:'se'
10090             });
10091             this.resizer.on('resize', function(r, w, h){
10092                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10093                 this.listWidth = w;
10094                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10095                 this.restrictHeight();
10096             }, this);
10097             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10098         }
10099         */
10100         if(!this.editable){
10101             this.editable = true;
10102             this.setEditable(false);
10103         }
10104         
10105         /*
10106         
10107         if (typeof(this.events.add.listeners) != 'undefined') {
10108             
10109             this.addicon = this.wrap.createChild(
10110                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10111        
10112             this.addicon.on('click', function(e) {
10113                 this.fireEvent('add', this);
10114             }, this);
10115         }
10116         if (typeof(this.events.edit.listeners) != 'undefined') {
10117             
10118             this.editicon = this.wrap.createChild(
10119                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10120             if (this.addicon) {
10121                 this.editicon.setStyle('margin-left', '40px');
10122             }
10123             this.editicon.on('click', function(e) {
10124                 
10125                 // we fire even  if inothing is selected..
10126                 this.fireEvent('edit', this, this.lastData );
10127                 
10128             }, this);
10129         }
10130         */
10131         
10132         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10133             "up" : function(e){
10134                 this.inKeyMode = true;
10135                 this.selectPrev();
10136             },
10137
10138             "down" : function(e){
10139                 if(!this.isExpanded()){
10140                     this.onTriggerClick();
10141                 }else{
10142                     this.inKeyMode = true;
10143                     this.selectNext();
10144                 }
10145             },
10146
10147             "enter" : function(e){
10148 //                this.onViewClick();
10149                 //return true;
10150                 this.collapse();
10151                 
10152                 if(this.fireEvent("specialkey", this, e)){
10153                     this.onViewClick(false);
10154                 }
10155                 
10156                 return true;
10157             },
10158
10159             "esc" : function(e){
10160                 this.collapse();
10161             },
10162
10163             "tab" : function(e){
10164                 this.collapse();
10165                 
10166                 if(this.fireEvent("specialkey", this, e)){
10167                     this.onViewClick(false);
10168                 }
10169                 
10170                 return true;
10171             },
10172
10173             scope : this,
10174
10175             doRelay : function(foo, bar, hname){
10176                 if(hname == 'down' || this.scope.isExpanded()){
10177                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10178                 }
10179                 return true;
10180             },
10181
10182             forceKeyDown: true
10183         });
10184         
10185         
10186         this.queryDelay = Math.max(this.queryDelay || 10,
10187                 this.mode == 'local' ? 10 : 250);
10188         
10189         
10190         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10191         
10192         if(this.typeAhead){
10193             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10194         }
10195         if(this.editable !== false){
10196             this.inputEl().on("keyup", this.onKeyUp, this);
10197         }
10198         if(this.forceSelection){
10199             this.inputEl().on('blur', this.doForce, this);
10200         }
10201         
10202         if(this.multiple){
10203             this.choices = this.el.select('ul.select2-choices', true).first();
10204             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10205         }
10206     },
10207
10208     onDestroy : function(){
10209         if(this.view){
10210             this.view.setStore(null);
10211             this.view.el.removeAllListeners();
10212             this.view.el.remove();
10213             this.view.purgeListeners();
10214         }
10215         if(this.list){
10216             this.list.dom.innerHTML  = '';
10217         }
10218         if(this.store){
10219             this.store.un('beforeload', this.onBeforeLoad, this);
10220             this.store.un('load', this.onLoad, this);
10221             this.store.un('loadexception', this.onLoadException, this);
10222         }
10223         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10224     },
10225
10226     // private
10227     fireKey : function(e){
10228         if(e.isNavKeyPress() && !this.list.isVisible()){
10229             this.fireEvent("specialkey", this, e);
10230         }
10231     },
10232
10233     // private
10234     onResize: function(w, h){
10235 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10236 //        
10237 //        if(typeof w != 'number'){
10238 //            // we do not handle it!?!?
10239 //            return;
10240 //        }
10241 //        var tw = this.trigger.getWidth();
10242 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10243 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10244 //        var x = w - tw;
10245 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10246 //            
10247 //        //this.trigger.setStyle('left', x+'px');
10248 //        
10249 //        if(this.list && this.listWidth === undefined){
10250 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10251 //            this.list.setWidth(lw);
10252 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10253 //        }
10254         
10255     
10256         
10257     },
10258
10259     /**
10260      * Allow or prevent the user from directly editing the field text.  If false is passed,
10261      * the user will only be able to select from the items defined in the dropdown list.  This method
10262      * is the runtime equivalent of setting the 'editable' config option at config time.
10263      * @param {Boolean} value True to allow the user to directly edit the field text
10264      */
10265     setEditable : function(value){
10266         if(value == this.editable){
10267             return;
10268         }
10269         this.editable = value;
10270         if(!value){
10271             this.inputEl().dom.setAttribute('readOnly', true);
10272             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10273             this.inputEl().addClass('x-combo-noedit');
10274         }else{
10275             this.inputEl().dom.setAttribute('readOnly', false);
10276             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10277             this.inputEl().removeClass('x-combo-noedit');
10278         }
10279     },
10280
10281     // private
10282     
10283     onBeforeLoad : function(combo,opts){
10284         if(!this.hasFocus){
10285             return;
10286         }
10287          if (!opts.add) {
10288             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10289          }
10290         this.restrictHeight();
10291         this.selectedIndex = -1;
10292     },
10293
10294     // private
10295     onLoad : function(){
10296         
10297         this.hasQuery = false;
10298         
10299         if(!this.hasFocus){
10300             return;
10301         }
10302         
10303         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10304             this.loading.hide();
10305         }
10306         
10307         if(this.store.getCount() > 0){
10308             this.expand();
10309             this.restrictHeight();
10310             if(this.lastQuery == this.allQuery){
10311                 if(this.editable){
10312                     this.inputEl().dom.select();
10313                 }
10314                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10315                     this.select(0, true);
10316                 }
10317             }else{
10318                 if(this.autoFocus){
10319                     this.selectNext();
10320                 }
10321                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10322                     this.taTask.delay(this.typeAheadDelay);
10323                 }
10324             }
10325         }else{
10326             this.onEmptyResults();
10327         }
10328         
10329         //this.el.focus();
10330     },
10331     // private
10332     onLoadException : function()
10333     {
10334         this.hasQuery = false;
10335         
10336         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10337             this.loading.hide();
10338         }
10339         
10340         this.collapse();
10341         Roo.log(this.store.reader.jsonData);
10342         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10343             // fixme
10344             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10345         }
10346         
10347         
10348     },
10349     // private
10350     onTypeAhead : function(){
10351         if(this.store.getCount() > 0){
10352             var r = this.store.getAt(0);
10353             var newValue = r.data[this.displayField];
10354             var len = newValue.length;
10355             var selStart = this.getRawValue().length;
10356             
10357             if(selStart != len){
10358                 this.setRawValue(newValue);
10359                 this.selectText(selStart, newValue.length);
10360             }
10361         }
10362     },
10363
10364     // private
10365     onSelect : function(record, index){
10366         
10367         if(this.fireEvent('beforeselect', this, record, index) !== false){
10368         
10369             this.setFromData(index > -1 ? record.data : false);
10370             
10371             this.collapse();
10372             this.fireEvent('select', this, record, index);
10373         }
10374     },
10375
10376     /**
10377      * Returns the currently selected field value or empty string if no value is set.
10378      * @return {String} value The selected value
10379      */
10380     getValue : function(){
10381         
10382         if(this.multiple){
10383             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10384         }
10385         
10386         if(this.valueField){
10387             return typeof this.value != 'undefined' ? this.value : '';
10388         }else{
10389             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10390         }
10391     },
10392
10393     /**
10394      * Clears any text/value currently set in the field
10395      */
10396     clearValue : function(){
10397         if(this.hiddenField){
10398             this.hiddenField.dom.value = '';
10399         }
10400         this.value = '';
10401         this.setRawValue('');
10402         this.lastSelectionText = '';
10403         
10404     },
10405
10406     /**
10407      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10408      * will be displayed in the field.  If the value does not match the data value of an existing item,
10409      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10410      * Otherwise the field will be blank (although the value will still be set).
10411      * @param {String} value The value to match
10412      */
10413     setValue : function(v){
10414         if(this.multiple){
10415             this.syncValue();
10416             return;
10417         }
10418         
10419         var text = v;
10420         if(this.valueField){
10421             var r = this.findRecord(this.valueField, v);
10422             if(r){
10423                 text = r.data[this.displayField];
10424             }else if(this.valueNotFoundText !== undefined){
10425                 text = this.valueNotFoundText;
10426             }
10427         }
10428         this.lastSelectionText = text;
10429         if(this.hiddenField){
10430             this.hiddenField.dom.value = v;
10431         }
10432         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10433         this.value = v;
10434     },
10435     /**
10436      * @property {Object} the last set data for the element
10437      */
10438     
10439     lastData : false,
10440     /**
10441      * Sets the value of the field based on a object which is related to the record format for the store.
10442      * @param {Object} value the value to set as. or false on reset?
10443      */
10444     setFromData : function(o){
10445         
10446         if(this.multiple){
10447             this.addItem(o);
10448             return;
10449         }
10450             
10451         var dv = ''; // display value
10452         var vv = ''; // value value..
10453         this.lastData = o;
10454         if (this.displayField) {
10455             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10456         } else {
10457             // this is an error condition!!!
10458             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10459         }
10460         
10461         if(this.valueField){
10462             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10463         }
10464         
10465         if(this.hiddenField){
10466             this.hiddenField.dom.value = vv;
10467             
10468             this.lastSelectionText = dv;
10469             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10470             this.value = vv;
10471             return;
10472         }
10473         // no hidden field.. - we store the value in 'value', but still display
10474         // display field!!!!
10475         this.lastSelectionText = dv;
10476         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10477         this.value = vv;
10478         
10479         
10480     },
10481     // private
10482     reset : function(){
10483         // overridden so that last data is reset..
10484         this.setValue(this.originalValue);
10485         this.clearInvalid();
10486         this.lastData = false;
10487         if (this.view) {
10488             this.view.clearSelections();
10489         }
10490     },
10491     // private
10492     findRecord : function(prop, value){
10493         var record;
10494         if(this.store.getCount() > 0){
10495             this.store.each(function(r){
10496                 if(r.data[prop] == value){
10497                     record = r;
10498                     return false;
10499                 }
10500                 return true;
10501             });
10502         }
10503         return record;
10504     },
10505     
10506     getName: function()
10507     {
10508         // returns hidden if it's set..
10509         if (!this.rendered) {return ''};
10510         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
10511         
10512     },
10513     // private
10514     onViewMove : function(e, t){
10515         this.inKeyMode = false;
10516     },
10517
10518     // private
10519     onViewOver : function(e, t){
10520         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
10521             return;
10522         }
10523         var item = this.view.findItemFromChild(t);
10524         if(item){
10525             var index = this.view.indexOf(item);
10526             this.select(index, false);
10527         }
10528     },
10529
10530     // private
10531     onViewClick : function(doFocus)
10532     {
10533         var index = this.view.getSelectedIndexes()[0];
10534         var r = this.store.getAt(index);
10535         if(r){
10536             this.onSelect(r, index);
10537         }
10538         if(doFocus !== false && !this.blockFocus){
10539             this.inputEl().focus();
10540         }
10541     },
10542
10543     // private
10544     restrictHeight : function(){
10545         //this.innerList.dom.style.height = '';
10546         //var inner = this.innerList.dom;
10547         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
10548         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
10549         //this.list.beginUpdate();
10550         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
10551         this.list.alignTo(this.inputEl(), this.listAlign);
10552         //this.list.endUpdate();
10553     },
10554
10555     // private
10556     onEmptyResults : function(){
10557         this.collapse();
10558     },
10559
10560     /**
10561      * Returns true if the dropdown list is expanded, else false.
10562      */
10563     isExpanded : function(){
10564         return this.list.isVisible();
10565     },
10566
10567     /**
10568      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
10569      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10570      * @param {String} value The data value of the item to select
10571      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10572      * selected item if it is not currently in view (defaults to true)
10573      * @return {Boolean} True if the value matched an item in the list, else false
10574      */
10575     selectByValue : function(v, scrollIntoView){
10576         if(v !== undefined && v !== null){
10577             var r = this.findRecord(this.valueField || this.displayField, v);
10578             if(r){
10579                 this.select(this.store.indexOf(r), scrollIntoView);
10580                 return true;
10581             }
10582         }
10583         return false;
10584     },
10585
10586     /**
10587      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
10588      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
10589      * @param {Number} index The zero-based index of the list item to select
10590      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
10591      * selected item if it is not currently in view (defaults to true)
10592      */
10593     select : function(index, scrollIntoView){
10594         this.selectedIndex = index;
10595         this.view.select(index);
10596         if(scrollIntoView !== false){
10597             var el = this.view.getNode(index);
10598             if(el){
10599                 //this.innerList.scrollChildIntoView(el, false);
10600                 
10601             }
10602         }
10603     },
10604
10605     // private
10606     selectNext : function(){
10607         var ct = this.store.getCount();
10608         if(ct > 0){
10609             if(this.selectedIndex == -1){
10610                 this.select(0);
10611             }else if(this.selectedIndex < ct-1){
10612                 this.select(this.selectedIndex+1);
10613             }
10614         }
10615     },
10616
10617     // private
10618     selectPrev : function(){
10619         var ct = this.store.getCount();
10620         if(ct > 0){
10621             if(this.selectedIndex == -1){
10622                 this.select(0);
10623             }else if(this.selectedIndex != 0){
10624                 this.select(this.selectedIndex-1);
10625             }
10626         }
10627     },
10628
10629     // private
10630     onKeyUp : function(e){
10631         if(this.editable !== false && !e.isSpecialKey()){
10632             this.lastKey = e.getKey();
10633             this.dqTask.delay(this.queryDelay);
10634         }
10635     },
10636
10637     // private
10638     validateBlur : function(){
10639         return !this.list || !this.list.isVisible();   
10640     },
10641
10642     // private
10643     initQuery : function(){
10644         this.doQuery(this.getRawValue());
10645     },
10646
10647     // private
10648     doForce : function(){
10649         if(this.inputEl().dom.value.length > 0){
10650             this.inputEl().dom.value =
10651                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
10652              
10653         }
10654     },
10655
10656     /**
10657      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
10658      * query allowing the query action to be canceled if needed.
10659      * @param {String} query The SQL query to execute
10660      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
10661      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
10662      * saved in the current store (defaults to false)
10663      */
10664     doQuery : function(q, forceAll){
10665         
10666         if(q === undefined || q === null){
10667             q = '';
10668         }
10669         var qe = {
10670             query: q,
10671             forceAll: forceAll,
10672             combo: this,
10673             cancel:false
10674         };
10675         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10676             return false;
10677         }
10678         q = qe.query;
10679         
10680         forceAll = qe.forceAll;
10681         if(forceAll === true || (q.length >= this.minChars)){
10682             
10683             this.hasQuery = true;
10684             
10685             if(this.lastQuery != q || this.alwaysQuery){
10686                 this.lastQuery = q;
10687                 if(this.mode == 'local'){
10688                     this.selectedIndex = -1;
10689                     if(forceAll){
10690                         this.store.clearFilter();
10691                     }else{
10692                         this.store.filter(this.displayField, q);
10693                     }
10694                     this.onLoad();
10695                 }else{
10696                     this.store.baseParams[this.queryParam] = q;
10697                     
10698                     var options = {params : this.getParams(q)};
10699                     
10700                     if(this.loadNext){
10701                         options.add = true;
10702                         options.params.start = this.page * this.pageSize;
10703                     }
10704                     
10705                     this.store.load(options);
10706                     /*
10707                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
10708                      *  we should expand the list on onLoad
10709                      *  so command out it
10710                      */
10711 //                    this.expand();
10712                 }
10713             }else{
10714                 this.selectedIndex = -1;
10715                 this.onLoad();   
10716             }
10717         }
10718         
10719         this.loadNext = false;
10720     },
10721
10722     // private
10723     getParams : function(q){
10724         var p = {};
10725         //p[this.queryParam] = q;
10726         
10727         if(this.pageSize){
10728             p.start = 0;
10729             p.limit = this.pageSize;
10730         }
10731         return p;
10732     },
10733
10734     /**
10735      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10736      */
10737     collapse : function(){
10738         if(!this.isExpanded()){
10739             return;
10740         }
10741         
10742         this.list.hide();
10743         Roo.get(document).un('mousedown', this.collapseIf, this);
10744         Roo.get(document).un('mousewheel', this.collapseIf, this);
10745         if (!this.editable) {
10746             Roo.get(document).un('keydown', this.listKeyPress, this);
10747         }
10748         this.fireEvent('collapse', this);
10749     },
10750
10751     // private
10752     collapseIf : function(e){
10753         var in_combo  = e.within(this.el);
10754         var in_list =  e.within(this.list);
10755         
10756         if (in_combo || in_list) {
10757             //e.stopPropagation();
10758             return;
10759         }
10760
10761         this.collapse();
10762         
10763     },
10764
10765     /**
10766      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10767      */
10768     expand : function(){
10769        
10770         if(this.isExpanded() || !this.hasFocus){
10771             return;
10772         }
10773          Roo.log('expand');
10774         this.list.alignTo(this.inputEl(), this.listAlign);
10775         this.list.show();
10776         Roo.get(document).on('mousedown', this.collapseIf, this);
10777         Roo.get(document).on('mousewheel', this.collapseIf, this);
10778         if (!this.editable) {
10779             Roo.get(document).on('keydown', this.listKeyPress, this);
10780         }
10781         
10782         this.fireEvent('expand', this);
10783     },
10784
10785     // private
10786     // Implements the default empty TriggerField.onTriggerClick function
10787     onTriggerClick : function()
10788     {
10789         Roo.log('trigger click');
10790         
10791         if(this.disabled){
10792             return;
10793         }
10794         
10795         this.page = 0;
10796         this.loadNext = false;
10797         
10798         if(this.isExpanded()){
10799             this.collapse();
10800             if (!this.blockFocus) {
10801                 this.inputEl().focus();
10802             }
10803             
10804         }else {
10805             this.hasFocus = true;
10806             if(this.triggerAction == 'all') {
10807                 this.doQuery(this.allQuery, true);
10808             } else {
10809                 this.doQuery(this.getRawValue());
10810             }
10811             if (!this.blockFocus) {
10812                 this.inputEl().focus();
10813             }
10814         }
10815     },
10816     listKeyPress : function(e)
10817     {
10818         //Roo.log('listkeypress');
10819         // scroll to first matching element based on key pres..
10820         if (e.isSpecialKey()) {
10821             return false;
10822         }
10823         var k = String.fromCharCode(e.getKey()).toUpperCase();
10824         //Roo.log(k);
10825         var match  = false;
10826         var csel = this.view.getSelectedNodes();
10827         var cselitem = false;
10828         if (csel.length) {
10829             var ix = this.view.indexOf(csel[0]);
10830             cselitem  = this.store.getAt(ix);
10831             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10832                 cselitem = false;
10833             }
10834             
10835         }
10836         
10837         this.store.each(function(v) { 
10838             if (cselitem) {
10839                 // start at existing selection.
10840                 if (cselitem.id == v.id) {
10841                     cselitem = false;
10842                 }
10843                 return true;
10844             }
10845                 
10846             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10847                 match = this.store.indexOf(v);
10848                 return false;
10849             }
10850             return true;
10851         }, this);
10852         
10853         if (match === false) {
10854             return true; // no more action?
10855         }
10856         // scroll to?
10857         this.view.select(match);
10858         var sn = Roo.get(this.view.getSelectedNodes()[0])
10859         //sn.scrollIntoView(sn.dom.parentNode, false);
10860     },
10861     
10862     onViewScroll : function(e, t){
10863         
10864         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10865             return;
10866         }
10867         
10868         this.hasQuery = true;
10869         
10870         this.loading = this.list.select('.loading', true).first();
10871         
10872         if(this.loading === null){
10873             this.list.createChild({
10874                 tag: 'div',
10875                 cls: 'loading select2-more-results select2-active',
10876                 html: 'Loading more results...'
10877             })
10878             
10879             this.loading = this.list.select('.loading', true).first();
10880             
10881             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10882             
10883             this.loading.hide();
10884         }
10885         
10886         this.loading.show();
10887         
10888         var _combo = this;
10889         
10890         this.page++;
10891         this.loadNext = true;
10892         
10893         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10894         
10895         return;
10896     },
10897     
10898     addItem : function(o)
10899     {   
10900         var dv = ''; // display value
10901         
10902         if (this.displayField) {
10903             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10904         } else {
10905             // this is an error condition!!!
10906             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10907         }
10908         
10909         if(!dv.length){
10910             return;
10911         }
10912         
10913         var choice = this.choices.createChild({
10914             tag: 'li',
10915             cls: 'select2-search-choice',
10916             cn: [
10917                 {
10918                     tag: 'div',
10919                     html: dv
10920                 },
10921                 {
10922                     tag: 'a',
10923                     href: '#',
10924                     cls: 'select2-search-choice-close',
10925                     tabindex: '-1'
10926                 }
10927             ]
10928             
10929         }, this.searchField);
10930         
10931         var close = choice.select('a.select2-search-choice-close', true).first()
10932         
10933         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10934         
10935         this.item.push(o);
10936         this.lastData = o;
10937         
10938         this.syncValue();
10939         
10940         this.inputEl().dom.value = '';
10941         
10942     },
10943     
10944     onRemoveItem : function(e, _self, o)
10945     {
10946         e.preventDefault();
10947         var index = this.item.indexOf(o.data) * 1;
10948         
10949         if( index < 0){
10950             Roo.log('not this item?!');
10951             return;
10952         }
10953         
10954         this.item.splice(index, 1);
10955         o.item.remove();
10956         
10957         this.syncValue();
10958         
10959         this.fireEvent('remove', this, e);
10960         
10961     },
10962     
10963     syncValue : function()
10964     {
10965         if(!this.item.length){
10966             this.clearValue();
10967             return;
10968         }
10969             
10970         var value = [];
10971         var _this = this;
10972         Roo.each(this.item, function(i){
10973             if(_this.valueField){
10974                 value.push(i[_this.valueField]);
10975                 return;
10976             }
10977
10978             value.push(i);
10979         });
10980
10981         this.value = value.join(',');
10982
10983         if(this.hiddenField){
10984             this.hiddenField.dom.value = this.value;
10985         }
10986     },
10987     
10988     clearItem : function()
10989     {
10990         if(!this.multiple){
10991             return;
10992         }
10993         
10994         this.item = [];
10995         
10996         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10997            c.remove();
10998         });
10999         
11000         this.syncValue();
11001     }
11002     
11003     
11004
11005     /** 
11006     * @cfg {Boolean} grow 
11007     * @hide 
11008     */
11009     /** 
11010     * @cfg {Number} growMin 
11011     * @hide 
11012     */
11013     /** 
11014     * @cfg {Number} growMax 
11015     * @hide 
11016     */
11017     /**
11018      * @hide
11019      * @method autoSize
11020      */
11021 });
11022 /*
11023  * Based on:
11024  * Ext JS Library 1.1.1
11025  * Copyright(c) 2006-2007, Ext JS, LLC.
11026  *
11027  * Originally Released Under LGPL - original licence link has changed is not relivant.
11028  *
11029  * Fork - LGPL
11030  * <script type="text/javascript">
11031  */
11032
11033 /**
11034  * @class Roo.View
11035  * @extends Roo.util.Observable
11036  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11037  * This class also supports single and multi selection modes. <br>
11038  * Create a data model bound view:
11039  <pre><code>
11040  var store = new Roo.data.Store(...);
11041
11042  var view = new Roo.View({
11043     el : "my-element",
11044     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11045  
11046     singleSelect: true,
11047     selectedClass: "ydataview-selected",
11048     store: store
11049  });
11050
11051  // listen for node click?
11052  view.on("click", function(vw, index, node, e){
11053  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11054  });
11055
11056  // load XML data
11057  dataModel.load("foobar.xml");
11058  </code></pre>
11059  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11060  * <br><br>
11061  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11062  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11063  * 
11064  * Note: old style constructor is still suported (container, template, config)
11065  * 
11066  * @constructor
11067  * Create a new View
11068  * @param {Object} config The config object
11069  * 
11070  */
11071 Roo.View = function(config, depreciated_tpl, depreciated_config){
11072     
11073     if (typeof(depreciated_tpl) == 'undefined') {
11074         // new way.. - universal constructor.
11075         Roo.apply(this, config);
11076         this.el  = Roo.get(this.el);
11077     } else {
11078         // old format..
11079         this.el  = Roo.get(config);
11080         this.tpl = depreciated_tpl;
11081         Roo.apply(this, depreciated_config);
11082     }
11083     this.wrapEl  = this.el.wrap().wrap();
11084     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11085     
11086     
11087     if(typeof(this.tpl) == "string"){
11088         this.tpl = new Roo.Template(this.tpl);
11089     } else {
11090         // support xtype ctors..
11091         this.tpl = new Roo.factory(this.tpl, Roo);
11092     }
11093     
11094     
11095     this.tpl.compile();
11096    
11097   
11098     
11099      
11100     /** @private */
11101     this.addEvents({
11102         /**
11103          * @event beforeclick
11104          * Fires before a click is processed. Returns false to cancel the default action.
11105          * @param {Roo.View} this
11106          * @param {Number} index The index of the target node
11107          * @param {HTMLElement} node The target node
11108          * @param {Roo.EventObject} e The raw event object
11109          */
11110             "beforeclick" : true,
11111         /**
11112          * @event click
11113          * Fires when a template node is clicked.
11114          * @param {Roo.View} this
11115          * @param {Number} index The index of the target node
11116          * @param {HTMLElement} node The target node
11117          * @param {Roo.EventObject} e The raw event object
11118          */
11119             "click" : true,
11120         /**
11121          * @event dblclick
11122          * Fires when a template node is double clicked.
11123          * @param {Roo.View} this
11124          * @param {Number} index The index of the target node
11125          * @param {HTMLElement} node The target node
11126          * @param {Roo.EventObject} e The raw event object
11127          */
11128             "dblclick" : true,
11129         /**
11130          * @event contextmenu
11131          * Fires when a template node is right clicked.
11132          * @param {Roo.View} this
11133          * @param {Number} index The index of the target node
11134          * @param {HTMLElement} node The target node
11135          * @param {Roo.EventObject} e The raw event object
11136          */
11137             "contextmenu" : true,
11138         /**
11139          * @event selectionchange
11140          * Fires when the selected nodes change.
11141          * @param {Roo.View} this
11142          * @param {Array} selections Array of the selected nodes
11143          */
11144             "selectionchange" : true,
11145     
11146         /**
11147          * @event beforeselect
11148          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11149          * @param {Roo.View} this
11150          * @param {HTMLElement} node The node to be selected
11151          * @param {Array} selections Array of currently selected nodes
11152          */
11153             "beforeselect" : true,
11154         /**
11155          * @event preparedata
11156          * Fires on every row to render, to allow you to change the data.
11157          * @param {Roo.View} this
11158          * @param {Object} data to be rendered (change this)
11159          */
11160           "preparedata" : true
11161           
11162           
11163         });
11164
11165
11166
11167     this.el.on({
11168         "click": this.onClick,
11169         "dblclick": this.onDblClick,
11170         "contextmenu": this.onContextMenu,
11171         scope:this
11172     });
11173
11174     this.selections = [];
11175     this.nodes = [];
11176     this.cmp = new Roo.CompositeElementLite([]);
11177     if(this.store){
11178         this.store = Roo.factory(this.store, Roo.data);
11179         this.setStore(this.store, true);
11180     }
11181     
11182     if ( this.footer && this.footer.xtype) {
11183            
11184          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11185         
11186         this.footer.dataSource = this.store
11187         this.footer.container = fctr;
11188         this.footer = Roo.factory(this.footer, Roo);
11189         fctr.insertFirst(this.el);
11190         
11191         // this is a bit insane - as the paging toolbar seems to detach the el..
11192 //        dom.parentNode.parentNode.parentNode
11193          // they get detached?
11194     }
11195     
11196     
11197     Roo.View.superclass.constructor.call(this);
11198     
11199     
11200 };
11201
11202 Roo.extend(Roo.View, Roo.util.Observable, {
11203     
11204      /**
11205      * @cfg {Roo.data.Store} store Data store to load data from.
11206      */
11207     store : false,
11208     
11209     /**
11210      * @cfg {String|Roo.Element} el The container element.
11211      */
11212     el : '',
11213     
11214     /**
11215      * @cfg {String|Roo.Template} tpl The template used by this View 
11216      */
11217     tpl : false,
11218     /**
11219      * @cfg {String} dataName the named area of the template to use as the data area
11220      *                          Works with domtemplates roo-name="name"
11221      */
11222     dataName: false,
11223     /**
11224      * @cfg {String} selectedClass The css class to add to selected nodes
11225      */
11226     selectedClass : "x-view-selected",
11227      /**
11228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11229      */
11230     emptyText : "",
11231     
11232     /**
11233      * @cfg {String} text to display on mask (default Loading)
11234      */
11235     mask : false,
11236     /**
11237      * @cfg {Boolean} multiSelect Allow multiple selection
11238      */
11239     multiSelect : false,
11240     /**
11241      * @cfg {Boolean} singleSelect Allow single selection
11242      */
11243     singleSelect:  false,
11244     
11245     /**
11246      * @cfg {Boolean} toggleSelect - selecting 
11247      */
11248     toggleSelect : false,
11249     
11250     /**
11251      * Returns the element this view is bound to.
11252      * @return {Roo.Element}
11253      */
11254     getEl : function(){
11255         return this.wrapEl;
11256     },
11257     
11258     
11259
11260     /**
11261      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11262      */
11263     refresh : function(){
11264         Roo.log('refresh');
11265         var t = this.tpl;
11266         
11267         // if we are using something like 'domtemplate', then
11268         // the what gets used is:
11269         // t.applySubtemplate(NAME, data, wrapping data..)
11270         // the outer template then get' applied with
11271         //     the store 'extra data'
11272         // and the body get's added to the
11273         //      roo-name="data" node?
11274         //      <span class='roo-tpl-{name}'></span> ?????
11275         
11276         
11277         
11278         this.clearSelections();
11279         this.el.update("");
11280         var html = [];
11281         var records = this.store.getRange();
11282         if(records.length < 1) {
11283             
11284             // is this valid??  = should it render a template??
11285             
11286             this.el.update(this.emptyText);
11287             return;
11288         }
11289         var el = this.el;
11290         if (this.dataName) {
11291             this.el.update(t.apply(this.store.meta)); //????
11292             el = this.el.child('.roo-tpl-' + this.dataName);
11293         }
11294         
11295         for(var i = 0, len = records.length; i < len; i++){
11296             var data = this.prepareData(records[i].data, i, records[i]);
11297             this.fireEvent("preparedata", this, data, i, records[i]);
11298             html[html.length] = Roo.util.Format.trim(
11299                 this.dataName ?
11300                     t.applySubtemplate(this.dataName, data, this.store.meta) :
11301                     t.apply(data)
11302             );
11303         }
11304         
11305         
11306         
11307         el.update(html.join(""));
11308         this.nodes = el.dom.childNodes;
11309         this.updateIndexes(0);
11310     },
11311     
11312
11313     /**
11314      * Function to override to reformat the data that is sent to
11315      * the template for each node.
11316      * DEPRICATED - use the preparedata event handler.
11317      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11318      * a JSON object for an UpdateManager bound view).
11319      */
11320     prepareData : function(data, index, record)
11321     {
11322         this.fireEvent("preparedata", this, data, index, record);
11323         return data;
11324     },
11325
11326     onUpdate : function(ds, record){
11327          Roo.log('on update');   
11328         this.clearSelections();
11329         var index = this.store.indexOf(record);
11330         var n = this.nodes[index];
11331         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11332         n.parentNode.removeChild(n);
11333         this.updateIndexes(index, index);
11334     },
11335
11336     
11337     
11338 // --------- FIXME     
11339     onAdd : function(ds, records, index)
11340     {
11341         Roo.log(['on Add', ds, records, index] );        
11342         this.clearSelections();
11343         if(this.nodes.length == 0){
11344             this.refresh();
11345             return;
11346         }
11347         var n = this.nodes[index];
11348         for(var i = 0, len = records.length; i < len; i++){
11349             var d = this.prepareData(records[i].data, i, records[i]);
11350             if(n){
11351                 this.tpl.insertBefore(n, d);
11352             }else{
11353                 
11354                 this.tpl.append(this.el, d);
11355             }
11356         }
11357         this.updateIndexes(index);
11358     },
11359
11360     onRemove : function(ds, record, index){
11361         Roo.log('onRemove');
11362         this.clearSelections();
11363         var el = this.dataName  ?
11364             this.el.child('.roo-tpl-' + this.dataName) :
11365             this.el; 
11366         
11367         el.dom.removeChild(this.nodes[index]);
11368         this.updateIndexes(index);
11369     },
11370
11371     /**
11372      * Refresh an individual node.
11373      * @param {Number} index
11374      */
11375     refreshNode : function(index){
11376         this.onUpdate(this.store, this.store.getAt(index));
11377     },
11378
11379     updateIndexes : function(startIndex, endIndex){
11380         var ns = this.nodes;
11381         startIndex = startIndex || 0;
11382         endIndex = endIndex || ns.length - 1;
11383         for(var i = startIndex; i <= endIndex; i++){
11384             ns[i].nodeIndex = i;
11385         }
11386     },
11387
11388     /**
11389      * Changes the data store this view uses and refresh the view.
11390      * @param {Store} store
11391      */
11392     setStore : function(store, initial){
11393         if(!initial && this.store){
11394             this.store.un("datachanged", this.refresh);
11395             this.store.un("add", this.onAdd);
11396             this.store.un("remove", this.onRemove);
11397             this.store.un("update", this.onUpdate);
11398             this.store.un("clear", this.refresh);
11399             this.store.un("beforeload", this.onBeforeLoad);
11400             this.store.un("load", this.onLoad);
11401             this.store.un("loadexception", this.onLoad);
11402         }
11403         if(store){
11404           
11405             store.on("datachanged", this.refresh, this);
11406             store.on("add", this.onAdd, this);
11407             store.on("remove", this.onRemove, this);
11408             store.on("update", this.onUpdate, this);
11409             store.on("clear", this.refresh, this);
11410             store.on("beforeload", this.onBeforeLoad, this);
11411             store.on("load", this.onLoad, this);
11412             store.on("loadexception", this.onLoad, this);
11413         }
11414         
11415         if(store){
11416             this.refresh();
11417         }
11418     },
11419     /**
11420      * onbeforeLoad - masks the loading area.
11421      *
11422      */
11423     onBeforeLoad : function(store,opts)
11424     {
11425          Roo.log('onBeforeLoad');   
11426         if (!opts.add) {
11427             this.el.update("");
11428         }
11429         this.el.mask(this.mask ? this.mask : "Loading" ); 
11430     },
11431     onLoad : function ()
11432     {
11433         this.el.unmask();
11434     },
11435     
11436
11437     /**
11438      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
11439      * @param {HTMLElement} node
11440      * @return {HTMLElement} The template node
11441      */
11442     findItemFromChild : function(node){
11443         var el = this.dataName  ?
11444             this.el.child('.roo-tpl-' + this.dataName,true) :
11445             this.el.dom; 
11446         
11447         if(!node || node.parentNode == el){
11448                     return node;
11449             }
11450             var p = node.parentNode;
11451             while(p && p != el){
11452             if(p.parentNode == el){
11453                 return p;
11454             }
11455             p = p.parentNode;
11456         }
11457             return null;
11458     },
11459
11460     /** @ignore */
11461     onClick : function(e){
11462         var item = this.findItemFromChild(e.getTarget());
11463         if(item){
11464             var index = this.indexOf(item);
11465             if(this.onItemClick(item, index, e) !== false){
11466                 this.fireEvent("click", this, index, item, e);
11467             }
11468         }else{
11469             this.clearSelections();
11470         }
11471     },
11472
11473     /** @ignore */
11474     onContextMenu : function(e){
11475         var item = this.findItemFromChild(e.getTarget());
11476         if(item){
11477             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
11478         }
11479     },
11480
11481     /** @ignore */
11482     onDblClick : function(e){
11483         var item = this.findItemFromChild(e.getTarget());
11484         if(item){
11485             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
11486         }
11487     },
11488
11489     onItemClick : function(item, index, e)
11490     {
11491         if(this.fireEvent("beforeclick", this, index, item, e) === false){
11492             return false;
11493         }
11494         if (this.toggleSelect) {
11495             var m = this.isSelected(item) ? 'unselect' : 'select';
11496             Roo.log(m);
11497             var _t = this;
11498             _t[m](item, true, false);
11499             return true;
11500         }
11501         if(this.multiSelect || this.singleSelect){
11502             if(this.multiSelect && e.shiftKey && this.lastSelection){
11503                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
11504             }else{
11505                 this.select(item, this.multiSelect && e.ctrlKey);
11506                 this.lastSelection = item;
11507             }
11508             e.preventDefault();
11509         }
11510         return true;
11511     },
11512
11513     /**
11514      * Get the number of selected nodes.
11515      * @return {Number}
11516      */
11517     getSelectionCount : function(){
11518         return this.selections.length;
11519     },
11520
11521     /**
11522      * Get the currently selected nodes.
11523      * @return {Array} An array of HTMLElements
11524      */
11525     getSelectedNodes : function(){
11526         return this.selections;
11527     },
11528
11529     /**
11530      * Get the indexes of the selected nodes.
11531      * @return {Array}
11532      */
11533     getSelectedIndexes : function(){
11534         var indexes = [], s = this.selections;
11535         for(var i = 0, len = s.length; i < len; i++){
11536             indexes.push(s[i].nodeIndex);
11537         }
11538         return indexes;
11539     },
11540
11541     /**
11542      * Clear all selections
11543      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
11544      */
11545     clearSelections : function(suppressEvent){
11546         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
11547             this.cmp.elements = this.selections;
11548             this.cmp.removeClass(this.selectedClass);
11549             this.selections = [];
11550             if(!suppressEvent){
11551                 this.fireEvent("selectionchange", this, this.selections);
11552             }
11553         }
11554     },
11555
11556     /**
11557      * Returns true if the passed node is selected
11558      * @param {HTMLElement/Number} node The node or node index
11559      * @return {Boolean}
11560      */
11561     isSelected : function(node){
11562         var s = this.selections;
11563         if(s.length < 1){
11564             return false;
11565         }
11566         node = this.getNode(node);
11567         return s.indexOf(node) !== -1;
11568     },
11569
11570     /**
11571      * Selects nodes.
11572      * @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
11573      * @param {Boolean} keepExisting (optional) true to keep existing selections
11574      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11575      */
11576     select : function(nodeInfo, keepExisting, suppressEvent){
11577         if(nodeInfo instanceof Array){
11578             if(!keepExisting){
11579                 this.clearSelections(true);
11580             }
11581             for(var i = 0, len = nodeInfo.length; i < len; i++){
11582                 this.select(nodeInfo[i], true, true);
11583             }
11584             return;
11585         } 
11586         var node = this.getNode(nodeInfo);
11587         if(!node || this.isSelected(node)){
11588             return; // already selected.
11589         }
11590         if(!keepExisting){
11591             this.clearSelections(true);
11592         }
11593         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
11594             Roo.fly(node).addClass(this.selectedClass);
11595             this.selections.push(node);
11596             if(!suppressEvent){
11597                 this.fireEvent("selectionchange", this, this.selections);
11598             }
11599         }
11600         
11601         
11602     },
11603       /**
11604      * Unselects nodes.
11605      * @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
11606      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
11607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
11608      */
11609     unselect : function(nodeInfo, keepExisting, suppressEvent)
11610     {
11611         if(nodeInfo instanceof Array){
11612             Roo.each(this.selections, function(s) {
11613                 this.unselect(s, nodeInfo);
11614             }, this);
11615             return;
11616         }
11617         var node = this.getNode(nodeInfo);
11618         if(!node || !this.isSelected(node)){
11619             Roo.log("not selected");
11620             return; // not selected.
11621         }
11622         // fireevent???
11623         var ns = [];
11624         Roo.each(this.selections, function(s) {
11625             if (s == node ) {
11626                 Roo.fly(node).removeClass(this.selectedClass);
11627
11628                 return;
11629             }
11630             ns.push(s);
11631         },this);
11632         
11633         this.selections= ns;
11634         this.fireEvent("selectionchange", this, this.selections);
11635     },
11636
11637     /**
11638      * Gets a template node.
11639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11640      * @return {HTMLElement} The node or null if it wasn't found
11641      */
11642     getNode : function(nodeInfo){
11643         if(typeof nodeInfo == "string"){
11644             return document.getElementById(nodeInfo);
11645         }else if(typeof nodeInfo == "number"){
11646             return this.nodes[nodeInfo];
11647         }
11648         return nodeInfo;
11649     },
11650
11651     /**
11652      * Gets a range template nodes.
11653      * @param {Number} startIndex
11654      * @param {Number} endIndex
11655      * @return {Array} An array of nodes
11656      */
11657     getNodes : function(start, end){
11658         var ns = this.nodes;
11659         start = start || 0;
11660         end = typeof end == "undefined" ? ns.length - 1 : end;
11661         var nodes = [];
11662         if(start <= end){
11663             for(var i = start; i <= end; i++){
11664                 nodes.push(ns[i]);
11665             }
11666         } else{
11667             for(var i = start; i >= end; i--){
11668                 nodes.push(ns[i]);
11669             }
11670         }
11671         return nodes;
11672     },
11673
11674     /**
11675      * Finds the index of the passed node
11676      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
11677      * @return {Number} The index of the node or -1
11678      */
11679     indexOf : function(node){
11680         node = this.getNode(node);
11681         if(typeof node.nodeIndex == "number"){
11682             return node.nodeIndex;
11683         }
11684         var ns = this.nodes;
11685         for(var i = 0, len = ns.length; i < len; i++){
11686             if(ns[i] == node){
11687                 return i;
11688             }
11689         }
11690         return -1;
11691     }
11692 });
11693 /*
11694  * - LGPL
11695  *
11696  * based on jquery fullcalendar
11697  * 
11698  */
11699
11700 Roo.bootstrap = Roo.bootstrap || {};
11701 /**
11702  * @class Roo.bootstrap.Calendar
11703  * @extends Roo.bootstrap.Component
11704  * Bootstrap Calendar class
11705  * @cfg {Boolean} loadMask (true|false) default false
11706  * @cfg {Object} header generate the user specific header of the calendar, default false
11707
11708  * @constructor
11709  * Create a new Container
11710  * @param {Object} config The config object
11711  */
11712
11713
11714
11715 Roo.bootstrap.Calendar = function(config){
11716     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11717      this.addEvents({
11718         /**
11719              * @event select
11720              * Fires when a date is selected
11721              * @param {DatePicker} this
11722              * @param {Date} date The selected date
11723              */
11724         'select': true,
11725         /**
11726              * @event monthchange
11727              * Fires when the displayed month changes 
11728              * @param {DatePicker} this
11729              * @param {Date} date The selected month
11730              */
11731         'monthchange': true,
11732         /**
11733              * @event evententer
11734              * Fires when mouse over an event
11735              * @param {Calendar} this
11736              * @param {event} Event
11737              */
11738         'evententer': true,
11739         /**
11740              * @event eventleave
11741              * Fires when the mouse leaves an
11742              * @param {Calendar} this
11743              * @param {event}
11744              */
11745         'eventleave': true,
11746         /**
11747              * @event eventclick
11748              * Fires when the mouse click an
11749              * @param {Calendar} this
11750              * @param {event}
11751              */
11752         'eventclick': true
11753         
11754     });
11755
11756 };
11757
11758 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
11759     
11760      /**
11761      * @cfg {Number} startDay
11762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11763      */
11764     startDay : 0,
11765     
11766     loadMask : false,
11767     
11768     header : false,
11769       
11770     getAutoCreate : function(){
11771         
11772         
11773         var fc_button = function(name, corner, style, content ) {
11774             return Roo.apply({},{
11775                 tag : 'span',
11776                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
11777                          (corner.length ?
11778                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11779                             ''
11780                         ),
11781                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11782                 unselectable: 'on'
11783             });
11784         };
11785         
11786         var header = {};
11787         
11788         if(!this.header){
11789             header = {
11790                 tag : 'table',
11791                 cls : 'fc-header',
11792                 style : 'width:100%',
11793                 cn : [
11794                     {
11795                         tag: 'tr',
11796                         cn : [
11797                             {
11798                                 tag : 'td',
11799                                 cls : 'fc-header-left',
11800                                 cn : [
11801                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
11802                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
11803                                     { tag: 'span', cls: 'fc-header-space' },
11804                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
11805
11806
11807                                 ]
11808                             },
11809
11810                             {
11811                                 tag : 'td',
11812                                 cls : 'fc-header-center',
11813                                 cn : [
11814                                     {
11815                                         tag: 'span',
11816                                         cls: 'fc-header-title',
11817                                         cn : {
11818                                             tag: 'H2',
11819                                             html : 'month / year'
11820                                         }
11821                                     }
11822
11823                                 ]
11824                             },
11825                             {
11826                                 tag : 'td',
11827                                 cls : 'fc-header-right',
11828                                 cn : [
11829                               /*      fc_button('month', 'left', '', 'month' ),
11830                                     fc_button('week', '', '', 'week' ),
11831                                     fc_button('day', 'right', '', 'day' )
11832                                 */    
11833
11834                                 ]
11835                             }
11836
11837                         ]
11838                     }
11839                 ]
11840             };
11841         }
11842         
11843         header = this.header;
11844         
11845        
11846         var cal_heads = function() {
11847             var ret = [];
11848             // fixme - handle this.
11849             
11850             for (var i =0; i < Date.dayNames.length; i++) {
11851                 var d = Date.dayNames[i];
11852                 ret.push({
11853                     tag: 'th',
11854                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11855                     html : d.substring(0,3)
11856                 });
11857                 
11858             }
11859             ret[0].cls += ' fc-first';
11860             ret[6].cls += ' fc-last';
11861             return ret;
11862         };
11863         var cal_cell = function(n) {
11864             return  {
11865                 tag: 'td',
11866                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11867                 cn : [
11868                     {
11869                         cn : [
11870                             {
11871                                 cls: 'fc-day-number',
11872                                 html: 'D'
11873                             },
11874                             {
11875                                 cls: 'fc-day-content',
11876                              
11877                                 cn : [
11878                                      {
11879                                         style: 'position: relative;' // height: 17px;
11880                                     }
11881                                 ]
11882                             }
11883                             
11884                             
11885                         ]
11886                     }
11887                 ]
11888                 
11889             }
11890         };
11891         var cal_rows = function() {
11892             
11893             var ret = []
11894             for (var r = 0; r < 6; r++) {
11895                 var row= {
11896                     tag : 'tr',
11897                     cls : 'fc-week',
11898                     cn : []
11899                 };
11900                 
11901                 for (var i =0; i < Date.dayNames.length; i++) {
11902                     var d = Date.dayNames[i];
11903                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11904
11905                 }
11906                 row.cn[0].cls+=' fc-first';
11907                 row.cn[0].cn[0].style = 'min-height:90px';
11908                 row.cn[6].cls+=' fc-last';
11909                 ret.push(row);
11910                 
11911             }
11912             ret[0].cls += ' fc-first';
11913             ret[4].cls += ' fc-prev-last';
11914             ret[5].cls += ' fc-last';
11915             return ret;
11916             
11917         };
11918         
11919         var cal_table = {
11920             tag: 'table',
11921             cls: 'fc-border-separate',
11922             style : 'width:100%',
11923             cellspacing  : 0,
11924             cn : [
11925                 { 
11926                     tag: 'thead',
11927                     cn : [
11928                         { 
11929                             tag: 'tr',
11930                             cls : 'fc-first fc-last',
11931                             cn : cal_heads()
11932                         }
11933                     ]
11934                 },
11935                 { 
11936                     tag: 'tbody',
11937                     cn : cal_rows()
11938                 }
11939                   
11940             ]
11941         };
11942          
11943          var cfg = {
11944             cls : 'fc fc-ltr',
11945             cn : [
11946                 header,
11947                 {
11948                     cls : 'fc-content',
11949                     style : "position: relative;",
11950                     cn : [
11951                         {
11952                             cls : 'fc-view fc-view-month fc-grid',
11953                             style : 'position: relative',
11954                             unselectable : 'on',
11955                             cn : [
11956                                 {
11957                                     cls : 'fc-event-container',
11958                                     style : 'position:absolute;z-index:8;top:0;left:0;'
11959                                 },
11960                                 cal_table
11961                             ]
11962                         }
11963                     ]
11964     
11965                 }
11966            ] 
11967             
11968         };
11969         
11970          
11971         
11972         return cfg;
11973     },
11974     
11975     
11976     initEvents : function()
11977     {
11978         if(!this.store){
11979             throw "can not find store for calendar";
11980         }
11981         
11982         var mark = {
11983             tag: "div",
11984             cls:"x-dlg-mask",
11985             style: "text-align:center",
11986             cn: [
11987                 {
11988                     tag: "div",
11989                     style: "background-color:white;width:50%;margin:250 auto",
11990                     cn: [
11991                         {
11992                             tag: "img",
11993                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
11994                         },
11995                         {
11996                             tag: "span",
11997                             html: "Loading"
11998                         }
11999                         
12000                     ]
12001                 }
12002             ]
12003         }
12004         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12005         
12006         var size = this.el.select('.fc-content', true).first().getSize();
12007         this.maskEl.setSize(size.width, size.height);
12008         this.maskEl.enableDisplayMode("block");
12009         if(!this.loadMask){
12010             this.maskEl.hide();
12011         }
12012         
12013         this.store = Roo.factory(this.store, Roo.data);
12014         this.store.on('load', this.onLoad, this);
12015         this.store.on('beforeload', this.onBeforeLoad, this);
12016         
12017         this.resize();
12018         
12019         this.cells = this.el.select('.fc-day',true);
12020         //Roo.log(this.cells);
12021         this.textNodes = this.el.query('.fc-day-number');
12022         this.cells.addClassOnOver('fc-state-hover');
12023         
12024         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12025         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12026         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12027         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12028         
12029         this.on('monthchange', this.onMonthChange, this);
12030         
12031         this.update(new Date().clearTime());
12032     },
12033     
12034     resize : function() {
12035         var sz  = this.el.getSize();
12036         
12037         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12038         this.el.select('.fc-day-content div',true).setHeight(34);
12039     },
12040     
12041     
12042     // private
12043     showPrevMonth : function(e){
12044         this.update(this.activeDate.add("mo", -1));
12045     },
12046     showToday : function(e){
12047         this.update(new Date().clearTime());
12048     },
12049     // private
12050     showNextMonth : function(e){
12051         this.update(this.activeDate.add("mo", 1));
12052     },
12053
12054     // private
12055     showPrevYear : function(){
12056         this.update(this.activeDate.add("y", -1));
12057     },
12058
12059     // private
12060     showNextYear : function(){
12061         this.update(this.activeDate.add("y", 1));
12062     },
12063
12064     
12065    // private
12066     update : function(date)
12067     {
12068         var vd = this.activeDate;
12069         this.activeDate = date;
12070 //        if(vd && this.el){
12071 //            var t = date.getTime();
12072 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12073 //                Roo.log('using add remove');
12074 //                
12075 //                this.fireEvent('monthchange', this, date);
12076 //                
12077 //                this.cells.removeClass("fc-state-highlight");
12078 //                this.cells.each(function(c){
12079 //                   if(c.dateValue == t){
12080 //                       c.addClass("fc-state-highlight");
12081 //                       setTimeout(function(){
12082 //                            try{c.dom.firstChild.focus();}catch(e){}
12083 //                       }, 50);
12084 //                       return false;
12085 //                   }
12086 //                   return true;
12087 //                });
12088 //                return;
12089 //            }
12090 //        }
12091         
12092         var days = date.getDaysInMonth();
12093         
12094         var firstOfMonth = date.getFirstDateOfMonth();
12095         var startingPos = firstOfMonth.getDay()-this.startDay;
12096         
12097         if(startingPos < this.startDay){
12098             startingPos += 7;
12099         }
12100         
12101         var pm = date.add(Date.MONTH, -1);
12102         var prevStart = pm.getDaysInMonth()-startingPos;
12103 //        
12104         this.cells = this.el.select('.fc-day',true);
12105         this.textNodes = this.el.query('.fc-day-number');
12106         this.cells.addClassOnOver('fc-state-hover');
12107         
12108         var cells = this.cells.elements;
12109         var textEls = this.textNodes;
12110         
12111         Roo.each(cells, function(cell){
12112             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12113         });
12114         
12115         days += startingPos;
12116
12117         // convert everything to numbers so it's fast
12118         var day = 86400000;
12119         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12120         //Roo.log(d);
12121         //Roo.log(pm);
12122         //Roo.log(prevStart);
12123         
12124         var today = new Date().clearTime().getTime();
12125         var sel = date.clearTime().getTime();
12126         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12127         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12128         var ddMatch = this.disabledDatesRE;
12129         var ddText = this.disabledDatesText;
12130         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12131         var ddaysText = this.disabledDaysText;
12132         var format = this.format;
12133         
12134         var setCellClass = function(cal, cell){
12135             cell.row = 0;
12136             cell.events = [];
12137             cell.more = [];
12138             //Roo.log('set Cell Class');
12139             cell.title = "";
12140             var t = d.getTime();
12141             
12142             //Roo.log(d);
12143             
12144             cell.dateValue = t;
12145             if(t == today){
12146                 cell.className += " fc-today";
12147                 cell.className += " fc-state-highlight";
12148                 cell.title = cal.todayText;
12149             }
12150             if(t == sel){
12151                 // disable highlight in other month..
12152                 //cell.className += " fc-state-highlight";
12153                 
12154             }
12155             // disabling
12156             if(t < min) {
12157                 cell.className = " fc-state-disabled";
12158                 cell.title = cal.minText;
12159                 return;
12160             }
12161             if(t > max) {
12162                 cell.className = " fc-state-disabled";
12163                 cell.title = cal.maxText;
12164                 return;
12165             }
12166             if(ddays){
12167                 if(ddays.indexOf(d.getDay()) != -1){
12168                     cell.title = ddaysText;
12169                     cell.className = " fc-state-disabled";
12170                 }
12171             }
12172             if(ddMatch && format){
12173                 var fvalue = d.dateFormat(format);
12174                 if(ddMatch.test(fvalue)){
12175                     cell.title = ddText.replace("%0", fvalue);
12176                     cell.className = " fc-state-disabled";
12177                 }
12178             }
12179             
12180             if (!cell.initialClassName) {
12181                 cell.initialClassName = cell.dom.className;
12182             }
12183             
12184             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12185         };
12186
12187         var i = 0;
12188         
12189         for(; i < startingPos; i++) {
12190             textEls[i].innerHTML = (++prevStart);
12191             d.setDate(d.getDate()+1);
12192             
12193             cells[i].className = "fc-past fc-other-month";
12194             setCellClass(this, cells[i]);
12195         }
12196         
12197         var intDay = 0;
12198         
12199         for(; i < days; i++){
12200             intDay = i - startingPos + 1;
12201             textEls[i].innerHTML = (intDay);
12202             d.setDate(d.getDate()+1);
12203             
12204             cells[i].className = ''; // "x-date-active";
12205             setCellClass(this, cells[i]);
12206         }
12207         var extraDays = 0;
12208         
12209         for(; i < 42; i++) {
12210             textEls[i].innerHTML = (++extraDays);
12211             d.setDate(d.getDate()+1);
12212             
12213             cells[i].className = "fc-future fc-other-month";
12214             setCellClass(this, cells[i]);
12215         }
12216         
12217         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12218         
12219         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12220         
12221         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12222         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12223         
12224         if(totalRows != 6){
12225             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12226             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12227         }
12228         
12229         this.fireEvent('monthchange', this, date);
12230         
12231         
12232         /*
12233         if(!this.internalRender){
12234             var main = this.el.dom.firstChild;
12235             var w = main.offsetWidth;
12236             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12237             Roo.fly(main).setWidth(w);
12238             this.internalRender = true;
12239             // opera does not respect the auto grow header center column
12240             // then, after it gets a width opera refuses to recalculate
12241             // without a second pass
12242             if(Roo.isOpera && !this.secondPass){
12243                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12244                 this.secondPass = true;
12245                 this.update.defer(10, this, [date]);
12246             }
12247         }
12248         */
12249         
12250     },
12251     
12252     findCell : function(dt) {
12253         dt = dt.clearTime().getTime();
12254         var ret = false;
12255         this.cells.each(function(c){
12256             //Roo.log("check " +c.dateValue + '?=' + dt);
12257             if(c.dateValue == dt){
12258                 ret = c;
12259                 return false;
12260             }
12261             return true;
12262         });
12263         
12264         return ret;
12265     },
12266     
12267     findCells : function(ev) {
12268         var s = ev.start.clone().clearTime().getTime();
12269        // Roo.log(s);
12270         var e= ev.end.clone().clearTime().getTime();
12271        // Roo.log(e);
12272         var ret = [];
12273         this.cells.each(function(c){
12274              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12275             
12276             if(c.dateValue > e){
12277                 return ;
12278             }
12279             if(c.dateValue < s){
12280                 return ;
12281             }
12282             ret.push(c);
12283         });
12284         
12285         return ret;    
12286     },
12287     
12288 //    findBestRow: function(cells)
12289 //    {
12290 //        var ret = 0;
12291 //        
12292 //        for (var i =0 ; i < cells.length;i++) {
12293 //            ret  = Math.max(cells[i].rows || 0,ret);
12294 //        }
12295 //        return ret;
12296 //        
12297 //    },
12298     
12299     
12300     addItem : function(ev)
12301     {
12302         // look for vertical location slot in
12303         var cells = this.findCells(ev);
12304         
12305 //        ev.row = this.findBestRow(cells);
12306         
12307         // work out the location.
12308         
12309         var crow = false;
12310         var rows = [];
12311         for(var i =0; i < cells.length; i++) {
12312             
12313             cells[i].row = cells[0].row;
12314             
12315             if(i == 0){
12316                 cells[i].row = cells[i].row + 1;
12317             }
12318             
12319             if (!crow) {
12320                 crow = {
12321                     start : cells[i],
12322                     end :  cells[i]
12323                 };
12324                 continue;
12325             }
12326             if (crow.start.getY() == cells[i].getY()) {
12327                 // on same row.
12328                 crow.end = cells[i];
12329                 continue;
12330             }
12331             // different row.
12332             rows.push(crow);
12333             crow = {
12334                 start: cells[i],
12335                 end : cells[i]
12336             };
12337             
12338         }
12339         
12340         rows.push(crow);
12341         ev.els = [];
12342         ev.rows = rows;
12343         ev.cells = cells;
12344         
12345         cells[0].events.push(ev);
12346         
12347         this.calevents.push(ev);
12348     },
12349     
12350     clearEvents: function() {
12351         
12352         if(!this.calevents){
12353             return;
12354         }
12355         
12356         Roo.each(this.cells.elements, function(c){
12357             c.row = 0;
12358             c.events = [];
12359             c.more = [];
12360         });
12361         
12362         Roo.each(this.calevents, function(e) {
12363             Roo.each(e.els, function(el) {
12364                 el.un('mouseenter' ,this.onEventEnter, this);
12365                 el.un('mouseleave' ,this.onEventLeave, this);
12366                 el.remove();
12367             },this);
12368         },this);
12369         
12370         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
12371             e.remove();
12372         });
12373         
12374     },
12375     
12376     renderEvents: function()
12377     {   
12378         var _this = this;
12379         
12380         this.cells.each(function(c) {
12381             
12382             if(c.row < 5){
12383                 return;
12384             }
12385             
12386             var ev = c.events;
12387             
12388             var r = 4;
12389             if(c.row != c.events.length){
12390                 r = 4 - (4 - (c.row - c.events.length));
12391             }
12392             
12393             c.events = ev.slice(0, r);
12394             c.more = ev.slice(r);
12395             
12396             if(c.more.length && c.more.length == 1){
12397                 c.events.push(c.more.pop());
12398             }
12399             
12400             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
12401             
12402         });
12403             
12404         this.cells.each(function(c) {
12405             
12406             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
12407             
12408             
12409             for (var e = 0; e < c.events.length; e++){
12410                 var ev = c.events[e];
12411                 var rows = ev.rows;
12412                 
12413                 for(var i = 0; i < rows.length; i++) {
12414                 
12415                     // how many rows should it span..
12416
12417                     var  cfg = {
12418                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
12419                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
12420
12421                         unselectable : "on",
12422                         cn : [
12423                             {
12424                                 cls: 'fc-event-inner',
12425                                 cn : [
12426     //                                {
12427     //                                  tag:'span',
12428     //                                  cls: 'fc-event-time',
12429     //                                  html : cells.length > 1 ? '' : ev.time
12430     //                                },
12431                                     {
12432                                       tag:'span',
12433                                       cls: 'fc-event-title',
12434                                       html : String.format('{0}', ev.title)
12435                                     }
12436
12437
12438                                 ]
12439                             },
12440                             {
12441                                 cls: 'ui-resizable-handle ui-resizable-e',
12442                                 html : '&nbsp;&nbsp;&nbsp'
12443                             }
12444
12445                         ]
12446                     };
12447
12448                     if (i == 0) {
12449                         cfg.cls += ' fc-event-start';
12450                     }
12451                     if ((i+1) == rows.length) {
12452                         cfg.cls += ' fc-event-end';
12453                     }
12454
12455                     var ctr = _this.el.select('.fc-event-container',true).first();
12456                     var cg = ctr.createChild(cfg);
12457
12458                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
12459                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
12460
12461                     var r = (c.more.length) ? 1 : 0;
12462                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
12463                     cg.setWidth(ebox.right - sbox.x -2);
12464
12465                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
12466                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
12467                     cg.on('click', _this.onEventClick, _this, ev);
12468
12469                     ev.els.push(cg);
12470                     
12471                 }
12472                 
12473             }
12474             
12475             
12476             if(c.more.length){
12477                 var  cfg = {
12478                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
12479                     style : 'position: absolute',
12480                     unselectable : "on",
12481                     cn : [
12482                         {
12483                             cls: 'fc-event-inner',
12484                             cn : [
12485                                 {
12486                                   tag:'span',
12487                                   cls: 'fc-event-title',
12488                                   html : 'More'
12489                                 }
12490
12491
12492                             ]
12493                         },
12494                         {
12495                             cls: 'ui-resizable-handle ui-resizable-e',
12496                             html : '&nbsp;&nbsp;&nbsp'
12497                         }
12498
12499                     ]
12500                 };
12501
12502                 var ctr = _this.el.select('.fc-event-container',true).first();
12503                 var cg = ctr.createChild(cfg);
12504
12505                 var sbox = c.select('.fc-day-content',true).first().getBox();
12506                 var ebox = c.select('.fc-day-content',true).first().getBox();
12507                 //Roo.log(cg);
12508                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
12509                 cg.setWidth(ebox.right - sbox.x -2);
12510
12511                 cg.on('click', _this.onMoreEventClick, _this, c.more);
12512                 
12513             }
12514             
12515         });
12516         
12517         
12518         
12519     },
12520     
12521     onEventEnter: function (e, el,event,d) {
12522         this.fireEvent('evententer', this, el, event);
12523     },
12524     
12525     onEventLeave: function (e, el,event,d) {
12526         this.fireEvent('eventleave', this, el, event);
12527     },
12528     
12529     onEventClick: function (e, el,event,d) {
12530         this.fireEvent('eventclick', this, el, event);
12531     },
12532     
12533     onMonthChange: function () {
12534         this.store.load();
12535     },
12536     
12537     onMoreEventClick: function(e, el, more)
12538     {
12539         var _this = this;
12540         
12541         this.calpopover.placement = 'right';
12542         this.calpopover.setTitle('More');
12543         
12544         this.calpopover.setContent('');
12545         
12546         var ctr = this.calpopover.el.select('.popover-content', true).first();
12547         
12548         Roo.each(more, function(m){
12549             var cfg = {
12550                 cls : 'fc-event-hori fc-event-draggable',
12551                 html : m.title
12552             }
12553             var cg = ctr.createChild(cfg);
12554             
12555             cg.on('click', _this.onEventClick, _this, m);
12556         });
12557         
12558         this.calpopover.show(el);
12559         
12560         
12561     },
12562     
12563     onLoad: function () 
12564     {   
12565         this.calevents = [];
12566         var cal = this;
12567         
12568         if(this.store.getCount() > 0){
12569             this.store.data.each(function(d){
12570                cal.addItem({
12571                     id : d.data.id,
12572                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
12573                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
12574                     time : d.data.start_time,
12575                     title : d.data.title,
12576                     description : d.data.description,
12577                     venue : d.data.venue
12578                 });
12579             });
12580         }
12581         
12582         this.renderEvents();
12583         
12584         if(this.calevents.length && this.loadMask){
12585             this.maskEl.hide();
12586         }
12587     },
12588     
12589     onBeforeLoad: function()
12590     {
12591         this.clearEvents();
12592         if(this.loadMask){
12593             this.maskEl.show();
12594         }
12595     }
12596 });
12597
12598  
12599  /*
12600  * - LGPL
12601  *
12602  * element
12603  * 
12604  */
12605
12606 /**
12607  * @class Roo.bootstrap.Popover
12608  * @extends Roo.bootstrap.Component
12609  * Bootstrap Popover class
12610  * @cfg {String} html contents of the popover   (or false to use children..)
12611  * @cfg {String} title of popover (or false to hide)
12612  * @cfg {String} placement how it is placed
12613  * @cfg {String} trigger click || hover (or false to trigger manually)
12614  * @cfg {String} over what (parent or false to trigger manually.)
12615  * 
12616  * @constructor
12617  * Create a new Popover
12618  * @param {Object} config The config object
12619  */
12620
12621 Roo.bootstrap.Popover = function(config){
12622     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
12623 };
12624
12625 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
12626     
12627     title: 'Fill in a title',
12628     html: false,
12629     
12630     placement : 'right',
12631     trigger : 'hover', // hover
12632     
12633     over: 'parent',
12634     
12635     can_build_overlaid : false,
12636     
12637     getChildContainer : function()
12638     {
12639         return this.el.select('.popover-content',true).first();
12640     },
12641     
12642     getAutoCreate : function(){
12643          Roo.log('make popover?');
12644         var cfg = {
12645            cls : 'popover roo-dynamic',
12646            style: 'display:block',
12647            cn : [
12648                 {
12649                     cls : 'arrow'
12650                 },
12651                 {
12652                     cls : 'popover-inner',
12653                     cn : [
12654                         {
12655                             tag: 'h3',
12656                             cls: 'popover-title',
12657                             html : this.title
12658                         },
12659                         {
12660                             cls : 'popover-content',
12661                             html : this.html
12662                         }
12663                     ]
12664                     
12665                 }
12666            ]
12667         };
12668         
12669         return cfg;
12670     },
12671     setTitle: function(str)
12672     {
12673         this.el.select('.popover-title',true).first().dom.innerHTML = str;
12674     },
12675     setContent: function(str)
12676     {
12677         this.el.select('.popover-content',true).first().dom.innerHTML = str;
12678     },
12679     // as it get's added to the bottom of the page.
12680     onRender : function(ct, position)
12681     {
12682         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12683         if(!this.el){
12684             var cfg = Roo.apply({},  this.getAutoCreate());
12685             cfg.id = Roo.id();
12686             
12687             if (this.cls) {
12688                 cfg.cls += ' ' + this.cls;
12689             }
12690             if (this.style) {
12691                 cfg.style = this.style;
12692             }
12693             Roo.log("adding to ")
12694             this.el = Roo.get(document.body).createChild(cfg, position);
12695             Roo.log(this.el);
12696         }
12697         this.initEvents();
12698     },
12699     
12700     initEvents : function()
12701     {
12702         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12703         this.el.enableDisplayMode('block');
12704         this.el.hide();
12705         if (this.over === false) {
12706             return; 
12707         }
12708         if (this.triggers === false) {
12709             return;
12710         }
12711         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12712         var triggers = this.trigger ? this.trigger.split(' ') : [];
12713         Roo.each(triggers, function(trigger) {
12714         
12715             if (trigger == 'click') {
12716                 on_el.on('click', this.toggle, this);
12717             } else if (trigger != 'manual') {
12718                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
12719                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12720       
12721                 on_el.on(eventIn  ,this.enter, this);
12722                 on_el.on(eventOut, this.leave, this);
12723             }
12724         }, this);
12725         
12726     },
12727     
12728     
12729     // private
12730     timeout : null,
12731     hoverState : null,
12732     
12733     toggle : function () {
12734         this.hoverState == 'in' ? this.leave() : this.enter();
12735     },
12736     
12737     enter : function () {
12738        
12739     
12740         clearTimeout(this.timeout);
12741     
12742         this.hoverState = 'in'
12743     
12744         if (!this.delay || !this.delay.show) {
12745             this.show();
12746             return 
12747         }
12748         var _t = this;
12749         this.timeout = setTimeout(function () {
12750             if (_t.hoverState == 'in') {
12751                 _t.show();
12752             }
12753         }, this.delay.show)
12754     },
12755     leave : function() {
12756         clearTimeout(this.timeout);
12757     
12758         this.hoverState = 'out'
12759     
12760         if (!this.delay || !this.delay.hide) {
12761             this.hide();
12762             return 
12763         }
12764         var _t = this;
12765         this.timeout = setTimeout(function () {
12766             if (_t.hoverState == 'out') {
12767                 _t.hide();
12768             }
12769         }, this.delay.hide)
12770     },
12771     
12772     show : function (on_el)
12773     {
12774         if (!on_el) {
12775             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12776         }
12777         // set content.
12778         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12779         if (this.html !== false) {
12780             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12781         }
12782         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12783         if (!this.title.length) {
12784             this.el.select('.popover-title',true).hide();
12785         }
12786         
12787         var placement = typeof this.placement == 'function' ?
12788             this.placement.call(this, this.el, on_el) :
12789             this.placement;
12790             
12791         var autoToken = /\s?auto?\s?/i;
12792         var autoPlace = autoToken.test(placement);
12793         if (autoPlace) {
12794             placement = placement.replace(autoToken, '') || 'top';
12795         }
12796         
12797         //this.el.detach()
12798         //this.el.setXY([0,0]);
12799         this.el.show();
12800         this.el.dom.style.display='block';
12801         this.el.addClass(placement);
12802         
12803         //this.el.appendTo(on_el);
12804         
12805         var p = this.getPosition();
12806         var box = this.el.getBox();
12807         
12808         if (autoPlace) {
12809             // fixme..
12810         }
12811         var align = Roo.bootstrap.Popover.alignment[placement]
12812         this.el.alignTo(on_el, align[0],align[1]);
12813         //var arrow = this.el.select('.arrow',true).first();
12814         //arrow.set(align[2], 
12815         
12816         this.el.addClass('in');
12817         this.hoverState = null;
12818         
12819         if (this.el.hasClass('fade')) {
12820             // fade it?
12821         }
12822         
12823     },
12824     hide : function()
12825     {
12826         this.el.setXY([0,0]);
12827         this.el.removeClass('in');
12828         this.el.hide();
12829         
12830     }
12831     
12832 });
12833
12834 Roo.bootstrap.Popover.alignment = {
12835     'left' : ['r-l', [-10,0], 'right'],
12836     'right' : ['l-r', [10,0], 'left'],
12837     'bottom' : ['t-b', [0,10], 'top'],
12838     'top' : [ 'b-t', [0,-10], 'bottom']
12839 };
12840
12841  /*
12842  * - LGPL
12843  *
12844  * Progress
12845  * 
12846  */
12847
12848 /**
12849  * @class Roo.bootstrap.Progress
12850  * @extends Roo.bootstrap.Component
12851  * Bootstrap Progress class
12852  * @cfg {Boolean} striped striped of the progress bar
12853  * @cfg {Boolean} active animated of the progress bar
12854  * 
12855  * 
12856  * @constructor
12857  * Create a new Progress
12858  * @param {Object} config The config object
12859  */
12860
12861 Roo.bootstrap.Progress = function(config){
12862     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12863 };
12864
12865 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
12866     
12867     striped : false,
12868     active: false,
12869     
12870     getAutoCreate : function(){
12871         var cfg = {
12872             tag: 'div',
12873             cls: 'progress'
12874         };
12875         
12876         
12877         if(this.striped){
12878             cfg.cls += ' progress-striped';
12879         }
12880       
12881         if(this.active){
12882             cfg.cls += ' active';
12883         }
12884         
12885         
12886         return cfg;
12887     }
12888    
12889 });
12890
12891  
12892
12893  /*
12894  * - LGPL
12895  *
12896  * ProgressBar
12897  * 
12898  */
12899
12900 /**
12901  * @class Roo.bootstrap.ProgressBar
12902  * @extends Roo.bootstrap.Component
12903  * Bootstrap ProgressBar class
12904  * @cfg {Number} aria_valuenow aria-value now
12905  * @cfg {Number} aria_valuemin aria-value min
12906  * @cfg {Number} aria_valuemax aria-value max
12907  * @cfg {String} label label for the progress bar
12908  * @cfg {String} panel (success | info | warning | danger )
12909  * @cfg {String} role role of the progress bar
12910  * @cfg {String} sr_only text
12911  * 
12912  * 
12913  * @constructor
12914  * Create a new ProgressBar
12915  * @param {Object} config The config object
12916  */
12917
12918 Roo.bootstrap.ProgressBar = function(config){
12919     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12920 };
12921
12922 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
12923     
12924     aria_valuenow : 0,
12925     aria_valuemin : 0,
12926     aria_valuemax : 100,
12927     label : false,
12928     panel : false,
12929     role : false,
12930     sr_only: false,
12931     
12932     getAutoCreate : function()
12933     {
12934         
12935         var cfg = {
12936             tag: 'div',
12937             cls: 'progress-bar',
12938             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12939         };
12940         
12941         if(this.sr_only){
12942             cfg.cn = {
12943                 tag: 'span',
12944                 cls: 'sr-only',
12945                 html: this.sr_only
12946             }
12947         }
12948         
12949         if(this.role){
12950             cfg.role = this.role;
12951         }
12952         
12953         if(this.aria_valuenow){
12954             cfg['aria-valuenow'] = this.aria_valuenow;
12955         }
12956         
12957         if(this.aria_valuemin){
12958             cfg['aria-valuemin'] = this.aria_valuemin;
12959         }
12960         
12961         if(this.aria_valuemax){
12962             cfg['aria-valuemax'] = this.aria_valuemax;
12963         }
12964         
12965         if(this.label && !this.sr_only){
12966             cfg.html = this.label;
12967         }
12968         
12969         if(this.panel){
12970             cfg.cls += ' progress-bar-' + this.panel;
12971         }
12972         
12973         return cfg;
12974     },
12975     
12976     update : function(aria_valuenow)
12977     {
12978         this.aria_valuenow = aria_valuenow;
12979         
12980         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12981     }
12982    
12983 });
12984
12985  
12986
12987  /*
12988  * - LGPL
12989  *
12990  * TabPanel
12991  * 
12992  */
12993
12994 /**
12995  * @class Roo.bootstrap.TabPanel
12996  * @extends Roo.bootstrap.Component
12997  * Bootstrap TabPanel class
12998  * @cfg {Boolean} active panel active
12999  * @cfg {String} html panel content
13000  * @cfg {String} tabId tab relate id
13001  * @cfg {String} navId The navbar which triggers show hide
13002  * 
13003  * 
13004  * @constructor
13005  * Create a new TabPanel
13006  * @param {Object} config The config object
13007  */
13008
13009 Roo.bootstrap.TabPanel = function(config){
13010     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13011      this.addEvents({
13012         /**
13013              * @event changed
13014              * Fires when the active status changes
13015              * @param {Roo.bootstrap.TabPanel} this
13016              * @param {Boolean} state the new state
13017             
13018          */
13019         'changed': true
13020      });
13021 };
13022
13023 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13024     
13025     active: false,
13026     html: false,
13027     tabId: false,
13028     navId : false,
13029     
13030     getAutoCreate : function(){
13031         var cfg = {
13032             tag: 'div',
13033             cls: 'tab-pane',
13034             html: this.html || ''
13035         };
13036         
13037         if(this.active){
13038             cfg.cls += ' active';
13039         }
13040         
13041         if(this.tabId){
13042             cfg.tabId = this.tabId;
13043         }
13044         
13045         return cfg;
13046     },
13047     onRender : function(ct, position)
13048     {
13049        // Roo.log("Call onRender: " + this.xtype);
13050         
13051         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13052         
13053         if (this.navId && this.tabId) {
13054             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13055             if (!item) {
13056                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13057             } else {
13058                 item.on('changed', function(item, state) {
13059                     this.setActive(state);
13060                 }, this);
13061             }
13062         }
13063         
13064     },
13065     setActive: function(state)
13066     {
13067         Roo.log("panel - set active " + this.tabId + "=" + state);
13068         
13069         this.active = state;
13070         if (!state) {
13071             this.el.removeClass('active');
13072             
13073         } else  if (!this.el.hasClass('active')) {
13074             this.el.addClass('active');
13075         }
13076         this.fireEvent('changed', this, state);
13077     }
13078     
13079     
13080 });
13081  
13082
13083  
13084
13085  /*
13086  * - LGPL
13087  *
13088  * DateField
13089  * 
13090  */
13091
13092 /**
13093  * @class Roo.bootstrap.DateField
13094  * @extends Roo.bootstrap.Input
13095  * Bootstrap DateField class
13096  * @cfg {Number} weekStart default 0
13097  * @cfg {Number} weekStart default 0
13098  * @cfg {Number} viewMode default empty, (months|years)
13099  * @cfg {Number} minViewMode default empty, (months|years)
13100  * @cfg {Number} startDate default -Infinity
13101  * @cfg {Number} endDate default Infinity
13102  * @cfg {Boolean} todayHighlight default false
13103  * @cfg {Boolean} todayBtn default false
13104  * @cfg {Boolean} calendarWeeks default false
13105  * @cfg {Object} daysOfWeekDisabled default empty
13106  * 
13107  * @cfg {Boolean} keyboardNavigation default true
13108  * @cfg {String} language default en
13109  * 
13110  * @constructor
13111  * Create a new DateField
13112  * @param {Object} config The config object
13113  */
13114
13115 Roo.bootstrap.DateField = function(config){
13116     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13117      this.addEvents({
13118             /**
13119              * @event show
13120              * Fires when this field show.
13121              * @param {Roo.bootstrap.DateField} this
13122              * @param {Mixed} date The date value
13123              */
13124             show : true,
13125             /**
13126              * @event show
13127              * Fires when this field hide.
13128              * @param {Roo.bootstrap.DateField} this
13129              * @param {Mixed} date The date value
13130              */
13131             hide : true,
13132             /**
13133              * @event select
13134              * Fires when select a date.
13135              * @param {Roo.bootstrap.DateField} this
13136              * @param {Mixed} date The date value
13137              */
13138             select : true
13139         });
13140 };
13141
13142 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13143     
13144     /**
13145      * @cfg {String} format
13146      * The default date format string which can be overriden for localization support.  The format must be
13147      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13148      */
13149     format : "m/d/y",
13150     /**
13151      * @cfg {String} altFormats
13152      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13153      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13154      */
13155     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13156     
13157     weekStart : 0,
13158     
13159     viewMode : '',
13160     
13161     minViewMode : '',
13162     
13163     todayHighlight : false,
13164     
13165     todayBtn: false,
13166     
13167     language: 'en',
13168     
13169     keyboardNavigation: true,
13170     
13171     calendarWeeks: false,
13172     
13173     startDate: -Infinity,
13174     
13175     endDate: Infinity,
13176     
13177     daysOfWeekDisabled: [],
13178     
13179     _events: [],
13180     
13181     UTCDate: function()
13182     {
13183         return new Date(Date.UTC.apply(Date, arguments));
13184     },
13185     
13186     UTCToday: function()
13187     {
13188         var today = new Date();
13189         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13190     },
13191     
13192     getDate: function() {
13193             var d = this.getUTCDate();
13194             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13195     },
13196     
13197     getUTCDate: function() {
13198             return this.date;
13199     },
13200     
13201     setDate: function(d) {
13202             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13203     },
13204     
13205     setUTCDate: function(d) {
13206             this.date = d;
13207             this.setValue(this.formatDate(this.date));
13208     },
13209         
13210     onRender: function(ct, position)
13211     {
13212         
13213         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13214         
13215         this.language = this.language || 'en';
13216         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13217         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13218         
13219         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13220         this.format = this.format || 'm/d/y';
13221         this.isInline = false;
13222         this.isInput = true;
13223         this.component = this.el.select('.add-on', true).first() || false;
13224         this.component = (this.component && this.component.length === 0) ? false : this.component;
13225         this.hasInput = this.component && this.inputEL().length;
13226         
13227         if (typeof(this.minViewMode === 'string')) {
13228             switch (this.minViewMode) {
13229                 case 'months':
13230                     this.minViewMode = 1;
13231                     break;
13232                 case 'years':
13233                     this.minViewMode = 2;
13234                     break;
13235                 default:
13236                     this.minViewMode = 0;
13237                     break;
13238             }
13239         }
13240         
13241         if (typeof(this.viewMode === 'string')) {
13242             switch (this.viewMode) {
13243                 case 'months':
13244                     this.viewMode = 1;
13245                     break;
13246                 case 'years':
13247                     this.viewMode = 2;
13248                     break;
13249                 default:
13250                     this.viewMode = 0;
13251                     break;
13252             }
13253         }
13254                 
13255         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13256         
13257         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13258         
13259         this.picker().on('mousedown', this.onMousedown, this);
13260         this.picker().on('click', this.onClick, this);
13261         
13262         this.picker().addClass('datepicker-dropdown');
13263         
13264         this.startViewMode = this.viewMode;
13265         
13266         
13267         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13268             if(!this.calendarWeeks){
13269                 v.remove();
13270                 return;
13271             };
13272             
13273             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13274             v.attr('colspan', function(i, val){
13275                 return parseInt(val) + 1;
13276             });
13277         })
13278                         
13279         
13280         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13281         
13282         this.setStartDate(this.startDate);
13283         this.setEndDate(this.endDate);
13284         
13285         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13286         
13287         this.fillDow();
13288         this.fillMonths();
13289         this.update();
13290         this.showMode();
13291         
13292         if(this.isInline) {
13293             this.show();
13294         }
13295     },
13296     
13297     picker : function()
13298     {
13299         return this.el.select('.datepicker', true).first();
13300     },
13301     
13302     fillDow: function()
13303     {
13304         var dowCnt = this.weekStart;
13305         
13306         var dow = {
13307             tag: 'tr',
13308             cn: [
13309                 
13310             ]
13311         };
13312         
13313         if(this.calendarWeeks){
13314             dow.cn.push({
13315                 tag: 'th',
13316                 cls: 'cw',
13317                 html: '&nbsp;'
13318             })
13319         }
13320         
13321         while (dowCnt < this.weekStart + 7) {
13322             dow.cn.push({
13323                 tag: 'th',
13324                 cls: 'dow',
13325                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13326             });
13327         }
13328         
13329         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13330     },
13331     
13332     fillMonths: function()
13333     {    
13334         var i = 0
13335         var months = this.picker().select('>.datepicker-months td', true).first();
13336         
13337         months.dom.innerHTML = '';
13338         
13339         while (i < 12) {
13340             var month = {
13341                 tag: 'span',
13342                 cls: 'month',
13343                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13344             }
13345             
13346             months.createChild(month);
13347         }
13348         
13349     },
13350     
13351     update: function(){
13352         
13353         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13354         
13355         if (this.date < this.startDate) {
13356             this.viewDate = new Date(this.startDate);
13357         } else if (this.date > this.endDate) {
13358             this.viewDate = new Date(this.endDate);
13359         } else {
13360             this.viewDate = new Date(this.date);
13361         }
13362         
13363         this.fill();
13364     },
13365     
13366     fill: function() {
13367         var d = new Date(this.viewDate),
13368                 year = d.getUTCFullYear(),
13369                 month = d.getUTCMonth(),
13370                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
13371                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
13372                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
13373                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
13374                 currentDate = this.date && this.date.valueOf(),
13375                 today = this.UTCToday();
13376         
13377         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
13378         
13379 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
13380         
13381 //        this.picker.select('>tfoot th.today').
13382 //                                              .text(dates[this.language].today)
13383 //                                              .toggle(this.todayBtn !== false);
13384     
13385         this.updateNavArrows();
13386         this.fillMonths();
13387                                                 
13388         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
13389         
13390         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
13391          
13392         prevMonth.setUTCDate(day);
13393         
13394         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
13395         
13396         var nextMonth = new Date(prevMonth);
13397         
13398         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
13399         
13400         nextMonth = nextMonth.valueOf();
13401         
13402         var fillMonths = false;
13403         
13404         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
13405         
13406         while(prevMonth.valueOf() < nextMonth) {
13407             var clsName = '';
13408             
13409             if (prevMonth.getUTCDay() === this.weekStart) {
13410                 if(fillMonths){
13411                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
13412                 }
13413                     
13414                 fillMonths = {
13415                     tag: 'tr',
13416                     cn: []
13417                 };
13418                 
13419                 if(this.calendarWeeks){
13420                     // ISO 8601: First week contains first thursday.
13421                     // ISO also states week starts on Monday, but we can be more abstract here.
13422                     var
13423                     // Start of current week: based on weekstart/current date
13424                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
13425                     // Thursday of this week
13426                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
13427                     // First Thursday of year, year from thursday
13428                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
13429                     // Calendar week: ms between thursdays, div ms per day, div 7 days
13430                     calWeek =  (th - yth) / 864e5 / 7 + 1;
13431                     
13432                     fillMonths.cn.push({
13433                         tag: 'td',
13434                         cls: 'cw',
13435                         html: calWeek
13436                     });
13437                 }
13438             }
13439             
13440             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
13441                 clsName += ' old';
13442             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
13443                 clsName += ' new';
13444             }
13445             if (this.todayHighlight &&
13446                 prevMonth.getUTCFullYear() == today.getFullYear() &&
13447                 prevMonth.getUTCMonth() == today.getMonth() &&
13448                 prevMonth.getUTCDate() == today.getDate()) {
13449                 clsName += ' today';
13450             }
13451             
13452             if (currentDate && prevMonth.valueOf() === currentDate) {
13453                 clsName += ' active';
13454             }
13455             
13456             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
13457                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
13458                     clsName += ' disabled';
13459             }
13460             
13461             fillMonths.cn.push({
13462                 tag: 'td',
13463                 cls: 'day ' + clsName,
13464                 html: prevMonth.getDate()
13465             })
13466             
13467             prevMonth.setDate(prevMonth.getDate()+1);
13468         }
13469           
13470         var currentYear = this.date && this.date.getUTCFullYear();
13471         var currentMonth = this.date && this.date.getUTCMonth();
13472         
13473         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
13474         
13475         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
13476             v.removeClass('active');
13477             
13478             if(currentYear === year && k === currentMonth){
13479                 v.addClass('active');
13480             }
13481             
13482             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
13483                 v.addClass('disabled');
13484             }
13485             
13486         });
13487         
13488         
13489         year = parseInt(year/10, 10) * 10;
13490         
13491         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
13492         
13493         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
13494         
13495         year -= 1;
13496         for (var i = -1; i < 11; i++) {
13497             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
13498                 tag: 'span',
13499                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
13500                 html: year
13501             })
13502             
13503             year += 1;
13504         }
13505     },
13506     
13507     showMode: function(dir) {
13508         if (dir) {
13509             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
13510         }
13511         Roo.each(this.picker().select('>div',true).elements, function(v){
13512             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13513             v.hide();
13514         });
13515         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
13516     },
13517     
13518     place: function()
13519     {
13520         if(this.isInline) return;
13521         
13522         this.picker().removeClass(['bottom', 'top']);
13523         
13524         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13525             /*
13526              * place to the top of element!
13527              *
13528              */
13529             
13530             this.picker().addClass('top');
13531             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13532             
13533             return;
13534         }
13535         
13536         this.picker().addClass('bottom');
13537         
13538         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13539     },
13540     
13541     parseDate : function(value){
13542         if(!value || value instanceof Date){
13543             return value;
13544         }
13545         var v = Date.parseDate(value, this.format);
13546         if (!v && this.useIso) {
13547             v = Date.parseDate(value, 'Y-m-d');
13548         }
13549         if(!v && this.altFormats){
13550             if(!this.altFormatsArray){
13551                 this.altFormatsArray = this.altFormats.split("|");
13552             }
13553             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
13554                 v = Date.parseDate(value, this.altFormatsArray[i]);
13555             }
13556         }
13557         return v;
13558     },
13559     
13560     formatDate : function(date, fmt){
13561         return (!date || !(date instanceof Date)) ?
13562         date : date.dateFormat(fmt || this.format);
13563     },
13564     
13565     onFocus : function()
13566     {
13567         Roo.bootstrap.DateField.superclass.onFocus.call(this);
13568         this.show();
13569     },
13570     
13571     onBlur : function()
13572     {
13573         Roo.bootstrap.DateField.superclass.onBlur.call(this);
13574         this.hide();
13575     },
13576     
13577     show : function()
13578     {
13579         this.picker().show();
13580         this.update();
13581         this.place();
13582         
13583         this.fireEvent('show', this, this.date);
13584     },
13585     
13586     hide : function()
13587     {
13588         if(this.isInline) return;
13589         this.picker().hide();
13590         this.viewMode = this.startViewMode;
13591         this.showMode();
13592         
13593         this.fireEvent('hide', this, this.date);
13594         
13595     },
13596     
13597     onMousedown: function(e){
13598         e.stopPropagation();
13599         e.preventDefault();
13600     },
13601     
13602     keyup: function(e){
13603         Roo.bootstrap.DateField.superclass.keyup.call(this);
13604         this.update();
13605         
13606     },
13607
13608     setValue: function(v){
13609         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
13610         
13611         this.fireEvent('select', this, this.date);
13612         
13613     },
13614     
13615     fireKey: function(e){
13616         if (!this.picker().isVisible()){
13617             if (e.keyCode == 27) // allow escape to hide and re-show picker
13618                 this.show();
13619             return;
13620         }
13621         var dateChanged = false,
13622         dir, day, month,
13623         newDate, newViewDate;
13624         switch(e.keyCode){
13625             case 27: // escape
13626                 this.hide();
13627                 e.preventDefault();
13628                 break;
13629             case 37: // left
13630             case 39: // right
13631                 if (!this.keyboardNavigation) break;
13632                 dir = e.keyCode == 37 ? -1 : 1;
13633                 
13634                 if (e.ctrlKey){
13635                     newDate = this.moveYear(this.date, dir);
13636                     newViewDate = this.moveYear(this.viewDate, dir);
13637                 } else if (e.shiftKey){
13638                     newDate = this.moveMonth(this.date, dir);
13639                     newViewDate = this.moveMonth(this.viewDate, dir);
13640                 } else {
13641                     newDate = new Date(this.date);
13642                     newDate.setUTCDate(this.date.getUTCDate() + dir);
13643                     newViewDate = new Date(this.viewDate);
13644                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
13645                 }
13646                 if (this.dateWithinRange(newDate)){
13647                     this.date = newDate;
13648                     this.viewDate = newViewDate;
13649                     this.setValue(this.formatDate(this.date));
13650                     this.update();
13651                     e.preventDefault();
13652                     dateChanged = true;
13653                 }
13654                 break;
13655             case 38: // up
13656             case 40: // down
13657                 if (!this.keyboardNavigation) break;
13658                 dir = e.keyCode == 38 ? -1 : 1;
13659                 if (e.ctrlKey){
13660                     newDate = this.moveYear(this.date, dir);
13661                     newViewDate = this.moveYear(this.viewDate, dir);
13662                 } else if (e.shiftKey){
13663                     newDate = this.moveMonth(this.date, dir);
13664                     newViewDate = this.moveMonth(this.viewDate, dir);
13665                 } else {
13666                     newDate = new Date(this.date);
13667                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
13668                     newViewDate = new Date(this.viewDate);
13669                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
13670                 }
13671                 if (this.dateWithinRange(newDate)){
13672                     this.date = newDate;
13673                     this.viewDate = newViewDate;
13674                     this.setValue(this.formatDate(this.date));
13675                     this.update();
13676                     e.preventDefault();
13677                     dateChanged = true;
13678                 }
13679                 break;
13680             case 13: // enter
13681                 this.setValue(this.formatDate(this.date));
13682                 this.hide();
13683                 e.preventDefault();
13684                 break;
13685             case 9: // tab
13686                 this.setValue(this.formatDate(this.date));
13687                 this.hide();
13688                 break;
13689         }
13690     },
13691     
13692     
13693     onClick: function(e) {
13694         e.stopPropagation();
13695         e.preventDefault();
13696         
13697         var target = e.getTarget();
13698         
13699         if(target.nodeName.toLowerCase() === 'i'){
13700             target = Roo.get(target).dom.parentNode;
13701         }
13702         
13703         var nodeName = target.nodeName;
13704         var className = target.className;
13705         var html = target.innerHTML;
13706         
13707         switch(nodeName.toLowerCase()) {
13708             case 'th':
13709                 switch(className) {
13710                     case 'switch':
13711                         this.showMode(1);
13712                         break;
13713                     case 'prev':
13714                     case 'next':
13715                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13716                         switch(this.viewMode){
13717                                 case 0:
13718                                         this.viewDate = this.moveMonth(this.viewDate, dir);
13719                                         break;
13720                                 case 1:
13721                                 case 2:
13722                                         this.viewDate = this.moveYear(this.viewDate, dir);
13723                                         break;
13724                         }
13725                         this.fill();
13726                         break;
13727                     case 'today':
13728                         var date = new Date();
13729                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13730                         this.fill()
13731                         this.setValue(this.formatDate(this.date));
13732                         this.hide();
13733                         break;
13734                 }
13735                 break;
13736             case 'span':
13737                 if (className.indexOf('disabled') === -1) {
13738                     this.viewDate.setUTCDate(1);
13739                     if (className.indexOf('month') !== -1) {
13740                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13741                     } else {
13742                         var year = parseInt(html, 10) || 0;
13743                         this.viewDate.setUTCFullYear(year);
13744                         
13745                     }
13746                     this.showMode(-1);
13747                     this.fill();
13748                 }
13749                 break;
13750                 
13751             case 'td':
13752                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13753                     var day = parseInt(html, 10) || 1;
13754                     var year = this.viewDate.getUTCFullYear(),
13755                         month = this.viewDate.getUTCMonth();
13756
13757                     if (className.indexOf('old') !== -1) {
13758                         if(month === 0 ){
13759                             month = 11;
13760                             year -= 1;
13761                         }else{
13762                             month -= 1;
13763                         }
13764                     } else if (className.indexOf('new') !== -1) {
13765                         if (month == 11) {
13766                             month = 0;
13767                             year += 1;
13768                         } else {
13769                             month += 1;
13770                         }
13771                     }
13772                     this.date = this.UTCDate(year, month, day,0,0,0,0);
13773                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13774                     this.fill();
13775                     this.setValue(this.formatDate(this.date));
13776                     this.hide();
13777                 }
13778                 break;
13779         }
13780     },
13781     
13782     setStartDate: function(startDate){
13783         this.startDate = startDate || -Infinity;
13784         if (this.startDate !== -Infinity) {
13785             this.startDate = this.parseDate(this.startDate);
13786         }
13787         this.update();
13788         this.updateNavArrows();
13789     },
13790
13791     setEndDate: function(endDate){
13792         this.endDate = endDate || Infinity;
13793         if (this.endDate !== Infinity) {
13794             this.endDate = this.parseDate(this.endDate);
13795         }
13796         this.update();
13797         this.updateNavArrows();
13798     },
13799     
13800     setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13801         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13802         if (typeof(this.daysOfWeekDisabled) !== 'object') {
13803             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13804         }
13805         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13806             return parseInt(d, 10);
13807         });
13808         this.update();
13809         this.updateNavArrows();
13810     },
13811     
13812     updateNavArrows: function() {
13813         var d = new Date(this.viewDate),
13814         year = d.getUTCFullYear(),
13815         month = d.getUTCMonth();
13816         
13817         Roo.each(this.picker().select('.prev', true).elements, function(v){
13818             v.show();
13819             switch (this.viewMode) {
13820                 case 0:
13821
13822                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13823                         v.hide();
13824                     }
13825                     break;
13826                 case 1:
13827                 case 2:
13828                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13829                         v.hide();
13830                     }
13831                     break;
13832             }
13833         });
13834         
13835         Roo.each(this.picker().select('.next', true).elements, function(v){
13836             v.show();
13837             switch (this.viewMode) {
13838                 case 0:
13839
13840                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13841                         v.hide();
13842                     }
13843                     break;
13844                 case 1:
13845                 case 2:
13846                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13847                         v.hide();
13848                     }
13849                     break;
13850             }
13851         })
13852     },
13853     
13854     moveMonth: function(date, dir){
13855         if (!dir) return date;
13856         var new_date = new Date(date.valueOf()),
13857         day = new_date.getUTCDate(),
13858         month = new_date.getUTCMonth(),
13859         mag = Math.abs(dir),
13860         new_month, test;
13861         dir = dir > 0 ? 1 : -1;
13862         if (mag == 1){
13863             test = dir == -1
13864             // If going back one month, make sure month is not current month
13865             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13866             ? function(){
13867                 return new_date.getUTCMonth() == month;
13868             }
13869             // If going forward one month, make sure month is as expected
13870             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13871             : function(){
13872                 return new_date.getUTCMonth() != new_month;
13873             };
13874             new_month = month + dir;
13875             new_date.setUTCMonth(new_month);
13876             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13877             if (new_month < 0 || new_month > 11)
13878                 new_month = (new_month + 12) % 12;
13879         } else {
13880             // For magnitudes >1, move one month at a time...
13881             for (var i=0; i<mag; i++)
13882                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13883                 new_date = this.moveMonth(new_date, dir);
13884             // ...then reset the day, keeping it in the new month
13885             new_month = new_date.getUTCMonth();
13886             new_date.setUTCDate(day);
13887             test = function(){
13888                 return new_month != new_date.getUTCMonth();
13889             };
13890         }
13891         // Common date-resetting loop -- if date is beyond end of month, make it
13892         // end of month
13893         while (test()){
13894             new_date.setUTCDate(--day);
13895             new_date.setUTCMonth(new_month);
13896         }
13897         return new_date;
13898     },
13899
13900     moveYear: function(date, dir){
13901         return this.moveMonth(date, dir*12);
13902     },
13903
13904     dateWithinRange: function(date){
13905         return date >= this.startDate && date <= this.endDate;
13906     },
13907
13908     
13909     remove: function() {
13910         this.picker().remove();
13911     }
13912    
13913 });
13914
13915 Roo.apply(Roo.bootstrap.DateField,  {
13916     
13917     head : {
13918         tag: 'thead',
13919         cn: [
13920         {
13921             tag: 'tr',
13922             cn: [
13923             {
13924                 tag: 'th',
13925                 cls: 'prev',
13926                 html: '<i class="fa fa-arrow-left"/>'
13927             },
13928             {
13929                 tag: 'th',
13930                 cls: 'switch',
13931                 colspan: '5'
13932             },
13933             {
13934                 tag: 'th',
13935                 cls: 'next',
13936                 html: '<i class="fa fa-arrow-right"/>'
13937             }
13938
13939             ]
13940         }
13941         ]
13942     },
13943     
13944     content : {
13945         tag: 'tbody',
13946         cn: [
13947         {
13948             tag: 'tr',
13949             cn: [
13950             {
13951                 tag: 'td',
13952                 colspan: '7'
13953             }
13954             ]
13955         }
13956         ]
13957     },
13958     
13959     footer : {
13960         tag: 'tfoot',
13961         cn: [
13962         {
13963             tag: 'tr',
13964             cn: [
13965             {
13966                 tag: 'th',
13967                 colspan: '7',
13968                 cls: 'today'
13969             }
13970                     
13971             ]
13972         }
13973         ]
13974     },
13975     
13976     dates:{
13977         en: {
13978             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13979             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13980             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13981             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13982             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13983             today: "Today"
13984         }
13985     },
13986     
13987     modes: [
13988     {
13989         clsName: 'days',
13990         navFnc: 'Month',
13991         navStep: 1
13992     },
13993     {
13994         clsName: 'months',
13995         navFnc: 'FullYear',
13996         navStep: 1
13997     },
13998     {
13999         clsName: 'years',
14000         navFnc: 'FullYear',
14001         navStep: 10
14002     }]
14003 });
14004
14005 Roo.apply(Roo.bootstrap.DateField,  {
14006   
14007     template : {
14008         tag: 'div',
14009         cls: 'datepicker dropdown-menu',
14010         cn: [
14011         {
14012             tag: 'div',
14013             cls: 'datepicker-days',
14014             cn: [
14015             {
14016                 tag: 'table',
14017                 cls: 'table-condensed',
14018                 cn:[
14019                 Roo.bootstrap.DateField.head,
14020                 {
14021                     tag: 'tbody'
14022                 },
14023                 Roo.bootstrap.DateField.footer
14024                 ]
14025             }
14026             ]
14027         },
14028         {
14029             tag: 'div',
14030             cls: 'datepicker-months',
14031             cn: [
14032             {
14033                 tag: 'table',
14034                 cls: 'table-condensed',
14035                 cn:[
14036                 Roo.bootstrap.DateField.head,
14037                 Roo.bootstrap.DateField.content,
14038                 Roo.bootstrap.DateField.footer
14039                 ]
14040             }
14041             ]
14042         },
14043         {
14044             tag: 'div',
14045             cls: 'datepicker-years',
14046             cn: [
14047             {
14048                 tag: 'table',
14049                 cls: 'table-condensed',
14050                 cn:[
14051                 Roo.bootstrap.DateField.head,
14052                 Roo.bootstrap.DateField.content,
14053                 Roo.bootstrap.DateField.footer
14054                 ]
14055             }
14056             ]
14057         }
14058         ]
14059     }
14060 });
14061
14062  
14063
14064  /*
14065  * - LGPL
14066  *
14067  * TimeField
14068  * 
14069  */
14070
14071 /**
14072  * @class Roo.bootstrap.TimeField
14073  * @extends Roo.bootstrap.Input
14074  * Bootstrap DateField class
14075  * 
14076  * 
14077  * @constructor
14078  * Create a new TimeField
14079  * @param {Object} config The config object
14080  */
14081
14082 Roo.bootstrap.TimeField = function(config){
14083     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14084     this.addEvents({
14085             /**
14086              * @event show
14087              * Fires when this field show.
14088              * @param {Roo.bootstrap.DateField} this
14089              * @param {Mixed} date The date value
14090              */
14091             show : true,
14092             /**
14093              * @event show
14094              * Fires when this field hide.
14095              * @param {Roo.bootstrap.DateField} this
14096              * @param {Mixed} date The date value
14097              */
14098             hide : true,
14099             /**
14100              * @event select
14101              * Fires when select a date.
14102              * @param {Roo.bootstrap.DateField} this
14103              * @param {Mixed} date The date value
14104              */
14105             select : true
14106         });
14107 };
14108
14109 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14110     
14111     /**
14112      * @cfg {String} format
14113      * The default time format string which can be overriden for localization support.  The format must be
14114      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14115      */
14116     format : "H:i",
14117        
14118     onRender: function(ct, position)
14119     {
14120         
14121         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14122                 
14123         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14124         
14125         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14126         
14127         this.pop = this.picker().select('>.datepicker-time',true).first();
14128         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14129         
14130         this.picker().on('mousedown', this.onMousedown, this);
14131         this.picker().on('click', this.onClick, this);
14132         
14133         this.picker().addClass('datepicker-dropdown');
14134     
14135         this.fillTime();
14136         this.update();
14137             
14138         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14139         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14140         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14141         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14142         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14143         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14144
14145     },
14146     
14147     fireKey: function(e){
14148         if (!this.picker().isVisible()){
14149             if (e.keyCode == 27) // allow escape to hide and re-show picker
14150                 this.show();
14151             return;
14152         }
14153
14154         e.preventDefault();
14155         
14156         switch(e.keyCode){
14157             case 27: // escape
14158                 this.hide();
14159                 break;
14160             case 37: // left
14161             case 39: // right
14162                 this.onTogglePeriod();
14163                 break;
14164             case 38: // up
14165                 this.onIncrementMinutes();
14166                 break;
14167             case 40: // down
14168                 this.onDecrementMinutes();
14169                 break;
14170             case 13: // enter
14171             case 9: // tab
14172                 this.setTime();
14173                 break;
14174         }
14175     },
14176     
14177     onClick: function(e) {
14178         e.stopPropagation();
14179         e.preventDefault();
14180     },
14181     
14182     picker : function()
14183     {
14184         return this.el.select('.datepicker', true).first();
14185     },
14186     
14187     fillTime: function()
14188     {    
14189         var time = this.pop.select('tbody', true).first();
14190         
14191         time.dom.innerHTML = '';
14192         
14193         time.createChild({
14194             tag: 'tr',
14195             cn: [
14196                 {
14197                     tag: 'td',
14198                     cn: [
14199                         {
14200                             tag: 'a',
14201                             href: '#',
14202                             cls: 'btn',
14203                             cn: [
14204                                 {
14205                                     tag: 'span',
14206                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14207                                 }
14208                             ]
14209                         } 
14210                     ]
14211                 },
14212                 {
14213                     tag: 'td',
14214                     cls: 'separator'
14215                 },
14216                 {
14217                     tag: 'td',
14218                     cn: [
14219                         {
14220                             tag: 'a',
14221                             href: '#',
14222                             cls: 'btn',
14223                             cn: [
14224                                 {
14225                                     tag: 'span',
14226                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14227                                 }
14228                             ]
14229                         }
14230                     ]
14231                 },
14232                 {
14233                     tag: 'td',
14234                     cls: 'separator'
14235                 }
14236             ]
14237         });
14238         
14239         time.createChild({
14240             tag: 'tr',
14241             cn: [
14242                 {
14243                     tag: 'td',
14244                     cn: [
14245                         {
14246                             tag: 'span',
14247                             cls: 'timepicker-hour',
14248                             html: '00'
14249                         }  
14250                     ]
14251                 },
14252                 {
14253                     tag: 'td',
14254                     cls: 'separator',
14255                     html: ':'
14256                 },
14257                 {
14258                     tag: 'td',
14259                     cn: [
14260                         {
14261                             tag: 'span',
14262                             cls: 'timepicker-minute',
14263                             html: '00'
14264                         }  
14265                     ]
14266                 },
14267                 {
14268                     tag: 'td',
14269                     cls: 'separator'
14270                 },
14271                 {
14272                     tag: 'td',
14273                     cn: [
14274                         {
14275                             tag: 'button',
14276                             type: 'button',
14277                             cls: 'btn btn-primary period',
14278                             html: 'AM'
14279                             
14280                         }
14281                     ]
14282                 }
14283             ]
14284         });
14285         
14286         time.createChild({
14287             tag: 'tr',
14288             cn: [
14289                 {
14290                     tag: 'td',
14291                     cn: [
14292                         {
14293                             tag: 'a',
14294                             href: '#',
14295                             cls: 'btn',
14296                             cn: [
14297                                 {
14298                                     tag: 'span',
14299                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14300                                 }
14301                             ]
14302                         }
14303                     ]
14304                 },
14305                 {
14306                     tag: 'td',
14307                     cls: 'separator'
14308                 },
14309                 {
14310                     tag: 'td',
14311                     cn: [
14312                         {
14313                             tag: 'a',
14314                             href: '#',
14315                             cls: 'btn',
14316                             cn: [
14317                                 {
14318                                     tag: 'span',
14319                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14320                                 }
14321                             ]
14322                         }
14323                     ]
14324                 },
14325                 {
14326                     tag: 'td',
14327                     cls: 'separator'
14328                 }
14329             ]
14330         });
14331         
14332     },
14333     
14334     update: function()
14335     {
14336         
14337         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
14338         
14339         this.fill();
14340     },
14341     
14342     fill: function() 
14343     {
14344         var hours = this.time.getHours();
14345         var minutes = this.time.getMinutes();
14346         var period = 'AM';
14347         
14348         if(hours > 11){
14349             period = 'PM';
14350         }
14351         
14352         if(hours == 0){
14353             hours = 12;
14354         }
14355         
14356         
14357         if(hours > 12){
14358             hours = hours - 12;
14359         }
14360         
14361         if(hours < 10){
14362             hours = '0' + hours;
14363         }
14364         
14365         if(minutes < 10){
14366             minutes = '0' + minutes;
14367         }
14368         
14369         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
14370         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
14371         this.pop.select('button', true).first().dom.innerHTML = period;
14372         
14373     },
14374     
14375     place: function()
14376     {   
14377         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
14378         
14379         var cls = ['bottom'];
14380         
14381         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
14382             cls.pop();
14383             cls.push('top');
14384         }
14385         
14386         cls.push('right');
14387         
14388         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
14389             cls.pop();
14390             cls.push('left');
14391         }
14392         
14393         this.picker().addClass(cls.join('-'));
14394         
14395         var _this = this;
14396         
14397         Roo.each(cls, function(c){
14398             if(c == 'bottom'){
14399                 _this.picker().setTop(_this.inputEl().getHeight());
14400                 return;
14401             }
14402             if(c == 'top'){
14403                 _this.picker().setTop(0 - _this.picker().getHeight());
14404                 return;
14405             }
14406             
14407             if(c == 'left'){
14408                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
14409                 return;
14410             }
14411             if(c == 'right'){
14412                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
14413                 return;
14414             }
14415         });
14416         
14417     },
14418   
14419     onFocus : function()
14420     {
14421         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
14422         this.show();
14423     },
14424     
14425     onBlur : function()
14426     {
14427         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
14428         this.hide();
14429     },
14430     
14431     show : function()
14432     {
14433         this.picker().show();
14434         this.pop.show();
14435         this.update();
14436         this.place();
14437         
14438         this.fireEvent('show', this, this.date);
14439     },
14440     
14441     hide : function()
14442     {
14443         this.picker().hide();
14444         this.pop.hide();
14445         
14446         this.fireEvent('hide', this, this.date);
14447     },
14448     
14449     setTime : function()
14450     {
14451         this.hide();
14452         this.setValue(this.time.format(this.format));
14453         
14454         this.fireEvent('select', this, this.date);
14455         
14456         
14457     },
14458     
14459     onMousedown: function(e){
14460         e.stopPropagation();
14461         e.preventDefault();
14462     },
14463     
14464     onIncrementHours: function()
14465     {
14466         Roo.log('onIncrementHours');
14467         this.time = this.time.add(Date.HOUR, 1);
14468         this.update();
14469         
14470     },
14471     
14472     onDecrementHours: function()
14473     {
14474         Roo.log('onDecrementHours');
14475         this.time = this.time.add(Date.HOUR, -1);
14476         this.update();
14477     },
14478     
14479     onIncrementMinutes: function()
14480     {
14481         Roo.log('onIncrementMinutes');
14482         this.time = this.time.add(Date.MINUTE, 1);
14483         this.update();
14484     },
14485     
14486     onDecrementMinutes: function()
14487     {
14488         Roo.log('onDecrementMinutes');
14489         this.time = this.time.add(Date.MINUTE, -1);
14490         this.update();
14491     },
14492     
14493     onTogglePeriod: function()
14494     {
14495         Roo.log('onTogglePeriod');
14496         this.time = this.time.add(Date.HOUR, 12);
14497         this.update();
14498     }
14499     
14500    
14501 });
14502
14503 Roo.apply(Roo.bootstrap.TimeField,  {
14504     
14505     content : {
14506         tag: 'tbody',
14507         cn: [
14508             {
14509                 tag: 'tr',
14510                 cn: [
14511                 {
14512                     tag: 'td',
14513                     colspan: '7'
14514                 }
14515                 ]
14516             }
14517         ]
14518     },
14519     
14520     footer : {
14521         tag: 'tfoot',
14522         cn: [
14523             {
14524                 tag: 'tr',
14525                 cn: [
14526                 {
14527                     tag: 'th',
14528                     colspan: '7',
14529                     cls: '',
14530                     cn: [
14531                         {
14532                             tag: 'button',
14533                             cls: 'btn btn-info ok',
14534                             html: 'OK'
14535                         }
14536                     ]
14537                 }
14538
14539                 ]
14540             }
14541         ]
14542     }
14543 });
14544
14545 Roo.apply(Roo.bootstrap.TimeField,  {
14546   
14547     template : {
14548         tag: 'div',
14549         cls: 'datepicker dropdown-menu',
14550         cn: [
14551             {
14552                 tag: 'div',
14553                 cls: 'datepicker-time',
14554                 cn: [
14555                 {
14556                     tag: 'table',
14557                     cls: 'table-condensed',
14558                     cn:[
14559                     Roo.bootstrap.TimeField.content,
14560                     Roo.bootstrap.TimeField.footer
14561                     ]
14562                 }
14563                 ]
14564             }
14565         ]
14566     }
14567 });
14568
14569  
14570
14571  /*
14572  * - LGPL
14573  *
14574  * CheckBox
14575  * 
14576  */
14577
14578 /**
14579  * @class Roo.bootstrap.CheckBox
14580  * @extends Roo.bootstrap.Input
14581  * Bootstrap CheckBox class
14582  * 
14583  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
14584  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
14585  * @cfg {String} boxLabel The text that appears beside the checkbox
14586  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
14587  * @cfg {Boolean} checked initnal the element
14588  * 
14589  * 
14590  * @constructor
14591  * Create a new CheckBox
14592  * @param {Object} config The config object
14593  */
14594
14595 Roo.bootstrap.CheckBox = function(config){
14596     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
14597    
14598         this.addEvents({
14599             /**
14600             * @event check
14601             * Fires when the element is checked or unchecked.
14602             * @param {Roo.bootstrap.CheckBox} this This input
14603             * @param {Boolean} checked The new checked value
14604             */
14605            check : true
14606         });
14607 };
14608
14609 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
14610     
14611     inputType: 'checkbox',
14612     inputValue: 1,
14613     valueOff: 0,
14614     boxLabel: false,
14615     checked: false,
14616     weight : false,
14617     
14618     getAutoCreate : function()
14619     {
14620         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14621         
14622         var id = Roo.id();
14623         
14624         var cfg = {};
14625         
14626         cfg.cls = 'form-group checkbox' //input-group
14627         
14628         
14629         
14630         
14631         var input =  {
14632             tag: 'input',
14633             id : id,
14634             type : this.inputType,
14635             value : (!this.checked) ? this.valueOff : this.inputValue,
14636             cls : 'roo-checkbox', //'form-box',
14637             placeholder : this.placeholder || ''
14638             
14639         };
14640         
14641         if (this.weight) { // Validity check?
14642             cfg.cls += " checkbox-" + this.weight;
14643         }
14644         
14645         if (this.disabled) {
14646             input.disabled=true;
14647         }
14648         
14649         if(this.checked){
14650             input.checked = this.checked;
14651         }
14652         
14653         if (this.name) {
14654             input.name = this.name;
14655         }
14656         
14657         if (this.size) {
14658             input.cls += ' input-' + this.size;
14659         }
14660         
14661         var settings=this;
14662         ['xs','sm','md','lg'].map(function(size){
14663             if (settings[size]) {
14664                 cfg.cls += ' col-' + size + '-' + settings[size];
14665             }
14666         });
14667         
14668        
14669         
14670         var inputblock = input;
14671         
14672         
14673         
14674         
14675         if (this.before || this.after) {
14676             
14677             inputblock = {
14678                 cls : 'input-group',
14679                 cn :  [] 
14680             };
14681             if (this.before) {
14682                 inputblock.cn.push({
14683                     tag :'span',
14684                     cls : 'input-group-addon',
14685                     html : this.before
14686                 });
14687             }
14688             inputblock.cn.push(input);
14689             if (this.after) {
14690                 inputblock.cn.push({
14691                     tag :'span',
14692                     cls : 'input-group-addon',
14693                     html : this.after
14694                 });
14695             }
14696             
14697         };
14698         
14699         if (align ==='left' && this.fieldLabel.length) {
14700                 Roo.log("left and has label");
14701                 cfg.cn = [
14702                     
14703                     {
14704                         tag: 'label',
14705                         'for' :  id,
14706                         cls : 'control-label col-md-' + this.labelWidth,
14707                         html : this.fieldLabel
14708                         
14709                     },
14710                     {
14711                         cls : "col-md-" + (12 - this.labelWidth), 
14712                         cn: [
14713                             inputblock
14714                         ]
14715                     }
14716                     
14717                 ];
14718         } else if ( this.fieldLabel.length) {
14719                 Roo.log(" label");
14720                 cfg.cn = [
14721                    
14722                     {
14723                         tag: this.boxLabel ? 'span' : 'label',
14724                         'for': id,
14725                         cls: 'control-label box-input-label',
14726                         //cls : 'input-group-addon',
14727                         html : this.fieldLabel
14728                         
14729                     },
14730                     
14731                     inputblock
14732                     
14733                 ];
14734
14735         } else {
14736             
14737                 Roo.log(" no label && no align");
14738                 cfg.cn = [  inputblock ] ;
14739                 
14740                 
14741         };
14742          if(this.boxLabel){
14743             cfg.cn.push( {
14744                 tag: 'label',
14745                 'for': id,
14746                 cls: 'box-label',
14747                 html: this.boxLabel
14748                 
14749             });
14750         }
14751         
14752         
14753        
14754         return cfg;
14755         
14756     },
14757     
14758     /**
14759      * return the real input element.
14760      */
14761     inputEl: function ()
14762     {
14763         return this.el.select('input.roo-checkbox',true).first();
14764     },
14765     
14766     label: function()
14767     {
14768         return this.el.select('label.control-label',true).first();
14769     },
14770     
14771     initEvents : function()
14772     {
14773 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14774         
14775         this.inputEl().on('click', this.onClick,  this);
14776         
14777     },
14778     
14779     onClick : function()
14780     {   
14781         this.setChecked(!this.checked);
14782     },
14783     
14784     setChecked : function(state,suppressEvent)
14785     {
14786         this.checked = state;
14787         
14788         this.inputEl().dom.checked = state;
14789         
14790         if(suppressEvent !== true){
14791             this.fireEvent('check', this, state);
14792         }
14793         
14794         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14795         
14796     },
14797     
14798     setValue : function(v,suppressEvent)
14799     {
14800         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14801     }
14802     
14803 });
14804
14805  
14806 /*
14807  * - LGPL
14808  *
14809  * Radio
14810  * 
14811  */
14812
14813 /**
14814  * @class Roo.bootstrap.Radio
14815  * @extends Roo.bootstrap.CheckBox
14816  * Bootstrap Radio class
14817
14818  * @constructor
14819  * Create a new Radio
14820  * @param {Object} config The config object
14821  */
14822
14823 Roo.bootstrap.Radio = function(config){
14824     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14825    
14826 };
14827
14828 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
14829     
14830     inputType: 'radio',
14831     inputValue: '',
14832     valueOff: '',
14833     
14834     getAutoCreate : function()
14835     {
14836         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14837         
14838         var id = Roo.id();
14839         
14840         var cfg = {};
14841         
14842         cfg.cls = 'form-group radio' //input-group
14843         
14844         var input =  {
14845             tag: 'input',
14846             id : id,
14847             type : this.inputType,
14848             value : (!this.checked) ? this.valueOff : this.inputValue,
14849             cls : 'roo-radio',
14850             placeholder : this.placeholder || ''
14851             
14852         };
14853           if (this.weight) { // Validity check?
14854             cfg.cls += " radio-" + this.weight;
14855         }
14856         if (this.disabled) {
14857             input.disabled=true;
14858         }
14859         
14860         if(this.checked){
14861             input.checked = this.checked;
14862         }
14863         
14864         if (this.name) {
14865             input.name = this.name;
14866         }
14867         
14868         if (this.size) {
14869             input.cls += ' input-' + this.size;
14870         }
14871         
14872         var settings=this;
14873         ['xs','sm','md','lg'].map(function(size){
14874             if (settings[size]) {
14875                 cfg.cls += ' col-' + size + '-' + settings[size];
14876             }
14877         });
14878         
14879         var inputblock = input;
14880         
14881         if (this.before || this.after) {
14882             
14883             inputblock = {
14884                 cls : 'input-group',
14885                 cn :  [] 
14886             };
14887             if (this.before) {
14888                 inputblock.cn.push({
14889                     tag :'span',
14890                     cls : 'input-group-addon',
14891                     html : this.before
14892                 });
14893             }
14894             inputblock.cn.push(input);
14895             if (this.after) {
14896                 inputblock.cn.push({
14897                     tag :'span',
14898                     cls : 'input-group-addon',
14899                     html : this.after
14900                 });
14901             }
14902             
14903         };
14904         
14905         if (align ==='left' && this.fieldLabel.length) {
14906                 Roo.log("left and has label");
14907                 cfg.cn = [
14908                     
14909                     {
14910                         tag: 'label',
14911                         'for' :  id,
14912                         cls : 'control-label col-md-' + this.labelWidth,
14913                         html : this.fieldLabel
14914                         
14915                     },
14916                     {
14917                         cls : "col-md-" + (12 - this.labelWidth), 
14918                         cn: [
14919                             inputblock
14920                         ]
14921                     }
14922                     
14923                 ];
14924         } else if ( this.fieldLabel.length) {
14925                 Roo.log(" label");
14926                  cfg.cn = [
14927                    
14928                     {
14929                         tag: 'label',
14930                         'for': id,
14931                         cls: 'control-label box-input-label',
14932                         //cls : 'input-group-addon',
14933                         html : this.fieldLabel
14934                         
14935                     },
14936                     
14937                     inputblock
14938                     
14939                 ];
14940
14941         } else {
14942             
14943                    Roo.log(" no label && no align");
14944                 cfg.cn = [
14945                     
14946                         inputblock
14947                     
14948                 ];
14949                 
14950                 
14951         };
14952         
14953         if(this.boxLabel){
14954             cfg.cn.push({
14955                 tag: 'label',
14956                 'for': id,
14957                 cls: 'box-label',
14958                 html: this.boxLabel
14959             })
14960         }
14961         
14962         return cfg;
14963         
14964     },
14965     inputEl: function ()
14966     {
14967         return this.el.select('input.roo-radio',true).first();
14968     },
14969     onClick : function()
14970     {   
14971         this.setChecked(true);
14972     },
14973     
14974     setChecked : function(state,suppressEvent)
14975     {
14976         if(state){
14977             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14978                 v.dom.checked = false;
14979             });
14980         }
14981         
14982         this.checked = state;
14983         this.inputEl().dom.checked = state;
14984         
14985         if(suppressEvent !== true){
14986             this.fireEvent('check', this, state);
14987         }
14988         
14989         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14990         
14991     },
14992     
14993     getGroupValue : function()
14994     {
14995         var value = ''
14996         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14997             if(v.dom.checked == true){
14998                 value = v.dom.value;
14999             }
15000         });
15001         
15002         return value;
15003     },
15004     
15005     /**
15006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15007      * @return {Mixed} value The field value
15008      */
15009     getValue : function(){
15010         return this.getGroupValue();
15011     }
15012     
15013 });
15014
15015  
15016 //<script type="text/javascript">
15017
15018 /*
15019  * Based  Ext JS Library 1.1.1
15020  * Copyright(c) 2006-2007, Ext JS, LLC.
15021  * LGPL
15022  *
15023  */
15024  
15025 /**
15026  * @class Roo.HtmlEditorCore
15027  * @extends Roo.Component
15028  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15029  *
15030  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15031  */
15032
15033 Roo.HtmlEditorCore = function(config){
15034     
15035     
15036     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15037     this.addEvents({
15038         /**
15039          * @event initialize
15040          * Fires when the editor is fully initialized (including the iframe)
15041          * @param {Roo.HtmlEditorCore} this
15042          */
15043         initialize: true,
15044         /**
15045          * @event activate
15046          * Fires when the editor is first receives the focus. Any insertion must wait
15047          * until after this event.
15048          * @param {Roo.HtmlEditorCore} this
15049          */
15050         activate: true,
15051          /**
15052          * @event beforesync
15053          * Fires before the textarea is updated with content from the editor iframe. Return false
15054          * to cancel the sync.
15055          * @param {Roo.HtmlEditorCore} this
15056          * @param {String} html
15057          */
15058         beforesync: true,
15059          /**
15060          * @event beforepush
15061          * Fires before the iframe editor is updated with content from the textarea. Return false
15062          * to cancel the push.
15063          * @param {Roo.HtmlEditorCore} this
15064          * @param {String} html
15065          */
15066         beforepush: true,
15067          /**
15068          * @event sync
15069          * Fires when the textarea is updated with content from the editor iframe.
15070          * @param {Roo.HtmlEditorCore} this
15071          * @param {String} html
15072          */
15073         sync: true,
15074          /**
15075          * @event push
15076          * Fires when the iframe editor is updated with content from the textarea.
15077          * @param {Roo.HtmlEditorCore} this
15078          * @param {String} html
15079          */
15080         push: true,
15081         
15082         /**
15083          * @event editorevent
15084          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15085          * @param {Roo.HtmlEditorCore} this
15086          */
15087         editorevent: true
15088     });
15089      
15090 };
15091
15092
15093 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15094
15095
15096      /**
15097      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15098      */
15099     
15100     owner : false,
15101     
15102      /**
15103      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15104      *                        Roo.resizable.
15105      */
15106     resizable : false,
15107      /**
15108      * @cfg {Number} height (in pixels)
15109      */   
15110     height: 300,
15111    /**
15112      * @cfg {Number} width (in pixels)
15113      */   
15114     width: 500,
15115     
15116     /**
15117      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15118      * 
15119      */
15120     stylesheets: false,
15121     
15122     // id of frame..
15123     frameId: false,
15124     
15125     // private properties
15126     validationEvent : false,
15127     deferHeight: true,
15128     initialized : false,
15129     activated : false,
15130     sourceEditMode : false,
15131     onFocus : Roo.emptyFn,
15132     iframePad:3,
15133     hideMode:'offsets',
15134     
15135     clearUp: true,
15136     
15137      
15138     
15139
15140     /**
15141      * Protected method that will not generally be called directly. It
15142      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15143      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15144      */
15145     getDocMarkup : function(){
15146         // body styles..
15147         var st = '';
15148         Roo.log(this.stylesheets);
15149         
15150         // inherit styels from page...?? 
15151         if (this.stylesheets === false) {
15152             
15153             Roo.get(document.head).select('style').each(function(node) {
15154                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15155             });
15156             
15157             Roo.get(document.head).select('link').each(function(node) { 
15158                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15159             });
15160             
15161         } else if (!this.stylesheets.length) {
15162                 // simple..
15163                 st = '<style type="text/css">' +
15164                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15165                    '</style>';
15166         } else {
15167             Roo.each(this.stylesheets, function(s) {
15168                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15169             });
15170             
15171         }
15172         
15173         st +=  '<style type="text/css">' +
15174             'IMG { cursor: pointer } ' +
15175         '</style>';
15176
15177         
15178         return '<html><head>' + st  +
15179             //<style type="text/css">' +
15180             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15181             //'</style>' +
15182             ' </head><body class="roo-htmleditor-body"></body></html>';
15183     },
15184
15185     // private
15186     onRender : function(ct, position)
15187     {
15188         var _t = this;
15189         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15190         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15191         
15192         
15193         this.el.dom.style.border = '0 none';
15194         this.el.dom.setAttribute('tabIndex', -1);
15195         this.el.addClass('x-hidden hide');
15196         
15197         
15198         
15199         if(Roo.isIE){ // fix IE 1px bogus margin
15200             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15201         }
15202        
15203         
15204         this.frameId = Roo.id();
15205         
15206          
15207         
15208         var iframe = this.owner.wrap.createChild({
15209             tag: 'iframe',
15210             cls: 'form-control', // bootstrap..
15211             id: this.frameId,
15212             name: this.frameId,
15213             frameBorder : 'no',
15214             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15215         }, this.el
15216         );
15217         
15218         
15219         this.iframe = iframe.dom;
15220
15221          this.assignDocWin();
15222         
15223         this.doc.designMode = 'on';
15224        
15225         this.doc.open();
15226         this.doc.write(this.getDocMarkup());
15227         this.doc.close();
15228
15229         
15230         var task = { // must defer to wait for browser to be ready
15231             run : function(){
15232                 //console.log("run task?" + this.doc.readyState);
15233                 this.assignDocWin();
15234                 if(this.doc.body || this.doc.readyState == 'complete'){
15235                     try {
15236                         this.doc.designMode="on";
15237                     } catch (e) {
15238                         return;
15239                     }
15240                     Roo.TaskMgr.stop(task);
15241                     this.initEditor.defer(10, this);
15242                 }
15243             },
15244             interval : 10,
15245             duration: 10000,
15246             scope: this
15247         };
15248         Roo.TaskMgr.start(task);
15249
15250         
15251          
15252     },
15253
15254     // private
15255     onResize : function(w, h)
15256     {
15257          Roo.log('resize: ' +w + ',' + h );
15258         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15259         if(!this.iframe){
15260             return;
15261         }
15262         if(typeof w == 'number'){
15263             
15264             this.iframe.style.width = w + 'px';
15265         }
15266         if(typeof h == 'number'){
15267             
15268             this.iframe.style.height = h + 'px';
15269             if(this.doc){
15270                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15271             }
15272         }
15273         
15274     },
15275
15276     /**
15277      * Toggles the editor between standard and source edit mode.
15278      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15279      */
15280     toggleSourceEdit : function(sourceEditMode){
15281         
15282         this.sourceEditMode = sourceEditMode === true;
15283         
15284         if(this.sourceEditMode){
15285  
15286             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15287             
15288         }else{
15289             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15290             //this.iframe.className = '';
15291             this.deferFocus();
15292         }
15293         //this.setSize(this.owner.wrap.getSize());
15294         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15295     },
15296
15297     
15298   
15299
15300     /**
15301      * Protected method that will not generally be called directly. If you need/want
15302      * custom HTML cleanup, this is the method you should override.
15303      * @param {String} html The HTML to be cleaned
15304      * return {String} The cleaned HTML
15305      */
15306     cleanHtml : function(html){
15307         html = String(html);
15308         if(html.length > 5){
15309             if(Roo.isSafari){ // strip safari nonsense
15310                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15311             }
15312         }
15313         if(html == '&nbsp;'){
15314             html = '';
15315         }
15316         return html;
15317     },
15318
15319     /**
15320      * HTML Editor -> Textarea
15321      * Protected method that will not generally be called directly. Syncs the contents
15322      * of the editor iframe with the textarea.
15323      */
15324     syncValue : function(){
15325         if(this.initialized){
15326             var bd = (this.doc.body || this.doc.documentElement);
15327             //this.cleanUpPaste(); -- this is done else where and causes havoc..
15328             var html = bd.innerHTML;
15329             if(Roo.isSafari){
15330                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
15331                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
15332                 if(m && m[1]){
15333                     html = '<div style="'+m[0]+'">' + html + '</div>';
15334                 }
15335             }
15336             html = this.cleanHtml(html);
15337             // fix up the special chars.. normaly like back quotes in word...
15338             // however we do not want to do this with chinese..
15339             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
15340                 var cc = b.charCodeAt();
15341                 if (
15342                     (cc >= 0x4E00 && cc < 0xA000 ) ||
15343                     (cc >= 0x3400 && cc < 0x4E00 ) ||
15344                     (cc >= 0xf900 && cc < 0xfb00 )
15345                 ) {
15346                         return b;
15347                 }
15348                 return "&#"+cc+";" 
15349             });
15350             if(this.owner.fireEvent('beforesync', this, html) !== false){
15351                 this.el.dom.value = html;
15352                 this.owner.fireEvent('sync', this, html);
15353             }
15354         }
15355     },
15356
15357     /**
15358      * Protected method that will not generally be called directly. Pushes the value of the textarea
15359      * into the iframe editor.
15360      */
15361     pushValue : function(){
15362         if(this.initialized){
15363             var v = this.el.dom.value.trim();
15364             
15365 //            if(v.length < 1){
15366 //                v = '&#160;';
15367 //            }
15368             
15369             if(this.owner.fireEvent('beforepush', this, v) !== false){
15370                 var d = (this.doc.body || this.doc.documentElement);
15371                 d.innerHTML = v;
15372                 this.cleanUpPaste();
15373                 this.el.dom.value = d.innerHTML;
15374                 this.owner.fireEvent('push', this, v);
15375             }
15376         }
15377     },
15378
15379     // private
15380     deferFocus : function(){
15381         this.focus.defer(10, this);
15382     },
15383
15384     // doc'ed in Field
15385     focus : function(){
15386         if(this.win && !this.sourceEditMode){
15387             this.win.focus();
15388         }else{
15389             this.el.focus();
15390         }
15391     },
15392     
15393     assignDocWin: function()
15394     {
15395         var iframe = this.iframe;
15396         
15397          if(Roo.isIE){
15398             this.doc = iframe.contentWindow.document;
15399             this.win = iframe.contentWindow;
15400         } else {
15401             if (!Roo.get(this.frameId)) {
15402                 return;
15403             }
15404             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
15405             this.win = Roo.get(this.frameId).dom.contentWindow;
15406         }
15407     },
15408     
15409     // private
15410     initEditor : function(){
15411         //console.log("INIT EDITOR");
15412         this.assignDocWin();
15413         
15414         
15415         
15416         this.doc.designMode="on";
15417         this.doc.open();
15418         this.doc.write(this.getDocMarkup());
15419         this.doc.close();
15420         
15421         var dbody = (this.doc.body || this.doc.documentElement);
15422         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
15423         // this copies styles from the containing element into thsi one..
15424         // not sure why we need all of this..
15425         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
15426         
15427         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
15428         //ss['background-attachment'] = 'fixed'; // w3c
15429         dbody.bgProperties = 'fixed'; // ie
15430         //Roo.DomHelper.applyStyles(dbody, ss);
15431         Roo.EventManager.on(this.doc, {
15432             //'mousedown': this.onEditorEvent,
15433             'mouseup': this.onEditorEvent,
15434             'dblclick': this.onEditorEvent,
15435             'click': this.onEditorEvent,
15436             'keyup': this.onEditorEvent,
15437             buffer:100,
15438             scope: this
15439         });
15440         if(Roo.isGecko){
15441             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
15442         }
15443         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
15444             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
15445         }
15446         this.initialized = true;
15447
15448         this.owner.fireEvent('initialize', this);
15449         this.pushValue();
15450     },
15451
15452     // private
15453     onDestroy : function(){
15454         
15455         
15456         
15457         if(this.rendered){
15458             
15459             //for (var i =0; i < this.toolbars.length;i++) {
15460             //    // fixme - ask toolbars for heights?
15461             //    this.toolbars[i].onDestroy();
15462            // }
15463             
15464             //this.wrap.dom.innerHTML = '';
15465             //this.wrap.remove();
15466         }
15467     },
15468
15469     // private
15470     onFirstFocus : function(){
15471         
15472         this.assignDocWin();
15473         
15474         
15475         this.activated = true;
15476          
15477     
15478         if(Roo.isGecko){ // prevent silly gecko errors
15479             this.win.focus();
15480             var s = this.win.getSelection();
15481             if(!s.focusNode || s.focusNode.nodeType != 3){
15482                 var r = s.getRangeAt(0);
15483                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
15484                 r.collapse(true);
15485                 this.deferFocus();
15486             }
15487             try{
15488                 this.execCmd('useCSS', true);
15489                 this.execCmd('styleWithCSS', false);
15490             }catch(e){}
15491         }
15492         this.owner.fireEvent('activate', this);
15493     },
15494
15495     // private
15496     adjustFont: function(btn){
15497         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
15498         //if(Roo.isSafari){ // safari
15499         //    adjust *= 2;
15500        // }
15501         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
15502         if(Roo.isSafari){ // safari
15503             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
15504             v =  (v < 10) ? 10 : v;
15505             v =  (v > 48) ? 48 : v;
15506             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
15507             
15508         }
15509         
15510         
15511         v = Math.max(1, v+adjust);
15512         
15513         this.execCmd('FontSize', v  );
15514     },
15515
15516     onEditorEvent : function(e){
15517         this.owner.fireEvent('editorevent', this, e);
15518       //  this.updateToolbar();
15519         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
15520     },
15521
15522     insertTag : function(tg)
15523     {
15524         // could be a bit smarter... -> wrap the current selected tRoo..
15525         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
15526             
15527             range = this.createRange(this.getSelection());
15528             var wrappingNode = this.doc.createElement(tg.toLowerCase());
15529             wrappingNode.appendChild(range.extractContents());
15530             range.insertNode(wrappingNode);
15531
15532             return;
15533             
15534             
15535             
15536         }
15537         this.execCmd("formatblock",   tg);
15538         
15539     },
15540     
15541     insertText : function(txt)
15542     {
15543         
15544         
15545         var range = this.createRange();
15546         range.deleteContents();
15547                //alert(Sender.getAttribute('label'));
15548                
15549         range.insertNode(this.doc.createTextNode(txt));
15550     } ,
15551     
15552      
15553
15554     /**
15555      * Executes a Midas editor command on the editor document and performs necessary focus and
15556      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
15557      * @param {String} cmd The Midas command
15558      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15559      */
15560     relayCmd : function(cmd, value){
15561         this.win.focus();
15562         this.execCmd(cmd, value);
15563         this.owner.fireEvent('editorevent', this);
15564         //this.updateToolbar();
15565         this.owner.deferFocus();
15566     },
15567
15568     /**
15569      * Executes a Midas editor command directly on the editor document.
15570      * For visual commands, you should use {@link #relayCmd} instead.
15571      * <b>This should only be called after the editor is initialized.</b>
15572      * @param {String} cmd The Midas command
15573      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
15574      */
15575     execCmd : function(cmd, value){
15576         this.doc.execCommand(cmd, false, value === undefined ? null : value);
15577         this.syncValue();
15578     },
15579  
15580  
15581    
15582     /**
15583      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
15584      * to insert tRoo.
15585      * @param {String} text | dom node.. 
15586      */
15587     insertAtCursor : function(text)
15588     {
15589         
15590         
15591         
15592         if(!this.activated){
15593             return;
15594         }
15595         /*
15596         if(Roo.isIE){
15597             this.win.focus();
15598             var r = this.doc.selection.createRange();
15599             if(r){
15600                 r.collapse(true);
15601                 r.pasteHTML(text);
15602                 this.syncValue();
15603                 this.deferFocus();
15604             
15605             }
15606             return;
15607         }
15608         */
15609         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
15610             this.win.focus();
15611             
15612             
15613             // from jquery ui (MIT licenced)
15614             var range, node;
15615             var win = this.win;
15616             
15617             if (win.getSelection && win.getSelection().getRangeAt) {
15618                 range = win.getSelection().getRangeAt(0);
15619                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
15620                 range.insertNode(node);
15621             } else if (win.document.selection && win.document.selection.createRange) {
15622                 // no firefox support
15623                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15624                 win.document.selection.createRange().pasteHTML(txt);
15625             } else {
15626                 // no firefox support
15627                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
15628                 this.execCmd('InsertHTML', txt);
15629             } 
15630             
15631             this.syncValue();
15632             
15633             this.deferFocus();
15634         }
15635     },
15636  // private
15637     mozKeyPress : function(e){
15638         if(e.ctrlKey){
15639             var c = e.getCharCode(), cmd;
15640           
15641             if(c > 0){
15642                 c = String.fromCharCode(c).toLowerCase();
15643                 switch(c){
15644                     case 'b':
15645                         cmd = 'bold';
15646                         break;
15647                     case 'i':
15648                         cmd = 'italic';
15649                         break;
15650                     
15651                     case 'u':
15652                         cmd = 'underline';
15653                         break;
15654                     
15655                     case 'v':
15656                         this.cleanUpPaste.defer(100, this);
15657                         return;
15658                         
15659                 }
15660                 if(cmd){
15661                     this.win.focus();
15662                     this.execCmd(cmd);
15663                     this.deferFocus();
15664                     e.preventDefault();
15665                 }
15666                 
15667             }
15668         }
15669     },
15670
15671     // private
15672     fixKeys : function(){ // load time branching for fastest keydown performance
15673         if(Roo.isIE){
15674             return function(e){
15675                 var k = e.getKey(), r;
15676                 if(k == e.TAB){
15677                     e.stopEvent();
15678                     r = this.doc.selection.createRange();
15679                     if(r){
15680                         r.collapse(true);
15681                         r.pasteHTML('&#160;&#160;&#160;&#160;');
15682                         this.deferFocus();
15683                     }
15684                     return;
15685                 }
15686                 
15687                 if(k == e.ENTER){
15688                     r = this.doc.selection.createRange();
15689                     if(r){
15690                         var target = r.parentElement();
15691                         if(!target || target.tagName.toLowerCase() != 'li'){
15692                             e.stopEvent();
15693                             r.pasteHTML('<br />');
15694                             r.collapse(false);
15695                             r.select();
15696                         }
15697                     }
15698                 }
15699                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15700                     this.cleanUpPaste.defer(100, this);
15701                     return;
15702                 }
15703                 
15704                 
15705             };
15706         }else if(Roo.isOpera){
15707             return function(e){
15708                 var k = e.getKey();
15709                 if(k == e.TAB){
15710                     e.stopEvent();
15711                     this.win.focus();
15712                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
15713                     this.deferFocus();
15714                 }
15715                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15716                     this.cleanUpPaste.defer(100, this);
15717                     return;
15718                 }
15719                 
15720             };
15721         }else if(Roo.isSafari){
15722             return function(e){
15723                 var k = e.getKey();
15724                 
15725                 if(k == e.TAB){
15726                     e.stopEvent();
15727                     this.execCmd('InsertText','\t');
15728                     this.deferFocus();
15729                     return;
15730                 }
15731                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15732                     this.cleanUpPaste.defer(100, this);
15733                     return;
15734                 }
15735                 
15736              };
15737         }
15738     }(),
15739     
15740     getAllAncestors: function()
15741     {
15742         var p = this.getSelectedNode();
15743         var a = [];
15744         if (!p) {
15745             a.push(p); // push blank onto stack..
15746             p = this.getParentElement();
15747         }
15748         
15749         
15750         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15751             a.push(p);
15752             p = p.parentNode;
15753         }
15754         a.push(this.doc.body);
15755         return a;
15756     },
15757     lastSel : false,
15758     lastSelNode : false,
15759     
15760     
15761     getSelection : function() 
15762     {
15763         this.assignDocWin();
15764         return Roo.isIE ? this.doc.selection : this.win.getSelection();
15765     },
15766     
15767     getSelectedNode: function() 
15768     {
15769         // this may only work on Gecko!!!
15770         
15771         // should we cache this!!!!
15772         
15773         
15774         
15775          
15776         var range = this.createRange(this.getSelection()).cloneRange();
15777         
15778         if (Roo.isIE) {
15779             var parent = range.parentElement();
15780             while (true) {
15781                 var testRange = range.duplicate();
15782                 testRange.moveToElementText(parent);
15783                 if (testRange.inRange(range)) {
15784                     break;
15785                 }
15786                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15787                     break;
15788                 }
15789                 parent = parent.parentElement;
15790             }
15791             return parent;
15792         }
15793         
15794         // is ancestor a text element.
15795         var ac =  range.commonAncestorContainer;
15796         if (ac.nodeType == 3) {
15797             ac = ac.parentNode;
15798         }
15799         
15800         var ar = ac.childNodes;
15801          
15802         var nodes = [];
15803         var other_nodes = [];
15804         var has_other_nodes = false;
15805         for (var i=0;i<ar.length;i++) {
15806             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
15807                 continue;
15808             }
15809             // fullly contained node.
15810             
15811             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15812                 nodes.push(ar[i]);
15813                 continue;
15814             }
15815             
15816             // probably selected..
15817             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15818                 other_nodes.push(ar[i]);
15819                 continue;
15820             }
15821             // outer..
15822             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
15823                 continue;
15824             }
15825             
15826             
15827             has_other_nodes = true;
15828         }
15829         if (!nodes.length && other_nodes.length) {
15830             nodes= other_nodes;
15831         }
15832         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15833             return false;
15834         }
15835         
15836         return nodes[0];
15837     },
15838     createRange: function(sel)
15839     {
15840         // this has strange effects when using with 
15841         // top toolbar - not sure if it's a great idea.
15842         //this.editor.contentWindow.focus();
15843         if (typeof sel != "undefined") {
15844             try {
15845                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15846             } catch(e) {
15847                 return this.doc.createRange();
15848             }
15849         } else {
15850             return this.doc.createRange();
15851         }
15852     },
15853     getParentElement: function()
15854     {
15855         
15856         this.assignDocWin();
15857         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15858         
15859         var range = this.createRange(sel);
15860          
15861         try {
15862             var p = range.commonAncestorContainer;
15863             while (p.nodeType == 3) { // text node
15864                 p = p.parentNode;
15865             }
15866             return p;
15867         } catch (e) {
15868             return null;
15869         }
15870     
15871     },
15872     /***
15873      *
15874      * Range intersection.. the hard stuff...
15875      *  '-1' = before
15876      *  '0' = hits..
15877      *  '1' = after.
15878      *         [ -- selected range --- ]
15879      *   [fail]                        [fail]
15880      *
15881      *    basically..
15882      *      if end is before start or  hits it. fail.
15883      *      if start is after end or hits it fail.
15884      *
15885      *   if either hits (but other is outside. - then it's not 
15886      *   
15887      *    
15888      **/
15889     
15890     
15891     // @see http://www.thismuchiknow.co.uk/?p=64.
15892     rangeIntersectsNode : function(range, node)
15893     {
15894         var nodeRange = node.ownerDocument.createRange();
15895         try {
15896             nodeRange.selectNode(node);
15897         } catch (e) {
15898             nodeRange.selectNodeContents(node);
15899         }
15900     
15901         var rangeStartRange = range.cloneRange();
15902         rangeStartRange.collapse(true);
15903     
15904         var rangeEndRange = range.cloneRange();
15905         rangeEndRange.collapse(false);
15906     
15907         var nodeStartRange = nodeRange.cloneRange();
15908         nodeStartRange.collapse(true);
15909     
15910         var nodeEndRange = nodeRange.cloneRange();
15911         nodeEndRange.collapse(false);
15912     
15913         return rangeStartRange.compareBoundaryPoints(
15914                  Range.START_TO_START, nodeEndRange) == -1 &&
15915                rangeEndRange.compareBoundaryPoints(
15916                  Range.START_TO_START, nodeStartRange) == 1;
15917         
15918          
15919     },
15920     rangeCompareNode : function(range, node)
15921     {
15922         var nodeRange = node.ownerDocument.createRange();
15923         try {
15924             nodeRange.selectNode(node);
15925         } catch (e) {
15926             nodeRange.selectNodeContents(node);
15927         }
15928         
15929         
15930         range.collapse(true);
15931     
15932         nodeRange.collapse(true);
15933      
15934         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15935         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
15936          
15937         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15938         
15939         var nodeIsBefore   =  ss == 1;
15940         var nodeIsAfter    = ee == -1;
15941         
15942         if (nodeIsBefore && nodeIsAfter)
15943             return 0; // outer
15944         if (!nodeIsBefore && nodeIsAfter)
15945             return 1; //right trailed.
15946         
15947         if (nodeIsBefore && !nodeIsAfter)
15948             return 2;  // left trailed.
15949         // fully contined.
15950         return 3;
15951     },
15952
15953     // private? - in a new class?
15954     cleanUpPaste :  function()
15955     {
15956         // cleans up the whole document..
15957         Roo.log('cleanuppaste');
15958         
15959         this.cleanUpChildren(this.doc.body);
15960         var clean = this.cleanWordChars(this.doc.body.innerHTML);
15961         if (clean != this.doc.body.innerHTML) {
15962             this.doc.body.innerHTML = clean;
15963         }
15964         
15965     },
15966     
15967     cleanWordChars : function(input) {// change the chars to hex code
15968         var he = Roo.HtmlEditorCore;
15969         
15970         var output = input;
15971         Roo.each(he.swapCodes, function(sw) { 
15972             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15973             
15974             output = output.replace(swapper, sw[1]);
15975         });
15976         
15977         return output;
15978     },
15979     
15980     
15981     cleanUpChildren : function (n)
15982     {
15983         if (!n.childNodes.length) {
15984             return;
15985         }
15986         for (var i = n.childNodes.length-1; i > -1 ; i--) {
15987            this.cleanUpChild(n.childNodes[i]);
15988         }
15989     },
15990     
15991     
15992         
15993     
15994     cleanUpChild : function (node)
15995     {
15996         var ed = this;
15997         //console.log(node);
15998         if (node.nodeName == "#text") {
15999             // clean up silly Windows -- stuff?
16000             return; 
16001         }
16002         if (node.nodeName == "#comment") {
16003             node.parentNode.removeChild(node);
16004             // clean up silly Windows -- stuff?
16005             return; 
16006         }
16007         
16008         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16009             // remove node.
16010             node.parentNode.removeChild(node);
16011             return;
16012             
16013         }
16014         
16015         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16016         
16017         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16018         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16019         
16020         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16021         //    remove_keep_children = true;
16022         //}
16023         
16024         if (remove_keep_children) {
16025             this.cleanUpChildren(node);
16026             // inserts everything just before this node...
16027             while (node.childNodes.length) {
16028                 var cn = node.childNodes[0];
16029                 node.removeChild(cn);
16030                 node.parentNode.insertBefore(cn, node);
16031             }
16032             node.parentNode.removeChild(node);
16033             return;
16034         }
16035         
16036         if (!node.attributes || !node.attributes.length) {
16037             this.cleanUpChildren(node);
16038             return;
16039         }
16040         
16041         function cleanAttr(n,v)
16042         {
16043             
16044             if (v.match(/^\./) || v.match(/^\//)) {
16045                 return;
16046             }
16047             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16048                 return;
16049             }
16050             if (v.match(/^#/)) {
16051                 return;
16052             }
16053 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16054             node.removeAttribute(n);
16055             
16056         }
16057         
16058         function cleanStyle(n,v)
16059         {
16060             if (v.match(/expression/)) { //XSS?? should we even bother..
16061                 node.removeAttribute(n);
16062                 return;
16063             }
16064             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16065             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16066             
16067             
16068             var parts = v.split(/;/);
16069             var clean = [];
16070             
16071             Roo.each(parts, function(p) {
16072                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16073                 if (!p.length) {
16074                     return true;
16075                 }
16076                 var l = p.split(':').shift().replace(/\s+/g,'');
16077                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16078                 
16079                 if ( cblack.indexOf(l) > -1) {
16080 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16081                     //node.removeAttribute(n);
16082                     return true;
16083                 }
16084                 //Roo.log()
16085                 // only allow 'c whitelisted system attributes'
16086                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16087 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16088                     //node.removeAttribute(n);
16089                     return true;
16090                 }
16091                 
16092                 
16093                  
16094                 
16095                 clean.push(p);
16096                 return true;
16097             });
16098             if (clean.length) { 
16099                 node.setAttribute(n, clean.join(';'));
16100             } else {
16101                 node.removeAttribute(n);
16102             }
16103             
16104         }
16105         
16106         
16107         for (var i = node.attributes.length-1; i > -1 ; i--) {
16108             var a = node.attributes[i];
16109             //console.log(a);
16110             
16111             if (a.name.toLowerCase().substr(0,2)=='on')  {
16112                 node.removeAttribute(a.name);
16113                 continue;
16114             }
16115             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16116                 node.removeAttribute(a.name);
16117                 continue;
16118             }
16119             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16120                 cleanAttr(a.name,a.value); // fixme..
16121                 continue;
16122             }
16123             if (a.name == 'style') {
16124                 cleanStyle(a.name,a.value);
16125                 continue;
16126             }
16127             /// clean up MS crap..
16128             // tecnically this should be a list of valid class'es..
16129             
16130             
16131             if (a.name == 'class') {
16132                 if (a.value.match(/^Mso/)) {
16133                     node.className = '';
16134                 }
16135                 
16136                 if (a.value.match(/body/)) {
16137                     node.className = '';
16138                 }
16139                 continue;
16140             }
16141             
16142             // style cleanup!?
16143             // class cleanup?
16144             
16145         }
16146         
16147         
16148         this.cleanUpChildren(node);
16149         
16150         
16151     },
16152     /**
16153      * Clean up MS wordisms...
16154      */
16155     cleanWord : function(node)
16156     {
16157         var _t = this;
16158         var cleanWordChildren = function()
16159         {
16160             if (!node.childNodes.length) {
16161                 return;
16162             }
16163             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16164                _t.cleanWord(node.childNodes[i]);
16165             }
16166         }
16167         
16168         
16169         if (!node) {
16170             this.cleanWord(this.doc.body);
16171             return;
16172         }
16173         if (node.nodeName == "#text") {
16174             // clean up silly Windows -- stuff?
16175             return; 
16176         }
16177         if (node.nodeName == "#comment") {
16178             node.parentNode.removeChild(node);
16179             // clean up silly Windows -- stuff?
16180             return; 
16181         }
16182         
16183         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16184             node.parentNode.removeChild(node);
16185             return;
16186         }
16187         
16188         // remove - but keep children..
16189         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16190             while (node.childNodes.length) {
16191                 var cn = node.childNodes[0];
16192                 node.removeChild(cn);
16193                 node.parentNode.insertBefore(cn, node);
16194             }
16195             node.parentNode.removeChild(node);
16196             cleanWordChildren();
16197             return;
16198         }
16199         // clean styles
16200         if (node.className.length) {
16201             
16202             var cn = node.className.split(/\W+/);
16203             var cna = [];
16204             Roo.each(cn, function(cls) {
16205                 if (cls.match(/Mso[a-zA-Z]+/)) {
16206                     return;
16207                 }
16208                 cna.push(cls);
16209             });
16210             node.className = cna.length ? cna.join(' ') : '';
16211             if (!cna.length) {
16212                 node.removeAttribute("class");
16213             }
16214         }
16215         
16216         if (node.hasAttribute("lang")) {
16217             node.removeAttribute("lang");
16218         }
16219         
16220         if (node.hasAttribute("style")) {
16221             
16222             var styles = node.getAttribute("style").split(";");
16223             var nstyle = [];
16224             Roo.each(styles, function(s) {
16225                 if (!s.match(/:/)) {
16226                     return;
16227                 }
16228                 var kv = s.split(":");
16229                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16230                     return;
16231                 }
16232                 // what ever is left... we allow.
16233                 nstyle.push(s);
16234             });
16235             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16236             if (!nstyle.length) {
16237                 node.removeAttribute('style');
16238             }
16239         }
16240         
16241         cleanWordChildren();
16242         
16243         
16244     },
16245     domToHTML : function(currentElement, depth, nopadtext) {
16246         
16247             depth = depth || 0;
16248             nopadtext = nopadtext || false;
16249         
16250             if (!currentElement) {
16251                 return this.domToHTML(this.doc.body);
16252             }
16253             
16254             //Roo.log(currentElement);
16255             var j;
16256             var allText = false;
16257             var nodeName = currentElement.nodeName;
16258             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16259             
16260             if  (nodeName == '#text') {
16261                 return currentElement.nodeValue;
16262             }
16263             
16264             
16265             var ret = '';
16266             if (nodeName != 'BODY') {
16267                  
16268                 var i = 0;
16269                 // Prints the node tagName, such as <A>, <IMG>, etc
16270                 if (tagName) {
16271                     var attr = [];
16272                     for(i = 0; i < currentElement.attributes.length;i++) {
16273                         // quoting?
16274                         var aname = currentElement.attributes.item(i).name;
16275                         if (!currentElement.attributes.item(i).value.length) {
16276                             continue;
16277                         }
16278                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16279                     }
16280                     
16281                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16282                 } 
16283                 else {
16284                     
16285                     // eack
16286                 }
16287             } else {
16288                 tagName = false;
16289             }
16290             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16291                 return ret;
16292             }
16293             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16294                 nopadtext = true;
16295             }
16296             
16297             
16298             // Traverse the tree
16299             i = 0;
16300             var currentElementChild = currentElement.childNodes.item(i);
16301             var allText = true;
16302             var innerHTML  = '';
16303             lastnode = '';
16304             while (currentElementChild) {
16305                 // Formatting code (indent the tree so it looks nice on the screen)
16306                 var nopad = nopadtext;
16307                 if (lastnode == 'SPAN') {
16308                     nopad  = true;
16309                 }
16310                 // text
16311                 if  (currentElementChild.nodeName == '#text') {
16312                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16313                     if (!nopad && toadd.length > 80) {
16314                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16315                     }
16316                     innerHTML  += toadd;
16317                     
16318                     i++;
16319                     currentElementChild = currentElement.childNodes.item(i);
16320                     lastNode = '';
16321                     continue;
16322                 }
16323                 allText = false;
16324                 
16325                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
16326                     
16327                 // Recursively traverse the tree structure of the child node
16328                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
16329                 lastnode = currentElementChild.nodeName;
16330                 i++;
16331                 currentElementChild=currentElement.childNodes.item(i);
16332             }
16333             
16334             ret += innerHTML;
16335             
16336             if (!allText) {
16337                     // The remaining code is mostly for formatting the tree
16338                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
16339             }
16340             
16341             
16342             if (tagName) {
16343                 ret+= "</"+tagName+">";
16344             }
16345             return ret;
16346             
16347         }
16348     
16349     // hide stuff that is not compatible
16350     /**
16351      * @event blur
16352      * @hide
16353      */
16354     /**
16355      * @event change
16356      * @hide
16357      */
16358     /**
16359      * @event focus
16360      * @hide
16361      */
16362     /**
16363      * @event specialkey
16364      * @hide
16365      */
16366     /**
16367      * @cfg {String} fieldClass @hide
16368      */
16369     /**
16370      * @cfg {String} focusClass @hide
16371      */
16372     /**
16373      * @cfg {String} autoCreate @hide
16374      */
16375     /**
16376      * @cfg {String} inputType @hide
16377      */
16378     /**
16379      * @cfg {String} invalidClass @hide
16380      */
16381     /**
16382      * @cfg {String} invalidText @hide
16383      */
16384     /**
16385      * @cfg {String} msgFx @hide
16386      */
16387     /**
16388      * @cfg {String} validateOnBlur @hide
16389      */
16390 });
16391
16392 Roo.HtmlEditorCore.white = [
16393         'area', 'br', 'img', 'input', 'hr', 'wbr',
16394         
16395        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
16396        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
16397        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
16398        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
16399        'table',   'ul',         'xmp', 
16400        
16401        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
16402       'thead',   'tr', 
16403      
16404       'dir', 'menu', 'ol', 'ul', 'dl',
16405        
16406       'embed',  'object'
16407 ];
16408
16409
16410 Roo.HtmlEditorCore.black = [
16411     //    'embed',  'object', // enable - backend responsiblity to clean thiese
16412         'applet', // 
16413         'base',   'basefont', 'bgsound', 'blink',  'body', 
16414         'frame',  'frameset', 'head',    'html',   'ilayer', 
16415         'iframe', 'layer',  'link',     'meta',    'object',   
16416         'script', 'style' ,'title',  'xml' // clean later..
16417 ];
16418 Roo.HtmlEditorCore.clean = [
16419     'script', 'style', 'title', 'xml'
16420 ];
16421 Roo.HtmlEditorCore.remove = [
16422     'font'
16423 ];
16424 // attributes..
16425
16426 Roo.HtmlEditorCore.ablack = [
16427     'on'
16428 ];
16429     
16430 Roo.HtmlEditorCore.aclean = [ 
16431     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
16432 ];
16433
16434 // protocols..
16435 Roo.HtmlEditorCore.pwhite= [
16436         'http',  'https',  'mailto'
16437 ];
16438
16439 // white listed style attributes.
16440 Roo.HtmlEditorCore.cwhite= [
16441       //  'text-align', /// default is to allow most things..
16442       
16443          
16444 //        'font-size'//??
16445 ];
16446
16447 // black listed style attributes.
16448 Roo.HtmlEditorCore.cblack= [
16449       //  'font-size' -- this can be set by the project 
16450 ];
16451
16452
16453 Roo.HtmlEditorCore.swapCodes   =[ 
16454     [    8211, "--" ], 
16455     [    8212, "--" ], 
16456     [    8216,  "'" ],  
16457     [    8217, "'" ],  
16458     [    8220, '"' ],  
16459     [    8221, '"' ],  
16460     [    8226, "*" ],  
16461     [    8230, "..." ]
16462 ]; 
16463
16464     /*
16465  * - LGPL
16466  *
16467  * HtmlEditor
16468  * 
16469  */
16470
16471 /**
16472  * @class Roo.bootstrap.HtmlEditor
16473  * @extends Roo.bootstrap.TextArea
16474  * Bootstrap HtmlEditor class
16475
16476  * @constructor
16477  * Create a new HtmlEditor
16478  * @param {Object} config The config object
16479  */
16480
16481 Roo.bootstrap.HtmlEditor = function(config){
16482     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
16483     if (!this.toolbars) {
16484         this.toolbars = [];
16485     }
16486     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
16487     this.addEvents({
16488             /**
16489              * @event initialize
16490              * Fires when the editor is fully initialized (including the iframe)
16491              * @param {HtmlEditor} this
16492              */
16493             initialize: true,
16494             /**
16495              * @event activate
16496              * Fires when the editor is first receives the focus. Any insertion must wait
16497              * until after this event.
16498              * @param {HtmlEditor} this
16499              */
16500             activate: true,
16501              /**
16502              * @event beforesync
16503              * Fires before the textarea is updated with content from the editor iframe. Return false
16504              * to cancel the sync.
16505              * @param {HtmlEditor} this
16506              * @param {String} html
16507              */
16508             beforesync: true,
16509              /**
16510              * @event beforepush
16511              * Fires before the iframe editor is updated with content from the textarea. Return false
16512              * to cancel the push.
16513              * @param {HtmlEditor} this
16514              * @param {String} html
16515              */
16516             beforepush: true,
16517              /**
16518              * @event sync
16519              * Fires when the textarea is updated with content from the editor iframe.
16520              * @param {HtmlEditor} this
16521              * @param {String} html
16522              */
16523             sync: true,
16524              /**
16525              * @event push
16526              * Fires when the iframe editor is updated with content from the textarea.
16527              * @param {HtmlEditor} this
16528              * @param {String} html
16529              */
16530             push: true,
16531              /**
16532              * @event editmodechange
16533              * Fires when the editor switches edit modes
16534              * @param {HtmlEditor} this
16535              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
16536              */
16537             editmodechange: true,
16538             /**
16539              * @event editorevent
16540              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16541              * @param {HtmlEditor} this
16542              */
16543             editorevent: true,
16544             /**
16545              * @event firstfocus
16546              * Fires when on first focus - needed by toolbars..
16547              * @param {HtmlEditor} this
16548              */
16549             firstfocus: true,
16550             /**
16551              * @event autosave
16552              * Auto save the htmlEditor value as a file into Events
16553              * @param {HtmlEditor} this
16554              */
16555             autosave: true,
16556             /**
16557              * @event savedpreview
16558              * preview the saved version of htmlEditor
16559              * @param {HtmlEditor} this
16560              */
16561             savedpreview: true
16562         });
16563 };
16564
16565
16566 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
16567     
16568     
16569       /**
16570      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
16571      */
16572     toolbars : false,
16573    
16574      /**
16575      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16576      *                        Roo.resizable.
16577      */
16578     resizable : false,
16579      /**
16580      * @cfg {Number} height (in pixels)
16581      */   
16582     height: 300,
16583    /**
16584      * @cfg {Number} width (in pixels)
16585      */   
16586     width: false,
16587     
16588     /**
16589      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16590      * 
16591      */
16592     stylesheets: false,
16593     
16594     // id of frame..
16595     frameId: false,
16596     
16597     // private properties
16598     validationEvent : false,
16599     deferHeight: true,
16600     initialized : false,
16601     activated : false,
16602     
16603     onFocus : Roo.emptyFn,
16604     iframePad:3,
16605     hideMode:'offsets',
16606     
16607     
16608     tbContainer : false,
16609     
16610     toolbarContainer :function() {
16611         return this.wrap.select('.x-html-editor-tb',true).first();
16612     },
16613
16614     /**
16615      * Protected method that will not generally be called directly. It
16616      * is called when the editor creates its toolbar. Override this method if you need to
16617      * add custom toolbar buttons.
16618      * @param {HtmlEditor} editor
16619      */
16620     createToolbar : function(){
16621         
16622         Roo.log("create toolbars");
16623         
16624         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
16625         this.toolbars[0].render(this.toolbarContainer());
16626         
16627         return;
16628         
16629 //        if (!editor.toolbars || !editor.toolbars.length) {
16630 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
16631 //        }
16632 //        
16633 //        for (var i =0 ; i < editor.toolbars.length;i++) {
16634 //            editor.toolbars[i] = Roo.factory(
16635 //                    typeof(editor.toolbars[i]) == 'string' ?
16636 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
16637 //                Roo.bootstrap.HtmlEditor);
16638 //            editor.toolbars[i].init(editor);
16639 //        }
16640     },
16641
16642      
16643     // private
16644     onRender : function(ct, position)
16645     {
16646        // Roo.log("Call onRender: " + this.xtype);
16647         var _t = this;
16648         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
16649       
16650         this.wrap = this.inputEl().wrap({
16651             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
16652         });
16653         
16654         this.editorcore.onRender(ct, position);
16655          
16656         if (this.resizable) {
16657             this.resizeEl = new Roo.Resizable(this.wrap, {
16658                 pinned : true,
16659                 wrap: true,
16660                 dynamic : true,
16661                 minHeight : this.height,
16662                 height: this.height,
16663                 handles : this.resizable,
16664                 width: this.width,
16665                 listeners : {
16666                     resize : function(r, w, h) {
16667                         _t.onResize(w,h); // -something
16668                     }
16669                 }
16670             });
16671             
16672         }
16673         this.createToolbar(this);
16674        
16675         
16676         if(!this.width && this.resizable){
16677             this.setSize(this.wrap.getSize());
16678         }
16679         if (this.resizeEl) {
16680             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
16681             // should trigger onReize..
16682         }
16683         
16684     },
16685
16686     // private
16687     onResize : function(w, h)
16688     {
16689         Roo.log('resize: ' +w + ',' + h );
16690         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
16691         var ew = false;
16692         var eh = false;
16693         
16694         if(this.inputEl() ){
16695             if(typeof w == 'number'){
16696                 var aw = w - this.wrap.getFrameWidth('lr');
16697                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
16698                 ew = aw;
16699             }
16700             if(typeof h == 'number'){
16701                  var tbh = -11;  // fixme it needs to tool bar size!
16702                 for (var i =0; i < this.toolbars.length;i++) {
16703                     // fixme - ask toolbars for heights?
16704                     tbh += this.toolbars[i].el.getHeight();
16705                     //if (this.toolbars[i].footer) {
16706                     //    tbh += this.toolbars[i].footer.el.getHeight();
16707                     //}
16708                 }
16709               
16710                 
16711                 
16712                 
16713                 
16714                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16715                 ah -= 5; // knock a few pixes off for look..
16716                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16717                 var eh = ah;
16718             }
16719         }
16720         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16721         this.editorcore.onResize(ew,eh);
16722         
16723     },
16724
16725     /**
16726      * Toggles the editor between standard and source edit mode.
16727      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16728      */
16729     toggleSourceEdit : function(sourceEditMode)
16730     {
16731         this.editorcore.toggleSourceEdit(sourceEditMode);
16732         
16733         if(this.editorcore.sourceEditMode){
16734             Roo.log('editor - showing textarea');
16735             
16736 //            Roo.log('in');
16737 //            Roo.log(this.syncValue());
16738             this.syncValue();
16739             this.inputEl().removeClass('hide');
16740             this.inputEl().dom.removeAttribute('tabIndex');
16741             this.inputEl().focus();
16742         }else{
16743             Roo.log('editor - hiding textarea');
16744 //            Roo.log('out')
16745 //            Roo.log(this.pushValue()); 
16746             this.pushValue();
16747             
16748             this.inputEl().addClass('hide');
16749             this.inputEl().dom.setAttribute('tabIndex', -1);
16750             //this.deferFocus();
16751         }
16752          
16753         if(this.resizable){
16754             this.setSize(this.wrap.getSize());
16755         }
16756         
16757         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16758     },
16759  
16760     // private (for BoxComponent)
16761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16762
16763     // private (for BoxComponent)
16764     getResizeEl : function(){
16765         return this.wrap;
16766     },
16767
16768     // private (for BoxComponent)
16769     getPositionEl : function(){
16770         return this.wrap;
16771     },
16772
16773     // private
16774     initEvents : function(){
16775         this.originalValue = this.getValue();
16776     },
16777
16778 //    /**
16779 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16780 //     * @method
16781 //     */
16782 //    markInvalid : Roo.emptyFn,
16783 //    /**
16784 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16785 //     * @method
16786 //     */
16787 //    clearInvalid : Roo.emptyFn,
16788
16789     setValue : function(v){
16790         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16791         this.editorcore.pushValue();
16792     },
16793
16794      
16795     // private
16796     deferFocus : function(){
16797         this.focus.defer(10, this);
16798     },
16799
16800     // doc'ed in Field
16801     focus : function(){
16802         this.editorcore.focus();
16803         
16804     },
16805       
16806
16807     // private
16808     onDestroy : function(){
16809         
16810         
16811         
16812         if(this.rendered){
16813             
16814             for (var i =0; i < this.toolbars.length;i++) {
16815                 // fixme - ask toolbars for heights?
16816                 this.toolbars[i].onDestroy();
16817             }
16818             
16819             this.wrap.dom.innerHTML = '';
16820             this.wrap.remove();
16821         }
16822     },
16823
16824     // private
16825     onFirstFocus : function(){
16826         //Roo.log("onFirstFocus");
16827         this.editorcore.onFirstFocus();
16828          for (var i =0; i < this.toolbars.length;i++) {
16829             this.toolbars[i].onFirstFocus();
16830         }
16831         
16832     },
16833     
16834     // private
16835     syncValue : function()
16836     {   
16837         this.editorcore.syncValue();
16838     },
16839     
16840     pushValue : function()
16841     {   
16842         this.editorcore.pushValue();
16843     }
16844      
16845     
16846     // hide stuff that is not compatible
16847     /**
16848      * @event blur
16849      * @hide
16850      */
16851     /**
16852      * @event change
16853      * @hide
16854      */
16855     /**
16856      * @event focus
16857      * @hide
16858      */
16859     /**
16860      * @event specialkey
16861      * @hide
16862      */
16863     /**
16864      * @cfg {String} fieldClass @hide
16865      */
16866     /**
16867      * @cfg {String} focusClass @hide
16868      */
16869     /**
16870      * @cfg {String} autoCreate @hide
16871      */
16872     /**
16873      * @cfg {String} inputType @hide
16874      */
16875     /**
16876      * @cfg {String} invalidClass @hide
16877      */
16878     /**
16879      * @cfg {String} invalidText @hide
16880      */
16881     /**
16882      * @cfg {String} msgFx @hide
16883      */
16884     /**
16885      * @cfg {String} validateOnBlur @hide
16886      */
16887 });
16888  
16889     
16890    
16891    
16892    
16893       
16894 Roo.namespace('Roo.bootstrap.htmleditor');
16895 /**
16896  * @class Roo.bootstrap.HtmlEditorToolbar1
16897  * Basic Toolbar
16898  * 
16899  * Usage:
16900  *
16901  new Roo.bootstrap.HtmlEditor({
16902     ....
16903     toolbars : [
16904         new Roo.bootstrap.HtmlEditorToolbar1({
16905             disable : { fonts: 1 , format: 1, ..., ... , ...],
16906             btns : [ .... ]
16907         })
16908     }
16909      
16910  * 
16911  * @cfg {Object} disable List of elements to disable..
16912  * @cfg {Array} btns List of additional buttons.
16913  * 
16914  * 
16915  * NEEDS Extra CSS? 
16916  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16917  */
16918  
16919 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
16920 {
16921     
16922     Roo.apply(this, config);
16923     
16924     // default disabled, based on 'good practice'..
16925     this.disable = this.disable || {};
16926     Roo.applyIf(this.disable, {
16927         fontSize : true,
16928         colors : true,
16929         specialElements : true
16930     });
16931     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
16932     
16933     this.editor = config.editor;
16934     this.editorcore = config.editor.editorcore;
16935     
16936     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16937     
16938     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16939     // dont call parent... till later.
16940 }
16941 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
16942      
16943     bar : true,
16944     
16945     editor : false,
16946     editorcore : false,
16947     
16948     
16949     formats : [
16950         "p" ,  
16951         "h1","h2","h3","h4","h5","h6", 
16952         "pre", "code", 
16953         "abbr", "acronym", "address", "cite", "samp", "var",
16954         'div','span'
16955     ],
16956     
16957     onRender : function(ct, position)
16958     {
16959        // Roo.log("Call onRender: " + this.xtype);
16960         
16961        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16962        Roo.log(this.el);
16963        this.el.dom.style.marginBottom = '0';
16964        var _this = this;
16965        var editorcore = this.editorcore;
16966        var editor= this.editor;
16967        
16968        var children = [];
16969        var btn = function(id,cmd , toggle, handler){
16970        
16971             var  event = toggle ? 'toggle' : 'click';
16972        
16973             var a = {
16974                 size : 'sm',
16975                 xtype: 'Button',
16976                 xns: Roo.bootstrap,
16977                 glyphicon : id,
16978                 cmd : id || cmd,
16979                 enableToggle:toggle !== false,
16980                 //html : 'submit'
16981                 pressed : toggle ? false : null,
16982                 listeners : {}
16983             }
16984             a.listeners[toggle ? 'toggle' : 'click'] = function() {
16985                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
16986             }
16987             children.push(a);
16988             return a;
16989        }
16990         
16991         var style = {
16992                 xtype: 'Button',
16993                 size : 'sm',
16994                 xns: Roo.bootstrap,
16995                 glyphicon : 'font',
16996                 //html : 'submit'
16997                 menu : {
16998                     xtype: 'Menu',
16999                     xns: Roo.bootstrap,
17000                     items:  []
17001                 }
17002         };
17003         Roo.each(this.formats, function(f) {
17004             style.menu.items.push({
17005                 xtype :'MenuItem',
17006                 xns: Roo.bootstrap,
17007                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17008                 tagname : f,
17009                 listeners : {
17010                     click : function()
17011                     {
17012                         editorcore.insertTag(this.tagname);
17013                         editor.focus();
17014                     }
17015                 }
17016                 
17017             });
17018         });
17019          children.push(style);   
17020             
17021             
17022         btn('bold',false,true);
17023         btn('italic',false,true);
17024         btn('align-left', 'justifyleft',true);
17025         btn('align-center', 'justifycenter',true);
17026         btn('align-right' , 'justifyright',true);
17027         btn('link', false, false, function(btn) {
17028             //Roo.log("create link?");
17029             var url = prompt(this.createLinkText, this.defaultLinkValue);
17030             if(url && url != 'http:/'+'/'){
17031                 this.editorcore.relayCmd('createlink', url);
17032             }
17033         }),
17034         btn('list','insertunorderedlist',true);
17035         btn('pencil', false,true, function(btn){
17036                 Roo.log(this);
17037                 
17038                 this.toggleSourceEdit(btn.pressed);
17039         });
17040         /*
17041         var cog = {
17042                 xtype: 'Button',
17043                 size : 'sm',
17044                 xns: Roo.bootstrap,
17045                 glyphicon : 'cog',
17046                 //html : 'submit'
17047                 menu : {
17048                     xtype: 'Menu',
17049                     xns: Roo.bootstrap,
17050                     items:  []
17051                 }
17052         };
17053         
17054         cog.menu.items.push({
17055             xtype :'MenuItem',
17056             xns: Roo.bootstrap,
17057             html : Clean styles,
17058             tagname : f,
17059             listeners : {
17060                 click : function()
17061                 {
17062                     editorcore.insertTag(this.tagname);
17063                     editor.focus();
17064                 }
17065             }
17066             
17067         });
17068        */
17069         
17070          
17071        this.xtype = 'NavSimplebar';
17072         
17073         for(var i=0;i< children.length;i++) {
17074             
17075             this.buttons.add(this.addxtypeChild(children[i]));
17076             
17077         }
17078         
17079         editor.on('editorevent', this.updateToolbar, this);
17080     },
17081     onBtnClick : function(id)
17082     {
17083        this.editorcore.relayCmd(id);
17084        this.editorcore.focus();
17085     },
17086     
17087     /**
17088      * Protected method that will not generally be called directly. It triggers
17089      * a toolbar update by reading the markup state of the current selection in the editor.
17090      */
17091     updateToolbar: function(){
17092
17093         if(!this.editorcore.activated){
17094             this.editor.onFirstFocus(); // is this neeed?
17095             return;
17096         }
17097
17098         var btns = this.buttons; 
17099         var doc = this.editorcore.doc;
17100         btns.get('bold').setActive(doc.queryCommandState('bold'));
17101         btns.get('italic').setActive(doc.queryCommandState('italic'));
17102         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17103         
17104         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17105         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17106         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17107         
17108         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17109         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17110          /*
17111         
17112         var ans = this.editorcore.getAllAncestors();
17113         if (this.formatCombo) {
17114             
17115             
17116             var store = this.formatCombo.store;
17117             this.formatCombo.setValue("");
17118             for (var i =0; i < ans.length;i++) {
17119                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17120                     // select it..
17121                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17122                     break;
17123                 }
17124             }
17125         }
17126         
17127         
17128         
17129         // hides menus... - so this cant be on a menu...
17130         Roo.bootstrap.MenuMgr.hideAll();
17131         */
17132         Roo.bootstrap.MenuMgr.hideAll();
17133         //this.editorsyncValue();
17134     },
17135     onFirstFocus: function() {
17136         this.buttons.each(function(item){
17137            item.enable();
17138         });
17139     },
17140     toggleSourceEdit : function(sourceEditMode){
17141         
17142           
17143         if(sourceEditMode){
17144             Roo.log("disabling buttons");
17145            this.buttons.each( function(item){
17146                 if(item.cmd != 'pencil'){
17147                     item.disable();
17148                 }
17149             });
17150           
17151         }else{
17152             Roo.log("enabling buttons");
17153             if(this.editorcore.initialized){
17154                 this.buttons.each( function(item){
17155                     item.enable();
17156                 });
17157             }
17158             
17159         }
17160         Roo.log("calling toggole on editor");
17161         // tell the editor that it's been pressed..
17162         this.editor.toggleSourceEdit(sourceEditMode);
17163        
17164     }
17165 });
17166
17167
17168
17169
17170
17171 /**
17172  * @class Roo.bootstrap.Table.AbstractSelectionModel
17173  * @extends Roo.util.Observable
17174  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17175  * implemented by descendant classes.  This class should not be directly instantiated.
17176  * @constructor
17177  */
17178 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17179     this.locked = false;
17180     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17181 };
17182
17183
17184 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17185     /** @ignore Called by the grid automatically. Do not call directly. */
17186     init : function(grid){
17187         this.grid = grid;
17188         this.initEvents();
17189     },
17190
17191     /**
17192      * Locks the selections.
17193      */
17194     lock : function(){
17195         this.locked = true;
17196     },
17197
17198     /**
17199      * Unlocks the selections.
17200      */
17201     unlock : function(){
17202         this.locked = false;
17203     },
17204
17205     /**
17206      * Returns true if the selections are locked.
17207      * @return {Boolean}
17208      */
17209     isLocked : function(){
17210         return this.locked;
17211     }
17212 });
17213 /**
17214  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17215  * @class Roo.bootstrap.Table.RowSelectionModel
17216  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17217  * It supports multiple selections and keyboard selection/navigation. 
17218  * @constructor
17219  * @param {Object} config
17220  */
17221
17222 Roo.bootstrap.Table.RowSelectionModel = function(config){
17223     Roo.apply(this, config);
17224     this.selections = new Roo.util.MixedCollection(false, function(o){
17225         return o.id;
17226     });
17227
17228     this.last = false;
17229     this.lastActive = false;
17230
17231     this.addEvents({
17232         /**
17233              * @event selectionchange
17234              * Fires when the selection changes
17235              * @param {SelectionModel} this
17236              */
17237             "selectionchange" : true,
17238         /**
17239              * @event afterselectionchange
17240              * Fires after the selection changes (eg. by key press or clicking)
17241              * @param {SelectionModel} this
17242              */
17243             "afterselectionchange" : true,
17244         /**
17245              * @event beforerowselect
17246              * Fires when a row is selected being selected, return false to cancel.
17247              * @param {SelectionModel} this
17248              * @param {Number} rowIndex The selected index
17249              * @param {Boolean} keepExisting False if other selections will be cleared
17250              */
17251             "beforerowselect" : true,
17252         /**
17253              * @event rowselect
17254              * Fires when a row is selected.
17255              * @param {SelectionModel} this
17256              * @param {Number} rowIndex The selected index
17257              * @param {Roo.data.Record} r The record
17258              */
17259             "rowselect" : true,
17260         /**
17261              * @event rowdeselect
17262              * Fires when a row is deselected.
17263              * @param {SelectionModel} this
17264              * @param {Number} rowIndex The selected index
17265              */
17266         "rowdeselect" : true
17267     });
17268     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17269     this.locked = false;
17270 };
17271
17272 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17273     /**
17274      * @cfg {Boolean} singleSelect
17275      * True to allow selection of only one row at a time (defaults to false)
17276      */
17277     singleSelect : false,
17278
17279     // private
17280     initEvents : function(){
17281
17282         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17283             this.grid.on("mousedown", this.handleMouseDown, this);
17284         }else{ // allow click to work like normal
17285             this.grid.on("rowclick", this.handleDragableRowClick, this);
17286         }
17287
17288         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17289             "up" : function(e){
17290                 if(!e.shiftKey){
17291                     this.selectPrevious(e.shiftKey);
17292                 }else if(this.last !== false && this.lastActive !== false){
17293                     var last = this.last;
17294                     this.selectRange(this.last,  this.lastActive-1);
17295                     this.grid.getView().focusRow(this.lastActive);
17296                     if(last !== false){
17297                         this.last = last;
17298                     }
17299                 }else{
17300                     this.selectFirstRow();
17301                 }
17302                 this.fireEvent("afterselectionchange", this);
17303             },
17304             "down" : function(e){
17305                 if(!e.shiftKey){
17306                     this.selectNext(e.shiftKey);
17307                 }else if(this.last !== false && this.lastActive !== false){
17308                     var last = this.last;
17309                     this.selectRange(this.last,  this.lastActive+1);
17310                     this.grid.getView().focusRow(this.lastActive);
17311                     if(last !== false){
17312                         this.last = last;
17313                     }
17314                 }else{
17315                     this.selectFirstRow();
17316                 }
17317                 this.fireEvent("afterselectionchange", this);
17318             },
17319             scope: this
17320         });
17321
17322         var view = this.grid.view;
17323         view.on("refresh", this.onRefresh, this);
17324         view.on("rowupdated", this.onRowUpdated, this);
17325         view.on("rowremoved", this.onRemove, this);
17326     },
17327
17328     // private
17329     onRefresh : function(){
17330         var ds = this.grid.dataSource, i, v = this.grid.view;
17331         var s = this.selections;
17332         s.each(function(r){
17333             if((i = ds.indexOfId(r.id)) != -1){
17334                 v.onRowSelect(i);
17335             }else{
17336                 s.remove(r);
17337             }
17338         });
17339     },
17340
17341     // private
17342     onRemove : function(v, index, r){
17343         this.selections.remove(r);
17344     },
17345
17346     // private
17347     onRowUpdated : function(v, index, r){
17348         if(this.isSelected(r)){
17349             v.onRowSelect(index);
17350         }
17351     },
17352
17353     /**
17354      * Select records.
17355      * @param {Array} records The records to select
17356      * @param {Boolean} keepExisting (optional) True to keep existing selections
17357      */
17358     selectRecords : function(records, keepExisting){
17359         if(!keepExisting){
17360             this.clearSelections();
17361         }
17362         var ds = this.grid.dataSource;
17363         for(var i = 0, len = records.length; i < len; i++){
17364             this.selectRow(ds.indexOf(records[i]), true);
17365         }
17366     },
17367
17368     /**
17369      * Gets the number of selected rows.
17370      * @return {Number}
17371      */
17372     getCount : function(){
17373         return this.selections.length;
17374     },
17375
17376     /**
17377      * Selects the first row in the grid.
17378      */
17379     selectFirstRow : function(){
17380         this.selectRow(0);
17381     },
17382
17383     /**
17384      * Select the last row.
17385      * @param {Boolean} keepExisting (optional) True to keep existing selections
17386      */
17387     selectLastRow : function(keepExisting){
17388         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17389     },
17390
17391     /**
17392      * Selects the row immediately following the last selected row.
17393      * @param {Boolean} keepExisting (optional) True to keep existing selections
17394      */
17395     selectNext : function(keepExisting){
17396         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17397             this.selectRow(this.last+1, keepExisting);
17398             this.grid.getView().focusRow(this.last);
17399         }
17400     },
17401
17402     /**
17403      * Selects the row that precedes the last selected row.
17404      * @param {Boolean} keepExisting (optional) True to keep existing selections
17405      */
17406     selectPrevious : function(keepExisting){
17407         if(this.last){
17408             this.selectRow(this.last-1, keepExisting);
17409             this.grid.getView().focusRow(this.last);
17410         }
17411     },
17412
17413     /**
17414      * Returns the selected records
17415      * @return {Array} Array of selected records
17416      */
17417     getSelections : function(){
17418         return [].concat(this.selections.items);
17419     },
17420
17421     /**
17422      * Returns the first selected record.
17423      * @return {Record}
17424      */
17425     getSelected : function(){
17426         return this.selections.itemAt(0);
17427     },
17428
17429
17430     /**
17431      * Clears all selections.
17432      */
17433     clearSelections : function(fast){
17434         if(this.locked) return;
17435         if(fast !== true){
17436             var ds = this.grid.dataSource;
17437             var s = this.selections;
17438             s.each(function(r){
17439                 this.deselectRow(ds.indexOfId(r.id));
17440             }, this);
17441             s.clear();
17442         }else{
17443             this.selections.clear();
17444         }
17445         this.last = false;
17446     },
17447
17448
17449     /**
17450      * Selects all rows.
17451      */
17452     selectAll : function(){
17453         if(this.locked) return;
17454         this.selections.clear();
17455         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17456             this.selectRow(i, true);
17457         }
17458     },
17459
17460     /**
17461      * Returns True if there is a selection.
17462      * @return {Boolean}
17463      */
17464     hasSelection : function(){
17465         return this.selections.length > 0;
17466     },
17467
17468     /**
17469      * Returns True if the specified row is selected.
17470      * @param {Number/Record} record The record or index of the record to check
17471      * @return {Boolean}
17472      */
17473     isSelected : function(index){
17474         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17475         return (r && this.selections.key(r.id) ? true : false);
17476     },
17477
17478     /**
17479      * Returns True if the specified record id is selected.
17480      * @param {String} id The id of record to check
17481      * @return {Boolean}
17482      */
17483     isIdSelected : function(id){
17484         return (this.selections.key(id) ? true : false);
17485     },
17486
17487     // private
17488     handleMouseDown : function(e, t){
17489         var view = this.grid.getView(), rowIndex;
17490         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17491             return;
17492         };
17493         if(e.shiftKey && this.last !== false){
17494             var last = this.last;
17495             this.selectRange(last, rowIndex, e.ctrlKey);
17496             this.last = last; // reset the last
17497             view.focusRow(rowIndex);
17498         }else{
17499             var isSelected = this.isSelected(rowIndex);
17500             if(e.button !== 0 && isSelected){
17501                 view.focusRow(rowIndex);
17502             }else if(e.ctrlKey && isSelected){
17503                 this.deselectRow(rowIndex);
17504             }else if(!isSelected){
17505                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17506                 view.focusRow(rowIndex);
17507             }
17508         }
17509         this.fireEvent("afterselectionchange", this);
17510     },
17511     // private
17512     handleDragableRowClick :  function(grid, rowIndex, e) 
17513     {
17514         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17515             this.selectRow(rowIndex, false);
17516             grid.view.focusRow(rowIndex);
17517              this.fireEvent("afterselectionchange", this);
17518         }
17519     },
17520     
17521     /**
17522      * Selects multiple rows.
17523      * @param {Array} rows Array of the indexes of the row to select
17524      * @param {Boolean} keepExisting (optional) True to keep existing selections
17525      */
17526     selectRows : function(rows, keepExisting){
17527         if(!keepExisting){
17528             this.clearSelections();
17529         }
17530         for(var i = 0, len = rows.length; i < len; i++){
17531             this.selectRow(rows[i], true);
17532         }
17533     },
17534
17535     /**
17536      * Selects a range of rows. All rows in between startRow and endRow are also selected.
17537      * @param {Number} startRow The index of the first row in the range
17538      * @param {Number} endRow The index of the last row in the range
17539      * @param {Boolean} keepExisting (optional) True to retain existing selections
17540      */
17541     selectRange : function(startRow, endRow, keepExisting){
17542         if(this.locked) return;
17543         if(!keepExisting){
17544             this.clearSelections();
17545         }
17546         if(startRow <= endRow){
17547             for(var i = startRow; i <= endRow; i++){
17548                 this.selectRow(i, true);
17549             }
17550         }else{
17551             for(var i = startRow; i >= endRow; i--){
17552                 this.selectRow(i, true);
17553             }
17554         }
17555     },
17556
17557     /**
17558      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17559      * @param {Number} startRow The index of the first row in the range
17560      * @param {Number} endRow The index of the last row in the range
17561      */
17562     deselectRange : function(startRow, endRow, preventViewNotify){
17563         if(this.locked) return;
17564         for(var i = startRow; i <= endRow; i++){
17565             this.deselectRow(i, preventViewNotify);
17566         }
17567     },
17568
17569     /**
17570      * Selects a row.
17571      * @param {Number} row The index of the row to select
17572      * @param {Boolean} keepExisting (optional) True to keep existing selections
17573      */
17574     selectRow : function(index, keepExisting, preventViewNotify){
17575         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17576         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17577             if(!keepExisting || this.singleSelect){
17578                 this.clearSelections();
17579             }
17580             var r = this.grid.dataSource.getAt(index);
17581             this.selections.add(r);
17582             this.last = this.lastActive = index;
17583             if(!preventViewNotify){
17584                 this.grid.getView().onRowSelect(index);
17585             }
17586             this.fireEvent("rowselect", this, index, r);
17587             this.fireEvent("selectionchange", this);
17588         }
17589     },
17590
17591     /**
17592      * Deselects a row.
17593      * @param {Number} row The index of the row to deselect
17594      */
17595     deselectRow : function(index, preventViewNotify){
17596         if(this.locked) return;
17597         if(this.last == index){
17598             this.last = false;
17599         }
17600         if(this.lastActive == index){
17601             this.lastActive = false;
17602         }
17603         var r = this.grid.dataSource.getAt(index);
17604         this.selections.remove(r);
17605         if(!preventViewNotify){
17606             this.grid.getView().onRowDeselect(index);
17607         }
17608         this.fireEvent("rowdeselect", this, index);
17609         this.fireEvent("selectionchange", this);
17610     },
17611
17612     // private
17613     restoreLast : function(){
17614         if(this._last){
17615             this.last = this._last;
17616         }
17617     },
17618
17619     // private
17620     acceptsNav : function(row, col, cm){
17621         return !cm.isHidden(col) && cm.isCellEditable(col, row);
17622     },
17623
17624     // private
17625     onEditorKey : function(field, e){
17626         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17627         if(k == e.TAB){
17628             e.stopEvent();
17629             ed.completeEdit();
17630             if(e.shiftKey){
17631                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17632             }else{
17633                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17634             }
17635         }else if(k == e.ENTER && !e.ctrlKey){
17636             e.stopEvent();
17637             ed.completeEdit();
17638             if(e.shiftKey){
17639                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17640             }else{
17641                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17642             }
17643         }else if(k == e.ESC){
17644             ed.cancelEdit();
17645         }
17646         if(newCell){
17647             g.startEditing(newCell[0], newCell[1]);
17648         }
17649     }
17650 });/*
17651  * Based on:
17652  * Ext JS Library 1.1.1
17653  * Copyright(c) 2006-2007, Ext JS, LLC.
17654  *
17655  * Originally Released Under LGPL - original licence link has changed is not relivant.
17656  *
17657  * Fork - LGPL
17658  * <script type="text/javascript">
17659  */
17660  
17661 /**
17662  * @class Roo.bootstrap.PagingToolbar
17663  * @extends Roo.Row
17664  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
17665  * @constructor
17666  * Create a new PagingToolbar
17667  * @param {Object} config The config object
17668  */
17669 Roo.bootstrap.PagingToolbar = function(config)
17670 {
17671     // old args format still supported... - xtype is prefered..
17672         // created from xtype...
17673     var ds = config.dataSource;
17674     this.toolbarItems = [];
17675     if (config.items) {
17676         this.toolbarItems = config.items;
17677 //        config.items = [];
17678     }
17679     
17680     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
17681     this.ds = ds;
17682     this.cursor = 0;
17683     if (ds) { 
17684         this.bind(ds);
17685     }
17686     
17687     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
17688     
17689 };
17690
17691 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
17692     /**
17693      * @cfg {Roo.data.Store} dataSource
17694      * The underlying data store providing the paged data
17695      */
17696     /**
17697      * @cfg {String/HTMLElement/Element} container
17698      * container The id or element that will contain the toolbar
17699      */
17700     /**
17701      * @cfg {Boolean} displayInfo
17702      * True to display the displayMsg (defaults to false)
17703      */
17704     /**
17705      * @cfg {Number} pageSize
17706      * The number of records to display per page (defaults to 20)
17707      */
17708     pageSize: 20,
17709     /**
17710      * @cfg {String} displayMsg
17711      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
17712      */
17713     displayMsg : 'Displaying {0} - {1} of {2}',
17714     /**
17715      * @cfg {String} emptyMsg
17716      * The message to display when no records are found (defaults to "No data to display")
17717      */
17718     emptyMsg : 'No data to display',
17719     /**
17720      * Customizable piece of the default paging text (defaults to "Page")
17721      * @type String
17722      */
17723     beforePageText : "Page",
17724     /**
17725      * Customizable piece of the default paging text (defaults to "of %0")
17726      * @type String
17727      */
17728     afterPageText : "of {0}",
17729     /**
17730      * Customizable piece of the default paging text (defaults to "First Page")
17731      * @type String
17732      */
17733     firstText : "First Page",
17734     /**
17735      * Customizable piece of the default paging text (defaults to "Previous Page")
17736      * @type String
17737      */
17738     prevText : "Previous Page",
17739     /**
17740      * Customizable piece of the default paging text (defaults to "Next Page")
17741      * @type String
17742      */
17743     nextText : "Next Page",
17744     /**
17745      * Customizable piece of the default paging text (defaults to "Last Page")
17746      * @type String
17747      */
17748     lastText : "Last Page",
17749     /**
17750      * Customizable piece of the default paging text (defaults to "Refresh")
17751      * @type String
17752      */
17753     refreshText : "Refresh",
17754
17755     buttons : false,
17756     // private
17757     onRender : function(ct, position) 
17758     {
17759         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
17760         this.navgroup.parentId = this.id;
17761         this.navgroup.onRender(this.el, null);
17762         // add the buttons to the navgroup
17763         
17764         if(this.displayInfo){
17765             Roo.log(this.el.select('ul.navbar-nav',true).first());
17766             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
17767             this.displayEl = this.el.select('.x-paging-info', true).first();
17768 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
17769 //            this.displayEl = navel.el.select('span',true).first();
17770         }
17771         
17772         var _this = this;
17773         
17774         if(this.buttons){
17775             Roo.each(_this.buttons, function(e){
17776                Roo.factory(e).onRender(_this.el, null);
17777             });
17778         }
17779             
17780         Roo.each(_this.toolbarItems, function(e) {
17781             _this.navgroup.addItem(e);
17782         });
17783         
17784         this.first = this.navgroup.addItem({
17785             tooltip: this.firstText,
17786             cls: "prev",
17787             icon : 'fa fa-backward',
17788             disabled: true,
17789             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
17790         });
17791         
17792         this.prev =  this.navgroup.addItem({
17793             tooltip: this.prevText,
17794             cls: "prev",
17795             icon : 'fa fa-step-backward',
17796             disabled: true,
17797             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
17798         });
17799     //this.addSeparator();
17800         
17801         
17802         var field = this.navgroup.addItem( {
17803             tagtype : 'span',
17804             cls : 'x-paging-position',
17805             
17806             html : this.beforePageText  +
17807                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
17808                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
17809          } ); //?? escaped?
17810         
17811         this.field = field.el.select('input', true).first();
17812         this.field.on("keydown", this.onPagingKeydown, this);
17813         this.field.on("focus", function(){this.dom.select();});
17814     
17815     
17816         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
17817         //this.field.setHeight(18);
17818         //this.addSeparator();
17819         this.next = this.navgroup.addItem({
17820             tooltip: this.nextText,
17821             cls: "next",
17822             html : ' <i class="fa fa-step-forward">',
17823             disabled: true,
17824             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
17825         });
17826         this.last = this.navgroup.addItem({
17827             tooltip: this.lastText,
17828             icon : 'fa fa-forward',
17829             cls: "next",
17830             disabled: true,
17831             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
17832         });
17833     //this.addSeparator();
17834         this.loading = this.navgroup.addItem({
17835             tooltip: this.refreshText,
17836             icon: 'fa fa-refresh',
17837             
17838             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
17839         });
17840
17841     },
17842
17843     // private
17844     updateInfo : function(){
17845         if(this.displayEl){
17846             var count = this.ds.getCount();
17847             var msg = count == 0 ?
17848                 this.emptyMsg :
17849                 String.format(
17850                     this.displayMsg,
17851                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
17852                 );
17853             this.displayEl.update(msg);
17854         }
17855     },
17856
17857     // private
17858     onLoad : function(ds, r, o){
17859        this.cursor = o.params ? o.params.start : 0;
17860        var d = this.getPageData(),
17861             ap = d.activePage,
17862             ps = d.pages;
17863         
17864        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
17865        this.field.dom.value = ap;
17866        this.first.setDisabled(ap == 1);
17867        this.prev.setDisabled(ap == 1);
17868        this.next.setDisabled(ap == ps);
17869        this.last.setDisabled(ap == ps);
17870        this.loading.enable();
17871        this.updateInfo();
17872     },
17873
17874     // private
17875     getPageData : function(){
17876         var total = this.ds.getTotalCount();
17877         return {
17878             total : total,
17879             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
17880             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
17881         };
17882     },
17883
17884     // private
17885     onLoadError : function(){
17886         this.loading.enable();
17887     },
17888
17889     // private
17890     onPagingKeydown : function(e){
17891         var k = e.getKey();
17892         var d = this.getPageData();
17893         if(k == e.RETURN){
17894             var v = this.field.dom.value, pageNum;
17895             if(!v || isNaN(pageNum = parseInt(v, 10))){
17896                 this.field.dom.value = d.activePage;
17897                 return;
17898             }
17899             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
17900             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17901             e.stopEvent();
17902         }
17903         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))
17904         {
17905           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
17906           this.field.dom.value = pageNum;
17907           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
17908           e.stopEvent();
17909         }
17910         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17911         {
17912           var v = this.field.dom.value, pageNum; 
17913           var increment = (e.shiftKey) ? 10 : 1;
17914           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
17915             increment *= -1;
17916           if(!v || isNaN(pageNum = parseInt(v, 10))) {
17917             this.field.dom.value = d.activePage;
17918             return;
17919           }
17920           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
17921           {
17922             this.field.dom.value = parseInt(v, 10) + increment;
17923             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
17924             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
17925           }
17926           e.stopEvent();
17927         }
17928     },
17929
17930     // private
17931     beforeLoad : function(){
17932         if(this.loading){
17933             this.loading.disable();
17934         }
17935     },
17936
17937     // private
17938     onClick : function(which){
17939         var ds = this.ds;
17940         if (!ds) {
17941             return;
17942         }
17943         switch(which){
17944             case "first":
17945                 ds.load({params:{start: 0, limit: this.pageSize}});
17946             break;
17947             case "prev":
17948                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
17949             break;
17950             case "next":
17951                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
17952             break;
17953             case "last":
17954                 var total = ds.getTotalCount();
17955                 var extra = total % this.pageSize;
17956                 var lastStart = extra ? (total - extra) : total-this.pageSize;
17957                 ds.load({params:{start: lastStart, limit: this.pageSize}});
17958             break;
17959             case "refresh":
17960                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
17961             break;
17962         }
17963     },
17964
17965     /**
17966      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
17967      * @param {Roo.data.Store} store The data store to unbind
17968      */
17969     unbind : function(ds){
17970         ds.un("beforeload", this.beforeLoad, this);
17971         ds.un("load", this.onLoad, this);
17972         ds.un("loadexception", this.onLoadError, this);
17973         ds.un("remove", this.updateInfo, this);
17974         ds.un("add", this.updateInfo, this);
17975         this.ds = undefined;
17976     },
17977
17978     /**
17979      * Binds the paging toolbar to the specified {@link Roo.data.Store}
17980      * @param {Roo.data.Store} store The data store to bind
17981      */
17982     bind : function(ds){
17983         ds.on("beforeload", this.beforeLoad, this);
17984         ds.on("load", this.onLoad, this);
17985         ds.on("loadexception", this.onLoadError, this);
17986         ds.on("remove", this.updateInfo, this);
17987         ds.on("add", this.updateInfo, this);
17988         this.ds = ds;
17989     }
17990 });/*
17991  * - LGPL
17992  *
17993  * element
17994  * 
17995  */
17996
17997 /**
17998  * @class Roo.bootstrap.MessageBar
17999  * @extends Roo.bootstrap.Component
18000  * Bootstrap MessageBar class
18001  * @cfg {String} html contents of the MessageBar
18002  * @cfg {String} weight (info | success | warning | danger) default info
18003  * @cfg {String} beforeClass insert the bar before the given class
18004  * @cfg {Boolean} closable (true | false) default false
18005  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18006  * 
18007  * @constructor
18008  * Create a new Element
18009  * @param {Object} config The config object
18010  */
18011
18012 Roo.bootstrap.MessageBar = function(config){
18013     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18014 };
18015
18016 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18017     
18018     html: '',
18019     weight: 'info',
18020     closable: false,
18021     fixed: false,
18022     beforeClass: 'bootstrap-sticky-wrap',
18023     
18024     getAutoCreate : function(){
18025         
18026         var cfg = {
18027             tag: 'div',
18028             cls: 'alert alert-dismissable alert-' + this.weight,
18029             cn: [
18030                 {
18031                     tag: 'span',
18032                     cls: 'message',
18033                     html: this.html || ''
18034                 }
18035             ]
18036         }
18037         
18038         if(this.fixed){
18039             cfg.cls += ' alert-messages-fixed';
18040         }
18041         
18042         if(this.closable){
18043             cfg.cn.push({
18044                 tag: 'button',
18045                 cls: 'close',
18046                 html: 'x'
18047             });
18048         }
18049         
18050         return cfg;
18051     },
18052     
18053     onRender : function(ct, position)
18054     {
18055         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18056         
18057         if(!this.el){
18058             var cfg = Roo.apply({},  this.getAutoCreate());
18059             cfg.id = Roo.id();
18060             
18061             if (this.cls) {
18062                 cfg.cls += ' ' + this.cls;
18063             }
18064             if (this.style) {
18065                 cfg.style = this.style;
18066             }
18067             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18068             
18069             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18070         }
18071         
18072         this.el.select('>button.close').on('click', this.hide, this);
18073         
18074     },
18075     
18076     show : function()
18077     {
18078         if (!this.rendered) {
18079             this.render();
18080         }
18081         
18082         this.el.show();
18083         
18084         this.fireEvent('show', this);
18085         
18086     },
18087     
18088     hide : function()
18089     {
18090         if (!this.rendered) {
18091             this.render();
18092         }
18093         
18094         this.el.hide();
18095         
18096         this.fireEvent('hide', this);
18097     },
18098     
18099     update : function()
18100     {
18101 //        var e = this.el.dom.firstChild;
18102 //        
18103 //        if(this.closable){
18104 //            e = e.nextSibling;
18105 //        }
18106 //        
18107 //        e.data = this.html || '';
18108
18109         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18110     }
18111    
18112 });
18113
18114  
18115
18116  /*
18117  * - LGPL
18118  *
18119  * Graph
18120  * 
18121  */
18122
18123
18124 /**
18125  * @class Roo.bootstrap.Graph
18126  * @extends Roo.bootstrap.Component
18127  * Bootstrap Graph class
18128 > Prameters
18129  -sm {number} sm 4
18130  -md {number} md 5
18131  -graphtype {String} graphtype bar | vbar | pie
18132  -g_x {number}  x coodinator | centre x (pie)
18133  -g_y {number}  y coodinator | centre y (pie)
18134  -g_r {number}  radius (pie)
18135  -g_height {number} height of the chart (respected by all elements in the set)
18136  -g_width {number}  width of the chart (respected by all elements in the set)
18137  -{Array}  values
18138  -opts (object) options for the chart 
18139      o {
18140      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18141      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18142      o vgutter (number)
18143      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
18144      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18145      o to
18146      o stretch (boolean)
18147      o }
18148  -opts (object) options for the pie
18149      o{
18150      o cut
18151      o startAngle (number)
18152      o endAngle (number)
18153      } 
18154  *
18155  * @constructor
18156  * Create a new Input
18157  * @param {Object} config The config object
18158  */
18159
18160 Roo.bootstrap.Graph = function(config){
18161     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18162     
18163     this.addEvents({
18164         // img events
18165         /**
18166          * @event click
18167          * The img click event for the img.
18168          * @param {Roo.EventObject} e
18169          */
18170         "click" : true
18171     });
18172 };
18173
18174 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18175     
18176     sm: 4,
18177     md: 5,
18178     graphtype: 'bar',
18179     g_height: 250,
18180     g_width: 650,
18181     g_x: 50,
18182     g_y: 50,
18183     g_r: 30,
18184     opts:{
18185         //g_colors: this.colors,
18186         g_type: 'soft',
18187         g_gutter: '20%'
18188
18189     },
18190
18191     getAutoCreate : function(){
18192         
18193         var cfg = {
18194             tag: 'div',
18195             html : null
18196         }
18197         
18198         
18199         return  cfg;
18200     },
18201
18202     onRender : function(ct,position){
18203         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18204         var r = {};
18205         this.raphael = Raphael(this.el.dom),
18206                     data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18207                     data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18208                     data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18209                     txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18210                 /*
18211                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18212                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18213                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18214                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18215                 
18216                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18217                 r.barchart(330, 10, 300, 220, data1);
18218                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18219                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18220                 */
18221                 
18222                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18223                 // r.barchart(30, 30, 560, 250,  xdata, {
18224                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18225                 //     axis : "0 0 1 1",
18226                 //     axisxlabels :  xdata
18227                 //     //yvalues : cols,
18228                    
18229                 // });
18230
18231     },
18232
18233     load : function(graphtype,xdata,opts){
18234         this.raphael.clear();
18235         if(!graphtype) {
18236             graphtype = this.graphtype;
18237         }
18238         if(!opts){
18239             opts = this.opts;
18240         }
18241         switch(graphtype){
18242             case 'bar':
18243                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts);
18244                 break;
18245             case 'hbar':
18246                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts);
18247                 break;
18248             case 'pie':
18249                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts);
18250                 break;
18251
18252         }
18253     },
18254     
18255     initEvents: function() {
18256         
18257         if(!this.href){
18258             this.el.on('click', this.onClick, this);
18259         }
18260     },
18261     
18262     onClick : function(e)
18263     {
18264         Roo.log('img onclick');
18265         this.fireEvent('click', this, e);
18266     }
18267    
18268 });
18269
18270  
18271 /*
18272  * - LGPL
18273  *
18274  * numberBox
18275  * 
18276  */
18277
18278
18279 /**
18280  * @class Roo.bootstrap.dash.NumberBox
18281  * @extends Roo.bootstrap.Component
18282  * Bootstrap NumberBox class
18283  * @cfg {number} sm 4
18284  * @cfg {number} md 5
18285  * @cfg {String} headline
18286  * @cfg {String} title
18287  * @cfg {String} more info url
18288  * @cfg {String} more info text
18289  * @cfg {Array}  opts values
18290  * 
18291  * @constructor
18292  * Create a new Input
18293  * @param {Object} config The config object
18294  */
18295
18296 Roo.bootstrap.dash.NumberBox = function(config){
18297     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
18298     
18299     this.addEvents({
18300         // img events
18301         /**
18302          * @event click
18303          * The img click event for the img.
18304          * @param {Roo.EventObject} e
18305          */
18306         "click" : true
18307     });
18308 };
18309
18310 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
18311     
18312     width: 200,
18313     height: 150,
18314     headline: '',
18315     title: 'Title',
18316     more_url: '',
18317     more_text: '',
18318
18319     getAutoCreate : function(){
18320         
18321         var cfg = {
18322             tag: 'div',
18323             cls: '',
18324             html : null,
18325             cn: [
18326             {
18327                 tag: 'h',
18328                 cls: '',
18329                 html: this.headline ? this.headline : 'Headline'
18330             },
18331             {
18332                 tag: 'p',
18333                 cls: '',
18334                 html: this.title ? this.title : 'Title'
18335             },
18336             {
18337                 tag: 'div',
18338                 cls: '',
18339                 html : null,
18340                 cn: [{
18341                     tag: 'a',
18342                     href: this.more_url,
18343                     cls: '',
18344                     html: this.more_text
18345                 }]
18346
18347             }]
18348         }
18349     
18350         
18351         return  cfg;
18352     },
18353
18354     // onRender : function(ct,position){
18355     //     Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18356                 
18357     // },
18358
18359     // load : function(graphtype,xdata){
18360     //     this.raphael.clear();
18361     //     if(!graphtype) {
18362     //         graphtype = this.graphtype;
18363     //     }
18364     //     switch(graphtype){
18365     //         case 'bar':
18366     //             this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,this.opts);
18367     //             break;
18368     //         case 'hbar':
18369     //             this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,this.opts);
18370     //             break;
18371     //         case 'pie':
18372     //             this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,this.opts);
18373     //             break;
18374
18375     //     }
18376     // },
18377     
18378     initEvents: function() {
18379         
18380         if(!this.href){
18381             this.el.on('click', this.onClick, this);
18382         }
18383     },
18384     
18385     onClick : function(e)
18386     {
18387         Roo.log('img onclick');
18388         this.fireEvent('click', this, e);
18389     }
18390    
18391 });
18392
18393