955f84f5f31c030dca9f517c170f430954f5783d
[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  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         this.initEvents();
140         
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165            
166         cn.parentType = this.xtype; //??
167         cn.parentId = this.id;
168         
169         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170         if (typeof(cn.container_method) == 'string') {
171             cntr = cn.container_method;
172         }
173         
174         
175         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
176         
177         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
178         
179         var build_from_html =  Roo.XComponent.build_from_html;
180           
181         var is_body  = (tree.xtype == 'Body') ;
182           
183         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184           
185         var self_cntr_el = Roo.get(this[cntr](false));
186         
187         // do not try and build conditional elements 
188         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
189             return false;
190         }
191         
192         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194                 return this.addxtypeChild(tree,cntr, is_body);
195             }
196             
197             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
198                 
199             if(echild){
200                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
201             }
202             
203             Roo.log('skipping render');
204             return cn;
205             
206         }
207         
208         var ret = false;
209         if (!build_from_html) {
210             return false;
211         }
212         
213         // this i think handles overlaying multiple children of the same type
214         // with the sam eelement.. - which might be buggy..
215         while (true) {
216             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
217             
218             if (!echild) {
219                 break;
220             }
221             
222             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
223                 break;
224             }
225             
226             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
227         }
228         return ret;
229     },
230     
231     addxtypeChild : function (tree, cntr, is_body)
232     {
233         Roo.debug && Roo.log('addxtypeChild:' + cntr);
234         var cn = this;
235         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
236         
237         
238         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239                     (typeof(tree['flexy:foreach']) != 'undefined');
240           
241         
242         
243          skip_children = false;
244         // render the element if it's not BODY.
245         if (!is_body) {
246            
247             cn = Roo.factory(tree);
248            
249             cn.parentType = this.xtype; //??
250             cn.parentId = this.id;
251             
252             var build_from_html =  Roo.XComponent.build_from_html;
253             
254             
255             // does the container contain child eleemnts with 'xtype' attributes.
256             // that match this xtype..
257             // note - when we render we create these as well..
258             // so we should check to see if body has xtype set.
259             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260                
261                 var self_cntr_el = Roo.get(this[cntr](false));
262                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263                 if (echild) { 
264                     //Roo.log(Roo.XComponent.build_from_html);
265                     //Roo.log("got echild:");
266                     //Roo.log(echild);
267                 }
268                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269                 // and are not displayed -this causes this to use up the wrong element when matching.
270                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
271                 
272                 
273                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
275                   
276                   
277                   
278                     cn.el = echild;
279                   //  Roo.log("GOT");
280                     //echild.dom.removeAttribute('xtype');
281                 } else {
282                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283                     Roo.debug && Roo.log(self_cntr_el);
284                     Roo.debug && Roo.log(echild);
285                     Roo.debug && Roo.log(cn);
286                 }
287             }
288            
289             
290            
291             // if object has flexy:if - then it may or may not be rendered.
292             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
293                 // skip a flexy if element.
294                 Roo.debug && Roo.log('skipping render');
295                 Roo.debug && Roo.log(tree);
296                 if (!cn.el) {
297                     Roo.debug && Roo.log('skipping all children');
298                     skip_children = true;
299                 }
300                 
301              } else {
302                  
303                 // actually if flexy:foreach is found, we really want to create 
304                 // multiple copies here...
305                 //Roo.log('render');
306                 //Roo.log(this[cntr]());
307                 cn.render(this[cntr](true));
308              }
309             // then add the element..
310         }
311         
312         
313         // handle the kids..
314         
315         var nitems = [];
316         /*
317         if (typeof (tree.menu) != 'undefined') {
318             tree.menu.parentType = cn.xtype;
319             tree.menu.triggerEl = cn.el;
320             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
321             
322         }
323         */
324         if (!tree.items || !tree.items.length) {
325             cn.items = nitems;
326             return cn;
327         }
328         var items = tree.items;
329         delete tree.items;
330         
331         //Roo.log(items.length);
332             // add the items..
333         if (!skip_children) {    
334             for(var i =0;i < items.length;i++) {
335                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
336             }
337         }
338         
339         cn.items = nitems;
340         
341         this.fireEvent('childrenrendered', this);
342         
343         return cn;
344     },
345     /**
346      * Show a component - removes 'hidden' class
347      */
348     show : function()
349     {
350         if (this.el) {
351             this.el.removeClass('hidden');
352         }
353     },
354     /**
355      * Hide a component - adds 'hidden' class
356      */
357     hide: function()
358     {
359         if (this.el && !this.el.hasClass('hidden')) {
360             this.el.addClass('hidden');
361         }
362         
363     }
364 });
365
366  /*
367  * - LGPL
368  *
369  * Body
370  * 
371  */
372
373 /**
374  * @class Roo.bootstrap.Body
375  * @extends Roo.bootstrap.Component
376  * Bootstrap Body class
377  * 
378  * @constructor
379  * Create a new body
380  * @param {Object} config The config object
381  */
382
383 Roo.bootstrap.Body = function(config){
384     Roo.bootstrap.Body.superclass.constructor.call(this, config);
385     this.el = Roo.get(document.body);
386     if (this.cls && this.cls.length) {
387         Roo.get(document.body).addClass(this.cls);
388     }
389 };
390
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
392     
393     is_body : true,// just to make sure it's constructed?
394     
395         autoCreate : {
396         cls: 'container'
397     },
398     onRender : function(ct, position)
399     {
400        /* Roo.log("Roo.bootstrap.Body - onRender");
401         if (this.cls && this.cls.length) {
402             Roo.get(document.body).addClass(this.cls);
403         }
404         // style??? xttr???
405         */
406     }
407     
408     
409  
410    
411 });
412
413  /*
414  * - LGPL
415  *
416  * button group
417  * 
418  */
419
420
421 /**
422  * @class Roo.bootstrap.ButtonGroup
423  * @extends Roo.bootstrap.Component
424  * Bootstrap ButtonGroup class
425  * @cfg {String} size lg | sm | xs (default empty normal)
426  * @cfg {String} align vertical | justified  (default none)
427  * @cfg {String} direction up | down (default down)
428  * @cfg {Boolean} toolbar false | true
429  * @cfg {Boolean} btn true | false
430  * 
431  * 
432  * @constructor
433  * Create a new Input
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.ButtonGroup = function(config){
438     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
439 };
440
441 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
442     
443     size: '',
444     align: '',
445     direction: '',
446     toolbar: false,
447     btn: true,
448
449     getAutoCreate : function(){
450         var cfg = {
451             cls: 'btn-group',
452             html : null
453         };
454         
455         cfg.html = this.html || cfg.html;
456         
457         if (this.toolbar) {
458             cfg = {
459                 cls: 'btn-toolbar',
460                 html: null
461             };
462             
463             return cfg;
464         }
465         
466         if (['vertical','justified'].indexOf(this.align)!==-1) {
467             cfg.cls = 'btn-group-' + this.align;
468             
469             if (this.align == 'justified') {
470                 console.log(this.items);
471             }
472         }
473         
474         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
475             cfg.cls += ' btn-group-' + this.size;
476         }
477         
478         if (this.direction == 'up') {
479             cfg.cls += ' dropup' ;
480         }
481         
482         return cfg;
483     }
484    
485 });
486
487  /*
488  * - LGPL
489  *
490  * button
491  * 
492  */
493
494 /**
495  * @class Roo.bootstrap.Button
496  * @extends Roo.bootstrap.Component
497  * Bootstrap Button class
498  * @cfg {String} html The button content
499  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
500  * @cfg {String} size ( lg | sm | xs)
501  * @cfg {String} tag ( a | input | submit)
502  * @cfg {String} href empty or href
503  * @cfg {Boolean} disabled default false;
504  * @cfg {Boolean} isClose default false;
505  * @cfg {String} glyphicon (| 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)
506  * @cfg {String} badge text for badge
507  * @cfg {String} theme default 
508  * @cfg {Boolean} inverse 
509  * @cfg {Boolean} toggle 
510  * @cfg {String} ontext text for on toggle state
511  * @cfg {String} offtext text for off toggle state
512  * @cfg {Boolean} defaulton 
513  * @cfg {Boolean} preventDefault  default true
514  * @cfg {Boolean} removeClass remove the standard class..
515  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
516  * 
517  * @constructor
518  * Create a new button
519  * @param {Object} config The config object
520  */
521
522
523 Roo.bootstrap.Button = function(config){
524     Roo.bootstrap.Button.superclass.constructor.call(this, config);
525     this.addEvents({
526         // raw events
527         /**
528          * @event click
529          * When a butotn is pressed
530          * @param {Roo.bootstrap.Button} this
531          * @param {Roo.EventObject} e
532          */
533         "click" : true,
534          /**
535          * @event toggle
536          * After the button has been toggles
537          * @param {Roo.EventObject} e
538          * @param {boolean} pressed (also available as button.pressed)
539          */
540         "toggle" : true
541     });
542 };
543
544 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
545     html: false,
546     active: false,
547     weight: '',
548     size: '',
549     tag: 'button',
550     href: '',
551     disabled: false,
552     isClose: false,
553     glyphicon: '',
554     badge: '',
555     theme: 'default',
556     inverse: false,
557     
558     toggle: false,
559     ontext: 'ON',
560     offtext: 'OFF',
561     defaulton: true,
562     preventDefault: true,
563     removeClass: false,
564     name: false,
565     target: false,
566     
567     
568     pressed : null,
569      
570     
571     getAutoCreate : function(){
572         
573         var cfg = {
574             tag : 'button',
575             cls : 'roo-button',
576             html: ''
577         };
578         
579         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
580             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
581             this.tag = 'button';
582         } else {
583             cfg.tag = this.tag;
584         }
585         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
586         
587         if (this.toggle == true) {
588             cfg={
589                 tag: 'div',
590                 cls: 'slider-frame roo-button',
591                 cn: [
592                     {
593                         tag: 'span',
594                         'data-on-text':'ON',
595                         'data-off-text':'OFF',
596                         cls: 'slider-button',
597                         html: this.offtext
598                     }
599                 ]
600             };
601             
602             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
603                 cfg.cls += ' '+this.weight;
604             }
605             
606             return cfg;
607         }
608         
609         if (this.isClose) {
610             cfg.cls += ' close';
611             
612             cfg["aria-hidden"] = true;
613             
614             cfg.html = "&times;";
615             
616             return cfg;
617         }
618         
619          
620         if (this.theme==='default') {
621             cfg.cls = 'btn roo-button';
622             
623             //if (this.parentType != 'Navbar') {
624             this.weight = this.weight.length ?  this.weight : 'default';
625             //}
626             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
627                 
628                 cfg.cls += ' btn-' + this.weight;
629             }
630         } else if (this.theme==='glow') {
631             
632             cfg.tag = 'a';
633             cfg.cls = 'btn-glow roo-button';
634             
635             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636                 
637                 cfg.cls += ' ' + this.weight;
638             }
639         }
640    
641         
642         if (this.inverse) {
643             this.cls += ' inverse';
644         }
645         
646         
647         if (this.active) {
648             cfg.cls += ' active';
649         }
650         
651         if (this.disabled) {
652             cfg.disabled = 'disabled';
653         }
654         
655         if (this.items) {
656             Roo.log('changing to ul' );
657             cfg.tag = 'ul';
658             this.glyphicon = 'caret';
659         }
660         
661         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
662          
663         //gsRoo.log(this.parentType);
664         if (this.parentType === 'Navbar' && !this.parent().bar) {
665             Roo.log('changing to li?');
666             
667             cfg.tag = 'li';
668             
669             cfg.cls = '';
670             cfg.cn =  [{
671                 tag : 'a',
672                 cls : 'roo-button',
673                 html : this.html,
674                 href : this.href || '#'
675             }];
676             if (this.menu) {
677                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
678                 cfg.cls += ' dropdown';
679             }   
680             
681             delete cfg.html;
682             
683         }
684         
685        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
686         
687         if (this.glyphicon) {
688             cfg.html = ' ' + cfg.html;
689             
690             cfg.cn = [
691                 {
692                     tag: 'span',
693                     cls: 'glyphicon glyphicon-' + this.glyphicon
694                 }
695             ];
696         }
697         
698         if (this.badge) {
699             cfg.html += ' ';
700             
701             cfg.tag = 'a';
702             
703 //            cfg.cls='btn roo-button';
704             
705             cfg.href=this.href;
706             
707             var value = cfg.html;
708             
709             if(this.glyphicon){
710                 value = {
711                             tag: 'span',
712                             cls: 'glyphicon glyphicon-' + this.glyphicon,
713                             html: this.html
714                         };
715                 
716             }
717             
718             cfg.cn = [
719                 value,
720                 {
721                     tag: 'span',
722                     cls: 'badge',
723                     html: this.badge
724                 }
725             ];
726             
727             cfg.html='';
728         }
729         
730         if (this.menu) {
731             cfg.cls += ' dropdown';
732             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
733         }
734         
735         if (cfg.tag !== 'a' && this.href !== '') {
736             throw "Tag must be a to set href.";
737         } else if (this.href.length > 0) {
738             cfg.href = this.href;
739         }
740         
741         if(this.removeClass){
742             cfg.cls = '';
743         }
744         
745         if(this.target){
746             cfg.target = this.target;
747         }
748         
749         return cfg;
750     },
751     initEvents: function() {
752        // Roo.log('init events?');
753 //        Roo.log(this.el.dom);
754         // add the menu...
755         
756         if (typeof (this.menu) != 'undefined') {
757             this.menu.parentType = this.xtype;
758             this.menu.triggerEl = this.el;
759             this.addxtype(Roo.apply({}, this.menu));
760         }
761
762
763        if (this.el.hasClass('roo-button')) {
764             this.el.on('click', this.onClick, this);
765        } else {
766             this.el.select('.roo-button').on('click', this.onClick, this);
767        }
768        
769        if(this.removeClass){
770            this.el.on('click', this.onClick, this);
771        }
772        
773        this.el.enableDisplayMode();
774         
775     },
776     onClick : function(e)
777     {
778         if (this.disabled) {
779             return;
780         }
781         
782         
783         Roo.log('button on click ');
784         if(this.preventDefault){
785             e.preventDefault();
786         }
787         if (this.pressed === true || this.pressed === false) {
788             this.pressed = !this.pressed;
789             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
790             this.fireEvent('toggle', this, e, this.pressed);
791         }
792         
793         
794         this.fireEvent('click', this, e);
795     },
796     
797     /**
798      * Enables this button
799      */
800     enable : function()
801     {
802         this.disabled = false;
803         this.el.removeClass('disabled');
804     },
805     
806     /**
807      * Disable this button
808      */
809     disable : function()
810     {
811         this.disabled = true;
812         this.el.addClass('disabled');
813     },
814      /**
815      * sets the active state on/off, 
816      * @param {Boolean} state (optional) Force a particular state
817      */
818     setActive : function(v) {
819         
820         this.el[v ? 'addClass' : 'removeClass']('active');
821     },
822      /**
823      * toggles the current active state 
824      */
825     toggleActive : function()
826     {
827        var active = this.el.hasClass('active');
828        this.setActive(!active);
829        
830         
831     },
832     setText : function(str)
833     {
834         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835     },
836     getText : function()
837     {
838         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
839     },
840     hide: function() {
841        
842      
843         this.el.hide();   
844     },
845     show: function() {
846        
847         this.el.show();   
848     }
849     
850     
851 });
852
853  /*
854  * - LGPL
855  *
856  * column
857  * 
858  */
859
860 /**
861  * @class Roo.bootstrap.Column
862  * @extends Roo.bootstrap.Component
863  * Bootstrap Column class
864  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
865  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
866  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
867  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
868  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
869  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
870  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
871  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
872  *
873  * 
874  * @cfg {Boolean} hidden (true|false) hide the element
875  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876  * @cfg {String} fa (ban|check|...) font awesome icon
877  * @cfg {Number} fasize (1|2|....) font awsome size
878
879  * @cfg {String} icon (info-sign|check|...) glyphicon name
880
881  * @cfg {String} html content of column.
882  * 
883  * @constructor
884  * Create a new Column
885  * @param {Object} config The config object
886  */
887
888 Roo.bootstrap.Column = function(config){
889     Roo.bootstrap.Column.superclass.constructor.call(this, config);
890 };
891
892 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
893     
894     xs: false,
895     sm: false,
896     md: false,
897     lg: false,
898     xsoff: false,
899     smoff: false,
900     mdoff: false,
901     lgoff: false,
902     html: '',
903     offset: 0,
904     alert: false,
905     fa: false,
906     icon : false,
907     hidden : false,
908     fasize : 1,
909     
910     getAutoCreate : function(){
911         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
912         
913         cfg = {
914             tag: 'div',
915             cls: 'column'
916         };
917         
918         var settings=this;
919         ['xs','sm','md','lg'].map(function(size){
920             //Roo.log( size + ':' + settings[size]);
921             
922             if (settings[size+'off'] !== false) {
923                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
924             }
925             
926             if (settings[size] === false) {
927                 return;
928             }
929             
930             if (!settings[size]) { // 0 = hidden
931                 cfg.cls += ' hidden-' + size;
932                 return;
933             }
934             cfg.cls += ' col-' + size + '-' + settings[size];
935             
936         });
937         
938         if (this.hidden) {
939             cfg.cls += ' hidden';
940         }
941         
942         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
943             cfg.cls +=' alert alert-' + this.alert;
944         }
945         
946         
947         if (this.html.length) {
948             cfg.html = this.html;
949         }
950         if (this.fa) {
951             var fasize = '';
952             if (this.fasize > 1) {
953                 fasize = ' fa-' + this.fasize + 'x';
954             }
955             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
956             
957             
958         }
959         if (this.icon) {
960             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
961         }
962         
963         return cfg;
964     }
965    
966 });
967
968  
969
970  /*
971  * - LGPL
972  *
973  * page container.
974  * 
975  */
976
977
978 /**
979  * @class Roo.bootstrap.Container
980  * @extends Roo.bootstrap.Component
981  * Bootstrap Container class
982  * @cfg {Boolean} jumbotron is it a jumbotron element
983  * @cfg {String} html content of element
984  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
985  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
986  * @cfg {String} header content of header (for panel)
987  * @cfg {String} footer content of footer (for panel)
988  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
989  * @cfg {String} tag (header|aside|section) type of HTML tag.
990  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
991  * @cfg {String} fa font awesome icon
992  * @cfg {String} icon (info-sign|check|...) glyphicon name
993  * @cfg {Boolean} hidden (true|false) hide the element
994  * @cfg {Boolean} expandable (true|false) default false
995  * @cfg {Boolean} expanded (true|false) default true
996  * @cfg {String} rheader contet on the right of header
997  * @cfg {Boolean} clickable (true|false) default false
998
999  *     
1000  * @constructor
1001  * Create a new Container
1002  * @param {Object} config The config object
1003  */
1004
1005 Roo.bootstrap.Container = function(config){
1006     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1007     
1008     this.addEvents({
1009         // raw events
1010          /**
1011          * @event expand
1012          * After the panel has been expand
1013          * 
1014          * @param {Roo.bootstrap.Container} this
1015          */
1016         "expand" : true,
1017         /**
1018          * @event collapse
1019          * After the panel has been collapsed
1020          * 
1021          * @param {Roo.bootstrap.Container} this
1022          */
1023         "collapse" : true,
1024         /**
1025          * @event click
1026          * When a element is chick
1027          * @param {Roo.bootstrap.Container} this
1028          * @param {Roo.EventObject} e
1029          */
1030         "click" : true
1031     });
1032 };
1033
1034 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1035     
1036     jumbotron : false,
1037     well: '',
1038     panel : '',
1039     header: '',
1040     footer : '',
1041     sticky: '',
1042     tag : false,
1043     alert : false,
1044     fa: false,
1045     icon : false,
1046     expandable : false,
1047     rheader : '',
1048     expanded : true,
1049     clickable: false,
1050   
1051      
1052     getChildContainer : function() {
1053         
1054         if(!this.el){
1055             return false;
1056         }
1057         
1058         if (this.panel.length) {
1059             return this.el.select('.panel-body',true).first();
1060         }
1061         
1062         return this.el;
1063     },
1064     
1065     
1066     getAutoCreate : function(){
1067         
1068         var cfg = {
1069             tag : this.tag || 'div',
1070             html : '',
1071             cls : ''
1072         };
1073         if (this.jumbotron) {
1074             cfg.cls = 'jumbotron';
1075         }
1076         
1077         
1078         
1079         // - this is applied by the parent..
1080         //if (this.cls) {
1081         //    cfg.cls = this.cls + '';
1082         //}
1083         
1084         if (this.sticky.length) {
1085             
1086             var bd = Roo.get(document.body);
1087             if (!bd.hasClass('bootstrap-sticky')) {
1088                 bd.addClass('bootstrap-sticky');
1089                 Roo.select('html',true).setStyle('height', '100%');
1090             }
1091              
1092             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093         }
1094         
1095         
1096         if (this.well.length) {
1097             switch (this.well) {
1098                 case 'lg':
1099                 case 'sm':
1100                     cfg.cls +=' well well-' +this.well;
1101                     break;
1102                 default:
1103                     cfg.cls +=' well';
1104                     break;
1105             }
1106         }
1107         
1108         if (this.hidden) {
1109             cfg.cls += ' hidden';
1110         }
1111         
1112         
1113         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1114             cfg.cls +=' alert alert-' + this.alert;
1115         }
1116         
1117         var body = cfg;
1118         
1119         if (this.panel.length) {
1120             cfg.cls += ' panel panel-' + this.panel;
1121             cfg.cn = [];
1122             if (this.header.length) {
1123                 
1124                 var h = [];
1125                 
1126                 if(this.expandable){
1127                     
1128                     cfg.cls = cfg.cls + ' expandable';
1129                     
1130                     h.push({
1131                         tag: 'i',
1132                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1133                     });
1134                     
1135                 }
1136                 
1137                 h.push(
1138                     {
1139                         tag: 'span',
1140                         cls : 'panel-title',
1141                         html : (this.expandable ? '&nbsp;' : '') + this.header
1142                     },
1143                     {
1144                         tag: 'span',
1145                         cls: 'panel-header-right',
1146                         html: this.rheader
1147                     }
1148                 );
1149                 
1150                 cfg.cn.push({
1151                     cls : 'panel-heading',
1152                     style : this.expandable ? 'cursor: pointer' : '',
1153                     cn : h
1154                 });
1155                 
1156             }
1157             
1158             body = false;
1159             cfg.cn.push({
1160                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1161                 html : this.html
1162             });
1163             
1164             
1165             if (this.footer.length) {
1166                 cfg.cn.push({
1167                     cls : 'panel-footer',
1168                     html : this.footer
1169                     
1170                 });
1171             }
1172             
1173         }
1174         
1175         if (body) {
1176             body.html = this.html || cfg.html;
1177             // prefix with the icons..
1178             if (this.fa) {
1179                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1180             }
1181             if (this.icon) {
1182                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1183             }
1184             
1185             
1186         }
1187         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1188             cfg.cls =  'container';
1189         }
1190         
1191         return cfg;
1192     },
1193     
1194     initEvents: function() 
1195     {
1196         if(this.expandable){
1197             var headerEl = this.headerEl();
1198         
1199             if(headerEl){
1200                 headerEl.on('click', this.onToggleClick, this);
1201             }
1202         }
1203         
1204         if(this.clickable){
1205             this.el.on('click', this.onClick, this);
1206         }
1207         
1208     },
1209     
1210     onToggleClick : function()
1211     {
1212         var headerEl = this.headerEl();
1213         
1214         if(!headerEl){
1215             return;
1216         }
1217         
1218         if(this.expanded){
1219             this.collapse();
1220             return;
1221         }
1222         
1223         this.expand();
1224     },
1225     
1226     expand : function()
1227     {
1228         if(this.fireEvent('expand', this)) {
1229             
1230             this.expanded = true;
1231             
1232             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1233             
1234             this.el.select('.panel-body',true).first().removeClass('hide');
1235             
1236             var toggleEl = this.toggleEl();
1237
1238             if(!toggleEl){
1239                 return;
1240             }
1241
1242             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1243         }
1244         
1245     },
1246     
1247     collapse : function()
1248     {
1249         if(this.fireEvent('collapse', this)) {
1250             
1251             this.expanded = false;
1252             
1253             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1254             this.el.select('.panel-body',true).first().addClass('hide');
1255         
1256             var toggleEl = this.toggleEl();
1257
1258             if(!toggleEl){
1259                 return;
1260             }
1261
1262             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263         }
1264     },
1265     
1266     toggleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-heading .fa',true).first();
1273     },
1274     
1275     headerEl : function()
1276     {
1277         if(!this.el || !this.panel.length || !this.header.length){
1278             return;
1279         }
1280         
1281         return this.el.select('.panel-heading',true).first()
1282     },
1283     
1284     titleEl : function()
1285     {
1286         if(!this.el || !this.panel.length || !this.header.length){
1287             return;
1288         }
1289         
1290         return this.el.select('.panel-title',true).first();
1291     },
1292     
1293     setTitle : function(v)
1294     {
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return;
1299         }
1300         
1301         titleEl.dom.innerHTML = v;
1302     },
1303     
1304     getTitle : function()
1305     {
1306         
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return '';
1311         }
1312         
1313         return titleEl.dom.innerHTML;
1314     },
1315     
1316     setRightTitle : function(v)
1317     {
1318         var t = this.el.select('.panel-header-right',true).first();
1319         
1320         if(!t){
1321             return;
1322         }
1323         
1324         t.dom.innerHTML = v;
1325     },
1326     
1327     onClick : function(e)
1328     {
1329         e.preventDefault();
1330         
1331         this.fireEvent('click', this, e);
1332     }
1333    
1334 });
1335
1336  /*
1337  * - LGPL
1338  *
1339  * image
1340  * 
1341  */
1342
1343
1344 /**
1345  * @class Roo.bootstrap.Img
1346  * @extends Roo.bootstrap.Component
1347  * Bootstrap Img class
1348  * @cfg {Boolean} imgResponsive false | true
1349  * @cfg {String} border rounded | circle | thumbnail
1350  * @cfg {String} src image source
1351  * @cfg {String} alt image alternative text
1352  * @cfg {String} href a tag href
1353  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1354  * @cfg {String} xsUrl xs image source
1355  * @cfg {String} smUrl sm image source
1356  * @cfg {String} mdUrl md image source
1357  * @cfg {String} lgUrl lg image source
1358  * 
1359  * @constructor
1360  * Create a new Input
1361  * @param {Object} config The config object
1362  */
1363
1364 Roo.bootstrap.Img = function(config){
1365     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1366     
1367     this.addEvents({
1368         // img events
1369         /**
1370          * @event click
1371          * The img click event for the img.
1372          * @param {Roo.EventObject} e
1373          */
1374         "click" : true
1375     });
1376 };
1377
1378 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1379     
1380     imgResponsive: true,
1381     border: '',
1382     src: 'about:blank',
1383     href: false,
1384     target: false,
1385     xsUrl: '',
1386     smUrl: '',
1387     mdUrl: '',
1388     lgUrl: '',
1389
1390     getAutoCreate : function()
1391     {   
1392         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1393             return this.createSingleImg();
1394         }
1395         
1396         var cfg = {
1397             tag: 'div',
1398             cls: 'roo-image-responsive-group',
1399             cn: []
1400         };
1401         var _this = this;
1402         
1403         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1404             
1405             if(!_this[size + 'Url']){
1406                 return;
1407             }
1408             
1409             var img = {
1410                 tag: 'img',
1411                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1412                 html: _this.html || cfg.html,
1413                 src: _this[size + 'Url']
1414             };
1415             
1416             img.cls += ' roo-image-responsive-' + size;
1417             
1418             var s = ['xs', 'sm', 'md', 'lg'];
1419             
1420             s.splice(s.indexOf(size), 1);
1421             
1422             Roo.each(s, function(ss){
1423                 img.cls += ' hidden-' + ss;
1424             });
1425             
1426             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1427                 cfg.cls += ' img-' + _this.border;
1428             }
1429             
1430             if(_this.alt){
1431                 cfg.alt = _this.alt;
1432             }
1433             
1434             if(_this.href){
1435                 var a = {
1436                     tag: 'a',
1437                     href: _this.href,
1438                     cn: [
1439                         img
1440                     ]
1441                 };
1442
1443                 if(this.target){
1444                     a.target = _this.target;
1445                 }
1446             }
1447             
1448             cfg.cn.push((_this.href) ? a : img);
1449             
1450         });
1451         
1452         return cfg;
1453     },
1454     
1455     createSingleImg : function()
1456     {
1457         var cfg = {
1458             tag: 'img',
1459             cls: (this.imgResponsive) ? 'img-responsive' : '',
1460             html : null,
1461             src : 'about:blank'  // just incase src get's set to undefined?!?
1462         };
1463         
1464         cfg.html = this.html || cfg.html;
1465         
1466         cfg.src = this.src || cfg.src;
1467         
1468         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1469             cfg.cls += ' img-' + this.border;
1470         }
1471         
1472         if(this.alt){
1473             cfg.alt = this.alt;
1474         }
1475         
1476         if(this.href){
1477             var a = {
1478                 tag: 'a',
1479                 href: this.href,
1480                 cn: [
1481                     cfg
1482                 ]
1483             };
1484             
1485             if(this.target){
1486                 a.target = this.target;
1487             }
1488             
1489         }
1490         
1491         return (this.href) ? a : cfg;
1492     },
1493     
1494     initEvents: function() 
1495     {
1496         if(!this.href){
1497             this.el.on('click', this.onClick, this);
1498         }
1499         
1500     },
1501     
1502     onClick : function(e)
1503     {
1504         Roo.log('img onclick');
1505         this.fireEvent('click', this, e);
1506     }
1507    
1508 });
1509
1510  /*
1511  * - LGPL
1512  *
1513  * image
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Link
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Link Class
1522  * @cfg {String} alt image alternative text
1523  * @cfg {String} href a tag href
1524  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1525  * @cfg {String} html the content of the link.
1526  * @cfg {String} anchor name for the anchor link
1527
1528  * @cfg {Boolean} preventDefault (true | false) default false
1529
1530  * 
1531  * @constructor
1532  * Create a new Input
1533  * @param {Object} config The config object
1534  */
1535
1536 Roo.bootstrap.Link = function(config){
1537     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1538     
1539     this.addEvents({
1540         // img events
1541         /**
1542          * @event click
1543          * The img click event for the img.
1544          * @param {Roo.EventObject} e
1545          */
1546         "click" : true
1547     });
1548 };
1549
1550 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1551     
1552     href: false,
1553     target: false,
1554     preventDefault: false,
1555     anchor : false,
1556     alt : false,
1557
1558     getAutoCreate : function()
1559     {
1560         
1561         var cfg = {
1562             tag: 'a'
1563         };
1564         // anchor's do not require html/href...
1565         if (this.anchor === false) {
1566             cfg.html = this.html || '';
1567             cfg.href = this.href || '#';
1568         } else {
1569             cfg.name = this.anchor;
1570             if (this.html !== false) {
1571                 cfg.html = this.html;
1572             }
1573             if (this.href !== false) {
1574                 cfg.href = this.href;
1575             }
1576         }
1577         
1578         if(this.alt !== false){
1579             cfg.alt = this.alt;
1580         }
1581         
1582         
1583         if(this.target !== false) {
1584             cfg.target = this.target;
1585         }
1586         
1587         return cfg;
1588     },
1589     
1590     initEvents: function() {
1591         
1592         if(!this.href || this.preventDefault){
1593             this.el.on('click', this.onClick, this);
1594         }
1595     },
1596     
1597     onClick : function(e)
1598     {
1599         if(this.preventDefault){
1600             e.preventDefault();
1601         }
1602         //Roo.log('img onclick');
1603         this.fireEvent('click', this, e);
1604     }
1605    
1606 });
1607
1608  /*
1609  * - LGPL
1610  *
1611  * header
1612  * 
1613  */
1614
1615 /**
1616  * @class Roo.bootstrap.Header
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap Header class
1619  * @cfg {String} html content of header
1620  * @cfg {Number} level (1|2|3|4|5|6) default 1
1621  * 
1622  * @constructor
1623  * Create a new Header
1624  * @param {Object} config The config object
1625  */
1626
1627
1628 Roo.bootstrap.Header  = function(config){
1629     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1630 };
1631
1632 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1633     
1634     //href : false,
1635     html : false,
1636     level : 1,
1637     
1638     
1639     
1640     getAutoCreate : function(){
1641         
1642         
1643         
1644         var cfg = {
1645             tag: 'h' + (1 *this.level),
1646             html: this.html || ''
1647         } ;
1648         
1649         return cfg;
1650     }
1651    
1652 });
1653
1654  
1655
1656  /*
1657  * Based on:
1658  * Ext JS Library 1.1.1
1659  * Copyright(c) 2006-2007, Ext JS, LLC.
1660  *
1661  * Originally Released Under LGPL - original licence link has changed is not relivant.
1662  *
1663  * Fork - LGPL
1664  * <script type="text/javascript">
1665  */
1666  
1667 /**
1668  * @class Roo.bootstrap.MenuMgr
1669  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1670  * @singleton
1671  */
1672 Roo.bootstrap.MenuMgr = function(){
1673    var menus, active, groups = {}, attached = false, lastShow = new Date();
1674
1675    // private - called when first menu is created
1676    function init(){
1677        menus = {};
1678        active = new Roo.util.MixedCollection();
1679        Roo.get(document).addKeyListener(27, function(){
1680            if(active.length > 0){
1681                hideAll();
1682            }
1683        });
1684    }
1685
1686    // private
1687    function hideAll(){
1688        if(active && active.length > 0){
1689            var c = active.clone();
1690            c.each(function(m){
1691                m.hide();
1692            });
1693        }
1694    }
1695
1696    // private
1697    function onHide(m){
1698        active.remove(m);
1699        if(active.length < 1){
1700            Roo.get(document).un("mouseup", onMouseDown);
1701             
1702            attached = false;
1703        }
1704    }
1705
1706    // private
1707    function onShow(m){
1708        var last = active.last();
1709        lastShow = new Date();
1710        active.add(m);
1711        if(!attached){
1712           Roo.get(document).on("mouseup", onMouseDown);
1713            
1714            attached = true;
1715        }
1716        if(m.parentMenu){
1717           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1718           m.parentMenu.activeChild = m;
1719        }else if(last && last.isVisible()){
1720           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1721        }
1722    }
1723
1724    // private
1725    function onBeforeHide(m){
1726        if(m.activeChild){
1727            m.activeChild.hide();
1728        }
1729        if(m.autoHideTimer){
1730            clearTimeout(m.autoHideTimer);
1731            delete m.autoHideTimer;
1732        }
1733    }
1734
1735    // private
1736    function onBeforeShow(m){
1737        var pm = m.parentMenu;
1738        if(!pm && !m.allowOtherMenus){
1739            hideAll();
1740        }else if(pm && pm.activeChild && active != m){
1741            pm.activeChild.hide();
1742        }
1743    }
1744
1745    // private this should really trigger on mouseup..
1746    function onMouseDown(e){
1747         Roo.log("on Mouse Up");
1748         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1749             Roo.log("hideAll");
1750             hideAll();
1751             e.stopEvent();
1752         }
1753         
1754         
1755    }
1756
1757    // private
1758    function onBeforeCheck(mi, state){
1759        if(state){
1760            var g = groups[mi.group];
1761            for(var i = 0, l = g.length; i < l; i++){
1762                if(g[i] != mi){
1763                    g[i].setChecked(false);
1764                }
1765            }
1766        }
1767    }
1768
1769    return {
1770
1771        /**
1772         * Hides all menus that are currently visible
1773         */
1774        hideAll : function(){
1775             hideAll();  
1776        },
1777
1778        // private
1779        register : function(menu){
1780            if(!menus){
1781                init();
1782            }
1783            menus[menu.id] = menu;
1784            menu.on("beforehide", onBeforeHide);
1785            menu.on("hide", onHide);
1786            menu.on("beforeshow", onBeforeShow);
1787            menu.on("show", onShow);
1788            var g = menu.group;
1789            if(g && menu.events["checkchange"]){
1790                if(!groups[g]){
1791                    groups[g] = [];
1792                }
1793                groups[g].push(menu);
1794                menu.on("checkchange", onCheck);
1795            }
1796        },
1797
1798         /**
1799          * Returns a {@link Roo.menu.Menu} object
1800          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1801          * be used to generate and return a new Menu instance.
1802          */
1803        get : function(menu){
1804            if(typeof menu == "string"){ // menu id
1805                return menus[menu];
1806            }else if(menu.events){  // menu instance
1807                return menu;
1808            }
1809            /*else if(typeof menu.length == 'number'){ // array of menu items?
1810                return new Roo.bootstrap.Menu({items:menu});
1811            }else{ // otherwise, must be a config
1812                return new Roo.bootstrap.Menu(menu);
1813            }
1814            */
1815            return false;
1816        },
1817
1818        // private
1819        unregister : function(menu){
1820            delete menus[menu.id];
1821            menu.un("beforehide", onBeforeHide);
1822            menu.un("hide", onHide);
1823            menu.un("beforeshow", onBeforeShow);
1824            menu.un("show", onShow);
1825            var g = menu.group;
1826            if(g && menu.events["checkchange"]){
1827                groups[g].remove(menu);
1828                menu.un("checkchange", onCheck);
1829            }
1830        },
1831
1832        // private
1833        registerCheckable : function(menuItem){
1834            var g = menuItem.group;
1835            if(g){
1836                if(!groups[g]){
1837                    groups[g] = [];
1838                }
1839                groups[g].push(menuItem);
1840                menuItem.on("beforecheckchange", onBeforeCheck);
1841            }
1842        },
1843
1844        // private
1845        unregisterCheckable : function(menuItem){
1846            var g = menuItem.group;
1847            if(g){
1848                groups[g].remove(menuItem);
1849                menuItem.un("beforecheckchange", onBeforeCheck);
1850            }
1851        }
1852    };
1853 }();/*
1854  * - LGPL
1855  *
1856  * menu
1857  * 
1858  */
1859
1860 /**
1861  * @class Roo.bootstrap.Menu
1862  * @extends Roo.bootstrap.Component
1863  * Bootstrap Menu class - container for MenuItems
1864  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1865  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1866  * 
1867  * @constructor
1868  * Create a new Menu
1869  * @param {Object} config The config object
1870  */
1871
1872
1873 Roo.bootstrap.Menu = function(config){
1874     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1875     if (this.registerMenu && this.type != 'treeview')  {
1876         Roo.bootstrap.MenuMgr.register(this);
1877     }
1878     this.addEvents({
1879         /**
1880          * @event beforeshow
1881          * Fires before this menu is displayed
1882          * @param {Roo.menu.Menu} this
1883          */
1884         beforeshow : true,
1885         /**
1886          * @event beforehide
1887          * Fires before this menu is hidden
1888          * @param {Roo.menu.Menu} this
1889          */
1890         beforehide : true,
1891         /**
1892          * @event show
1893          * Fires after this menu is displayed
1894          * @param {Roo.menu.Menu} this
1895          */
1896         show : true,
1897         /**
1898          * @event hide
1899          * Fires after this menu is hidden
1900          * @param {Roo.menu.Menu} this
1901          */
1902         hide : true,
1903         /**
1904          * @event click
1905          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1906          * @param {Roo.menu.Menu} this
1907          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1908          * @param {Roo.EventObject} e
1909          */
1910         click : true,
1911         /**
1912          * @event mouseover
1913          * Fires when the mouse is hovering over this menu
1914          * @param {Roo.menu.Menu} this
1915          * @param {Roo.EventObject} e
1916          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1917          */
1918         mouseover : true,
1919         /**
1920          * @event mouseout
1921          * Fires when the mouse exits this menu
1922          * @param {Roo.menu.Menu} this
1923          * @param {Roo.EventObject} e
1924          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1925          */
1926         mouseout : true,
1927         /**
1928          * @event itemclick
1929          * Fires when a menu item contained in this menu is clicked
1930          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1931          * @param {Roo.EventObject} e
1932          */
1933         itemclick: true
1934     });
1935     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1936 };
1937
1938 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1939     
1940    /// html : false,
1941     //align : '',
1942     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1943     type: false,
1944     /**
1945      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1946      */
1947     registerMenu : true,
1948     
1949     menuItems :false, // stores the menu items..
1950     
1951     hidden:true,
1952     
1953         
1954     parentMenu : false,
1955     
1956     getChildContainer : function() {
1957         return this.el;  
1958     },
1959     
1960     getAutoCreate : function(){
1961          
1962         //if (['right'].indexOf(this.align)!==-1) {
1963         //    cfg.cn[1].cls += ' pull-right'
1964         //}
1965         
1966         
1967         var cfg = {
1968             tag : 'ul',
1969             cls : 'dropdown-menu' ,
1970             style : 'z-index:1000'
1971             
1972         };
1973         
1974         if (this.type === 'submenu') {
1975             cfg.cls = 'submenu active';
1976         }
1977         if (this.type === 'treeview') {
1978             cfg.cls = 'treeview-menu';
1979         }
1980         
1981         return cfg;
1982     },
1983     initEvents : function() {
1984         
1985        // Roo.log("ADD event");
1986        // Roo.log(this.triggerEl.dom);
1987         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1988         
1989         this.triggerEl.addClass('dropdown-toggle');
1990         
1991         
1992         if (Roo.isTouch) {
1993             this.el.on('touchstart'  , this.onTouch, this);
1994         }
1995         this.el.on('click' , this.onClick, this);
1996
1997         this.el.on("mouseover", this.onMouseOver, this);
1998         this.el.on("mouseout", this.onMouseOut, this);
1999         
2000     },
2001     
2002     findTargetItem : function(e)
2003     {
2004         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2005         if(!t){
2006             return false;
2007         }
2008         //Roo.log(t);         Roo.log(t.id);
2009         if(t && t.id){
2010             //Roo.log(this.menuitems);
2011             return this.menuitems.get(t.id);
2012             
2013             //return this.items.get(t.menuItemId);
2014         }
2015         
2016         return false;
2017     },
2018     
2019     onTouch : function(e) 
2020     {
2021         //e.stopEvent(); this make the user popdown broken
2022         this.onClick(e);
2023     },
2024     
2025     onClick : function(e)
2026     {
2027         Roo.log("menu.onClick");
2028         var t = this.findTargetItem(e);
2029         if(!t || t.isContainer){
2030             return;
2031         }
2032         Roo.log(e);
2033         /*
2034         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2035             if(t == this.activeItem && t.shouldDeactivate(e)){
2036                 this.activeItem.deactivate();
2037                 delete this.activeItem;
2038                 return;
2039             }
2040             if(t.canActivate){
2041                 this.setActiveItem(t, true);
2042             }
2043             return;
2044             
2045             
2046         }
2047         */
2048        
2049         Roo.log('pass click event');
2050         
2051         t.onClick(e);
2052         
2053         this.fireEvent("click", this, t, e);
2054         
2055         this.hide();
2056     },
2057      onMouseOver : function(e){
2058         var t  = this.findTargetItem(e);
2059         //Roo.log(t);
2060         //if(t){
2061         //    if(t.canActivate && !t.disabled){
2062         //        this.setActiveItem(t, true);
2063         //    }
2064         //}
2065         
2066         this.fireEvent("mouseover", this, e, t);
2067     },
2068     isVisible : function(){
2069         return !this.hidden;
2070     },
2071      onMouseOut : function(e){
2072         var t  = this.findTargetItem(e);
2073         
2074         //if(t ){
2075         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2076         //        this.activeItem.deactivate();
2077         //        delete this.activeItem;
2078         //    }
2079         //}
2080         this.fireEvent("mouseout", this, e, t);
2081     },
2082     
2083     
2084     /**
2085      * Displays this menu relative to another element
2086      * @param {String/HTMLElement/Roo.Element} element The element to align to
2087      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2088      * the element (defaults to this.defaultAlign)
2089      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2090      */
2091     show : function(el, pos, parentMenu){
2092         this.parentMenu = parentMenu;
2093         if(!this.el){
2094             this.render();
2095         }
2096         this.fireEvent("beforeshow", this);
2097         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2098     },
2099      /**
2100      * Displays this menu at a specific xy position
2101      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2102      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2103      */
2104     showAt : function(xy, parentMenu, /* private: */_e){
2105         this.parentMenu = parentMenu;
2106         if(!this.el){
2107             this.render();
2108         }
2109         if(_e !== false){
2110             this.fireEvent("beforeshow", this);
2111             //xy = this.el.adjustForConstraints(xy);
2112         }
2113         
2114         //this.el.show();
2115         this.hideMenuItems();
2116         this.hidden = false;
2117         this.triggerEl.addClass('open');
2118         
2119         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2120             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2121         }
2122         
2123         this.el.setXY(xy);
2124         this.focus();
2125         this.fireEvent("show", this);
2126     },
2127     
2128     focus : function(){
2129         return;
2130         if(!this.hidden){
2131             this.doFocus.defer(50, this);
2132         }
2133     },
2134
2135     doFocus : function(){
2136         if(!this.hidden){
2137             this.focusEl.focus();
2138         }
2139     },
2140
2141     /**
2142      * Hides this menu and optionally all parent menus
2143      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2144      */
2145     hide : function(deep)
2146     {
2147         
2148         this.hideMenuItems();
2149         if(this.el && this.isVisible()){
2150             this.fireEvent("beforehide", this);
2151             if(this.activeItem){
2152                 this.activeItem.deactivate();
2153                 this.activeItem = null;
2154             }
2155             this.triggerEl.removeClass('open');;
2156             this.hidden = true;
2157             this.fireEvent("hide", this);
2158         }
2159         if(deep === true && this.parentMenu){
2160             this.parentMenu.hide(true);
2161         }
2162     },
2163     
2164     onTriggerPress  : function(e)
2165     {
2166         
2167         Roo.log('trigger press');
2168         //Roo.log(e.getTarget());
2169        // Roo.log(this.triggerEl.dom);
2170        
2171         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2172         var pel = Roo.get(e.getTarget());
2173         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2174            
2175             return;
2176         }
2177         
2178         if (this.isVisible()) {
2179             Roo.log('hide');
2180             this.hide();
2181         } else {
2182             Roo.log('show');
2183             this.show(this.triggerEl, false, false);
2184         }
2185         
2186         e.stopEvent();
2187     },
2188     
2189          
2190        
2191     
2192     hideMenuItems : function()
2193     {
2194         Roo.log("hide Menu Items");
2195         if (!this.el) { 
2196             return;
2197         }
2198         //$(backdrop).remove()
2199         this.el.select('.open',true).each(function(aa) {
2200             
2201             aa.removeClass('open');
2202           //var parent = getParent($(this))
2203           //var relatedTarget = { relatedTarget: this }
2204           
2205            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2206           //if (e.isDefaultPrevented()) return
2207            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2208         });
2209     },
2210     addxtypeChild : function (tree, cntr) {
2211         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2212           
2213         this.menuitems.add(comp);
2214         return comp;
2215
2216     },
2217     getEl : function()
2218     {
2219         Roo.log(this.el);
2220         return this.el;
2221     }
2222 });
2223
2224  
2225  /*
2226  * - LGPL
2227  *
2228  * menu item
2229  * 
2230  */
2231
2232
2233 /**
2234  * @class Roo.bootstrap.MenuItem
2235  * @extends Roo.bootstrap.Component
2236  * Bootstrap MenuItem class
2237  * @cfg {String} html the menu label
2238  * @cfg {String} href the link
2239  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2240  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2241  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2242  * @cfg {String} fa favicon to show on left of menu item.
2243  * 
2244  * 
2245  * @constructor
2246  * Create a new MenuItem
2247  * @param {Object} config The config object
2248  */
2249
2250
2251 Roo.bootstrap.MenuItem = function(config){
2252     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2253     this.addEvents({
2254         // raw events
2255         /**
2256          * @event click
2257          * The raw click event for the entire grid.
2258          * @param {Roo.bootstrap.MenuItem} this
2259          * @param {Roo.EventObject} e
2260          */
2261         "click" : true
2262     });
2263 };
2264
2265 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2266     
2267     href : false,
2268     html : false,
2269     preventDefault: true,
2270     isContainer : false,
2271     active : false,
2272     fa: false,
2273     
2274     getAutoCreate : function(){
2275         
2276         if(this.isContainer){
2277             return {
2278                 tag: 'li',
2279                 cls: 'dropdown-menu-item'
2280             };
2281         }
2282         var ctag = {
2283             tag: 'span',
2284             html: 'Link'
2285         };
2286         
2287         var anc = {
2288             tag : 'a',
2289             href : '#',
2290             cn : [  ]
2291         };
2292         
2293         if (this.fa !== false) {
2294             anc.cn.push({
2295                 tag : 'i',
2296                 cls : 'fa fa-' + this.fa
2297             });
2298         }
2299         
2300         anc.cn.push(ctag);
2301         
2302         
2303         var cfg= {
2304             tag: 'li',
2305             cls: 'dropdown-menu-item',
2306             cn: [ anc ]
2307         };
2308         if (this.parent().type == 'treeview') {
2309             cfg.cls = 'treeview-menu';
2310         }
2311         if (this.active) {
2312             cfg.cls += ' active';
2313         }
2314         
2315         
2316         
2317         anc.href = this.href || cfg.cn[0].href ;
2318         ctag.html = this.html || cfg.cn[0].html ;
2319         return cfg;
2320     },
2321     
2322     initEvents: function() {
2323         if (this.parent().type == 'treeview') {
2324             this.el.select('a').on('click', this.onClick, this);
2325         }
2326         
2327     },
2328     onClick : function(e)
2329     {
2330         Roo.log('item on click ');
2331         //if(this.preventDefault){
2332         //    e.preventDefault();
2333         //}
2334         //this.parent().hideMenuItems();
2335         
2336         this.fireEvent('click', this, e);
2337     },
2338     getEl : function()
2339     {
2340         return this.el;
2341     }
2342 });
2343
2344  
2345
2346  /*
2347  * - LGPL
2348  *
2349  * menu separator
2350  * 
2351  */
2352
2353
2354 /**
2355  * @class Roo.bootstrap.MenuSeparator
2356  * @extends Roo.bootstrap.Component
2357  * Bootstrap MenuSeparator class
2358  * 
2359  * @constructor
2360  * Create a new MenuItem
2361  * @param {Object} config The config object
2362  */
2363
2364
2365 Roo.bootstrap.MenuSeparator = function(config){
2366     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2367 };
2368
2369 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2370     
2371     getAutoCreate : function(){
2372         var cfg = {
2373             cls: 'divider',
2374             tag : 'li'
2375         };
2376         
2377         return cfg;
2378     }
2379    
2380 });
2381
2382  
2383
2384  
2385 /*
2386 * Licence: LGPL
2387 */
2388
2389 /**
2390  * @class Roo.bootstrap.Modal
2391  * @extends Roo.bootstrap.Component
2392  * Bootstrap Modal class
2393  * @cfg {String} title Title of dialog
2394  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2395  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2396  * @cfg {Boolean} specificTitle default false
2397  * @cfg {Array} buttons Array of buttons or standard button set..
2398  * @cfg {String} buttonPosition (left|right|center) default right
2399  * @cfg {Boolean} animate default true
2400  * @cfg {Boolean} allow_close default true
2401  * 
2402  * @constructor
2403  * Create a new Modal Dialog
2404  * @param {Object} config The config object
2405  */
2406
2407 Roo.bootstrap.Modal = function(config){
2408     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2409     this.addEvents({
2410         // raw events
2411         /**
2412          * @event btnclick
2413          * The raw btnclick event for the button
2414          * @param {Roo.EventObject} e
2415          */
2416         "btnclick" : true
2417     });
2418     this.buttons = this.buttons || [];
2419      
2420     if (this.tmpl) {
2421         this.tmpl = Roo.factory(this.tmpl);
2422     }
2423     
2424 };
2425
2426 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2427     
2428     title : 'test dialog',
2429    
2430     buttons : false,
2431     
2432     // set on load...
2433      
2434     html: false,
2435     
2436     tmp: false,
2437     
2438     specificTitle: false,
2439     
2440     buttonPosition: 'right',
2441     
2442     allow_close : true,
2443     
2444     animate : true,
2445     
2446     
2447      // private
2448     bodyEl:  false,
2449     footerEl:  false,
2450     titleEl:  false,
2451     closeEl:  false,
2452     
2453     
2454     onRender : function(ct, position)
2455     {
2456         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2457      
2458         if(!this.el){
2459             var cfg = Roo.apply({},  this.getAutoCreate());
2460             cfg.id = Roo.id();
2461             //if(!cfg.name){
2462             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2463             //}
2464             //if (!cfg.name.length) {
2465             //    delete cfg.name;
2466            // }
2467             if (this.cls) {
2468                 cfg.cls += ' ' + this.cls;
2469             }
2470             if (this.style) {
2471                 cfg.style = this.style;
2472             }
2473             this.el = Roo.get(document.body).createChild(cfg, position);
2474         }
2475         //var type = this.el.dom.type;
2476         
2477         
2478         if(this.tabIndex !== undefined){
2479             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2480         }
2481         
2482         
2483         this.bodyEl = this.el.select('.modal-body',true).first();
2484         this.closeEl = this.el.select('.modal-header .close', true).first();
2485         this.footerEl = this.el.select('.modal-footer',true).first();
2486         this.titleEl = this.el.select('.modal-title',true).first();
2487         
2488         
2489          
2490         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2491         this.maskEl.enableDisplayMode("block");
2492         this.maskEl.hide();
2493         //this.el.addClass("x-dlg-modal");
2494     
2495         if (this.buttons.length) {
2496             Roo.each(this.buttons, function(bb) {
2497                 var b = Roo.apply({}, bb);
2498                 b.xns = b.xns || Roo.bootstrap;
2499                 b.xtype = b.xtype || 'Button';
2500                 if (typeof(b.listeners) == 'undefined') {
2501                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2502                 }
2503                 
2504                 var btn = Roo.factory(b);
2505                 
2506                 btn.onRender(this.el.select('.modal-footer div').first());
2507                 
2508             },this);
2509         }
2510         // render the children.
2511         var nitems = [];
2512         
2513         if(typeof(this.items) != 'undefined'){
2514             var items = this.items;
2515             delete this.items;
2516
2517             for(var i =0;i < items.length;i++) {
2518                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2519             }
2520         }
2521         
2522         this.items = nitems;
2523         
2524         // where are these used - they used to be body/close/footer
2525         
2526        
2527         this.initEvents();
2528         //this.el.addClass([this.fieldClass, this.cls]);
2529         
2530     },
2531     
2532     getAutoCreate : function(){
2533         
2534         
2535         var bdy = {
2536                 cls : 'modal-body',
2537                 html : this.html || ''
2538         };
2539         
2540         var title = {
2541             tag: 'h4',
2542             cls : 'modal-title',
2543             html : this.title
2544         };
2545         
2546         if(this.specificTitle){
2547             title = this.title;
2548             
2549         };
2550         
2551         var header = [];
2552         if (this.allow_close) {
2553             header.push({
2554                 tag: 'button',
2555                 cls : 'close',
2556                 html : '&times'
2557             });
2558         }
2559         header.push(title);
2560         
2561         var modal = {
2562             cls: "modal",
2563             style : 'display: none',
2564             cn : [
2565                 {
2566                     cls: "modal-dialog",
2567                     cn : [
2568                         {
2569                             cls : "modal-content",
2570                             cn : [
2571                                 {
2572                                     cls : 'modal-header',
2573                                     cn : header
2574                                 },
2575                                 bdy,
2576                                 {
2577                                     cls : 'modal-footer',
2578                                     cn : [
2579                                         {
2580                                             tag: 'div',
2581                                             cls: 'btn-' + this.buttonPosition
2582                                         }
2583                                     ]
2584                                     
2585                                 }
2586                                 
2587                                 
2588                             ]
2589                             
2590                         }
2591                     ]
2592                         
2593                 }
2594             ]
2595         };
2596         
2597         if(this.animate){
2598             modal.cls += ' fade';
2599         }
2600         
2601         return modal;
2602           
2603     },
2604     getChildContainer : function() {
2605          
2606          return this.bodyEl;
2607         
2608     },
2609     getButtonContainer : function() {
2610          return this.el.select('.modal-footer div',true).first();
2611         
2612     },
2613     initEvents : function()
2614     {
2615         if (this.allow_close) {
2616             this.closeEl.on('click', this.hide, this);
2617         }
2618         
2619         var _this = this;
2620         
2621         window.addEventListener("resize", function() { _this.resize(); } );
2622
2623     },
2624     
2625     resize : function()
2626     {
2627         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2628     },
2629     
2630     show : function() {
2631         
2632         if (!this.rendered) {
2633             this.render();
2634         }
2635         
2636         this.el.setStyle('display', 'block');
2637         
2638         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2639             var _this = this;
2640             (function(){
2641                 this.el.addClass('in');
2642             }).defer(50, this);
2643         }else{
2644             this.el.addClass('in');
2645             
2646         }
2647         
2648         // not sure how we can show data in here.. 
2649         //if (this.tmpl) {
2650         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2651         //}
2652         
2653         Roo.get(document.body).addClass("x-body-masked");
2654         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2655         this.maskEl.show();
2656         this.el.setStyle('zIndex', '10001');
2657        
2658         this.fireEvent('show', this);
2659          
2660         
2661         
2662     },
2663     hide : function()
2664     {
2665         this.maskEl.hide();
2666         Roo.get(document.body).removeClass("x-body-masked");
2667         this.el.removeClass('in');
2668         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2669         
2670         if(this.animate){ // why
2671             var _this = this;
2672             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2673         }else{
2674             this.el.setStyle('display', 'none');
2675         }
2676         
2677         this.fireEvent('hide', this);
2678     },
2679     
2680     addButton : function(str, cb)
2681     {
2682          
2683         
2684         var b = Roo.apply({}, { html : str } );
2685         b.xns = b.xns || Roo.bootstrap;
2686         b.xtype = b.xtype || 'Button';
2687         if (typeof(b.listeners) == 'undefined') {
2688             b.listeners = { click : cb.createDelegate(this)  };
2689         }
2690         
2691         var btn = Roo.factory(b);
2692            
2693         btn.onRender(this.el.select('.modal-footer div').first());
2694         
2695         return btn;   
2696        
2697     },
2698     
2699     setDefaultButton : function(btn)
2700     {
2701         //this.el.select('.modal-footer').()
2702     },
2703     diff : false,
2704     
2705     resizeTo: function(w,h)
2706     {
2707         // skip.. ?? why??
2708         
2709         this.el.select('.modal-dialog',true).first().setWidth(w);
2710         if (this.diff === false) {
2711             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2712         }
2713         
2714         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2715         
2716         
2717     },
2718     setContentSize  : function(w, h)
2719     {
2720         
2721     },
2722     onButtonClick: function(btn,e)
2723     {
2724         //Roo.log([a,b,c]);
2725         this.fireEvent('btnclick', btn.name, e);
2726     },
2727      /**
2728      * Set the title of the Dialog
2729      * @param {String} str new Title
2730      */
2731     setTitle: function(str) {
2732         this.titleEl.dom.innerHTML = str;    
2733     },
2734     /**
2735      * Set the body of the Dialog
2736      * @param {String} str new Title
2737      */
2738     setBody: function(str) {
2739         this.bodyEl.dom.innerHTML = str;    
2740     },
2741     /**
2742      * Set the body of the Dialog using the template
2743      * @param {Obj} data - apply this data to the template and replace the body contents.
2744      */
2745     applyBody: function(obj)
2746     {
2747         if (!this.tmpl) {
2748             Roo.log("Error - using apply Body without a template");
2749             //code
2750         }
2751         this.tmpl.overwrite(this.bodyEl, obj);
2752     }
2753     
2754 });
2755
2756
2757 Roo.apply(Roo.bootstrap.Modal,  {
2758     /**
2759          * Button config that displays a single OK button
2760          * @type Object
2761          */
2762         OK :  [{
2763             name : 'ok',
2764             weight : 'primary',
2765             html : 'OK'
2766         }], 
2767         /**
2768          * Button config that displays Yes and No buttons
2769          * @type Object
2770          */
2771         YESNO : [
2772             {
2773                 name  : 'no',
2774                 html : 'No'
2775             },
2776             {
2777                 name  :'yes',
2778                 weight : 'primary',
2779                 html : 'Yes'
2780             }
2781         ],
2782         
2783         /**
2784          * Button config that displays OK and Cancel buttons
2785          * @type Object
2786          */
2787         OKCANCEL : [
2788             {
2789                name : 'cancel',
2790                 html : 'Cancel'
2791             },
2792             {
2793                 name : 'ok',
2794                 weight : 'primary',
2795                 html : 'OK'
2796             }
2797         ],
2798         /**
2799          * Button config that displays Yes, No and Cancel buttons
2800          * @type Object
2801          */
2802         YESNOCANCEL : [
2803             {
2804                 name : 'yes',
2805                 weight : 'primary',
2806                 html : 'Yes'
2807             },
2808             {
2809                 name : 'no',
2810                 html : 'No'
2811             },
2812             {
2813                 name : 'cancel',
2814                 html : 'Cancel'
2815             }
2816         ]
2817 });
2818  
2819  /*
2820  * - LGPL
2821  *
2822  * messagebox - can be used as a replace
2823  * 
2824  */
2825 /**
2826  * @class Roo.MessageBox
2827  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2828  * Example usage:
2829  *<pre><code>
2830 // Basic alert:
2831 Roo.Msg.alert('Status', 'Changes saved successfully.');
2832
2833 // Prompt for user data:
2834 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2835     if (btn == 'ok'){
2836         // process text value...
2837     }
2838 });
2839
2840 // Show a dialog using config options:
2841 Roo.Msg.show({
2842    title:'Save Changes?',
2843    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2844    buttons: Roo.Msg.YESNOCANCEL,
2845    fn: processResult,
2846    animEl: 'elId'
2847 });
2848 </code></pre>
2849  * @singleton
2850  */
2851 Roo.bootstrap.MessageBox = function(){
2852     var dlg, opt, mask, waitTimer;
2853     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2854     var buttons, activeTextEl, bwidth;
2855
2856     
2857     // private
2858     var handleButton = function(button){
2859         dlg.hide();
2860         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2861     };
2862
2863     // private
2864     var handleHide = function(){
2865         if(opt && opt.cls){
2866             dlg.el.removeClass(opt.cls);
2867         }
2868         //if(waitTimer){
2869         //    Roo.TaskMgr.stop(waitTimer);
2870         //    waitTimer = null;
2871         //}
2872     };
2873
2874     // private
2875     var updateButtons = function(b){
2876         var width = 0;
2877         if(!b){
2878             buttons["ok"].hide();
2879             buttons["cancel"].hide();
2880             buttons["yes"].hide();
2881             buttons["no"].hide();
2882             //dlg.footer.dom.style.display = 'none';
2883             return width;
2884         }
2885         dlg.footerEl.dom.style.display = '';
2886         for(var k in buttons){
2887             if(typeof buttons[k] != "function"){
2888                 if(b[k]){
2889                     buttons[k].show();
2890                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2891                     width += buttons[k].el.getWidth()+15;
2892                 }else{
2893                     buttons[k].hide();
2894                 }
2895             }
2896         }
2897         return width;
2898     };
2899
2900     // private
2901     var handleEsc = function(d, k, e){
2902         if(opt && opt.closable !== false){
2903             dlg.hide();
2904         }
2905         if(e){
2906             e.stopEvent();
2907         }
2908     };
2909
2910     return {
2911         /**
2912          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2913          * @return {Roo.BasicDialog} The BasicDialog element
2914          */
2915         getDialog : function(){
2916            if(!dlg){
2917                 dlg = new Roo.bootstrap.Modal( {
2918                     //draggable: true,
2919                     //resizable:false,
2920                     //constraintoviewport:false,
2921                     //fixedcenter:true,
2922                     //collapsible : false,
2923                     //shim:true,
2924                     //modal: true,
2925                   //  width:400,
2926                   //  height:100,
2927                     //buttonAlign:"center",
2928                     closeClick : function(){
2929                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2930                             handleButton("no");
2931                         }else{
2932                             handleButton("cancel");
2933                         }
2934                     }
2935                 });
2936                 dlg.render();
2937                 dlg.on("hide", handleHide);
2938                 mask = dlg.mask;
2939                 //dlg.addKeyListener(27, handleEsc);
2940                 buttons = {};
2941                 this.buttons = buttons;
2942                 var bt = this.buttonText;
2943                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2944                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2945                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2946                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2947                 //Roo.log(buttons);
2948                 bodyEl = dlg.bodyEl.createChild({
2949
2950                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2951                         '<textarea class="roo-mb-textarea"></textarea>' +
2952                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2953                 });
2954                 msgEl = bodyEl.dom.firstChild;
2955                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2956                 textboxEl.enableDisplayMode();
2957                 textboxEl.addKeyListener([10,13], function(){
2958                     if(dlg.isVisible() && opt && opt.buttons){
2959                         if(opt.buttons.ok){
2960                             handleButton("ok");
2961                         }else if(opt.buttons.yes){
2962                             handleButton("yes");
2963                         }
2964                     }
2965                 });
2966                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2967                 textareaEl.enableDisplayMode();
2968                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2969                 progressEl.enableDisplayMode();
2970                 var pf = progressEl.dom.firstChild;
2971                 if (pf) {
2972                     pp = Roo.get(pf.firstChild);
2973                     pp.setHeight(pf.offsetHeight);
2974                 }
2975                 
2976             }
2977             return dlg;
2978         },
2979
2980         /**
2981          * Updates the message box body text
2982          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2983          * the XHTML-compliant non-breaking space character '&amp;#160;')
2984          * @return {Roo.MessageBox} This message box
2985          */
2986         updateText : function(text){
2987             if(!dlg.isVisible() && !opt.width){
2988                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2989             }
2990             msgEl.innerHTML = text || '&#160;';
2991       
2992             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2993             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2994             var w = Math.max(
2995                     Math.min(opt.width || cw , this.maxWidth), 
2996                     Math.max(opt.minWidth || this.minWidth, bwidth)
2997             );
2998             if(opt.prompt){
2999                 activeTextEl.setWidth(w);
3000             }
3001             if(dlg.isVisible()){
3002                 dlg.fixedcenter = false;
3003             }
3004             // to big, make it scroll. = But as usual stupid IE does not support
3005             // !important..
3006             
3007             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3008                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3009                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3010             } else {
3011                 bodyEl.dom.style.height = '';
3012                 bodyEl.dom.style.overflowY = '';
3013             }
3014             if (cw > w) {
3015                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3016             } else {
3017                 bodyEl.dom.style.overflowX = '';
3018             }
3019             
3020             dlg.setContentSize(w, bodyEl.getHeight());
3021             if(dlg.isVisible()){
3022                 dlg.fixedcenter = true;
3023             }
3024             return this;
3025         },
3026
3027         /**
3028          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3029          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3030          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3031          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3032          * @return {Roo.MessageBox} This message box
3033          */
3034         updateProgress : function(value, text){
3035             if(text){
3036                 this.updateText(text);
3037             }
3038             if (pp) { // weird bug on my firefox - for some reason this is not defined
3039                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3040             }
3041             return this;
3042         },        
3043
3044         /**
3045          * Returns true if the message box is currently displayed
3046          * @return {Boolean} True if the message box is visible, else false
3047          */
3048         isVisible : function(){
3049             return dlg && dlg.isVisible();  
3050         },
3051
3052         /**
3053          * Hides the message box if it is displayed
3054          */
3055         hide : function(){
3056             if(this.isVisible()){
3057                 dlg.hide();
3058             }  
3059         },
3060
3061         /**
3062          * Displays a new message box, or reinitializes an existing message box, based on the config options
3063          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3064          * The following config object properties are supported:
3065          * <pre>
3066 Property    Type             Description
3067 ----------  ---------------  ------------------------------------------------------------------------------------
3068 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3069                                    closes (defaults to undefined)
3070 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3071                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3072 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3073                                    progress and wait dialogs will ignore this property and always hide the
3074                                    close button as they can only be closed programmatically.
3075 cls               String           A custom CSS class to apply to the message box element
3076 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3077                                    displayed (defaults to 75)
3078 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3079                                    function will be btn (the name of the button that was clicked, if applicable,
3080                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3081                                    Progress and wait dialogs will ignore this option since they do not respond to
3082                                    user actions and can only be closed programmatically, so any required function
3083                                    should be called by the same code after it closes the dialog.
3084 icon              String           A CSS class that provides a background image to be used as an icon for
3085                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3086 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3087 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3088 modal             Boolean          False to allow user interaction with the page while the message box is
3089                                    displayed (defaults to true)
3090 msg               String           A string that will replace the existing message box body text (defaults
3091                                    to the XHTML-compliant non-breaking space character '&#160;')
3092 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3093 progress          Boolean          True to display a progress bar (defaults to false)
3094 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3095 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3096 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3097 title             String           The title text
3098 value             String           The string value to set into the active textbox element if displayed
3099 wait              Boolean          True to display a progress bar (defaults to false)
3100 width             Number           The width of the dialog in pixels
3101 </pre>
3102          *
3103          * Example usage:
3104          * <pre><code>
3105 Roo.Msg.show({
3106    title: 'Address',
3107    msg: 'Please enter your address:',
3108    width: 300,
3109    buttons: Roo.MessageBox.OKCANCEL,
3110    multiline: true,
3111    fn: saveAddress,
3112    animEl: 'addAddressBtn'
3113 });
3114 </code></pre>
3115          * @param {Object} config Configuration options
3116          * @return {Roo.MessageBox} This message box
3117          */
3118         show : function(options)
3119         {
3120             
3121             // this causes nightmares if you show one dialog after another
3122             // especially on callbacks..
3123              
3124             if(this.isVisible()){
3125                 
3126                 this.hide();
3127                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3128                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3129                 Roo.log("New Dialog Message:" +  options.msg )
3130                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3131                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3132                 
3133             }
3134             var d = this.getDialog();
3135             opt = options;
3136             d.setTitle(opt.title || "&#160;");
3137             d.closeEl.setDisplayed(opt.closable !== false);
3138             activeTextEl = textboxEl;
3139             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3140             if(opt.prompt){
3141                 if(opt.multiline){
3142                     textboxEl.hide();
3143                     textareaEl.show();
3144                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3145                         opt.multiline : this.defaultTextHeight);
3146                     activeTextEl = textareaEl;
3147                 }else{
3148                     textboxEl.show();
3149                     textareaEl.hide();
3150                 }
3151             }else{
3152                 textboxEl.hide();
3153                 textareaEl.hide();
3154             }
3155             progressEl.setDisplayed(opt.progress === true);
3156             this.updateProgress(0);
3157             activeTextEl.dom.value = opt.value || "";
3158             if(opt.prompt){
3159                 dlg.setDefaultButton(activeTextEl);
3160             }else{
3161                 var bs = opt.buttons;
3162                 var db = null;
3163                 if(bs && bs.ok){
3164                     db = buttons["ok"];
3165                 }else if(bs && bs.yes){
3166                     db = buttons["yes"];
3167                 }
3168                 dlg.setDefaultButton(db);
3169             }
3170             bwidth = updateButtons(opt.buttons);
3171             this.updateText(opt.msg);
3172             if(opt.cls){
3173                 d.el.addClass(opt.cls);
3174             }
3175             d.proxyDrag = opt.proxyDrag === true;
3176             d.modal = opt.modal !== false;
3177             d.mask = opt.modal !== false ? mask : false;
3178             if(!d.isVisible()){
3179                 // force it to the end of the z-index stack so it gets a cursor in FF
3180                 document.body.appendChild(dlg.el.dom);
3181                 d.animateTarget = null;
3182                 d.show(options.animEl);
3183             }
3184             return this;
3185         },
3186
3187         /**
3188          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3189          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3190          * and closing the message box when the process is complete.
3191          * @param {String} title The title bar text
3192          * @param {String} msg The message box body text
3193          * @return {Roo.MessageBox} This message box
3194          */
3195         progress : function(title, msg){
3196             this.show({
3197                 title : title,
3198                 msg : msg,
3199                 buttons: false,
3200                 progress:true,
3201                 closable:false,
3202                 minWidth: this.minProgressWidth,
3203                 modal : true
3204             });
3205             return this;
3206         },
3207
3208         /**
3209          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3210          * If a callback function is passed it will be called after the user clicks the button, and the
3211          * id of the button that was clicked will be passed as the only parameter to the callback
3212          * (could also be the top-right close button).
3213          * @param {String} title The title bar text
3214          * @param {String} msg The message box body text
3215          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216          * @param {Object} scope (optional) The scope of the callback function
3217          * @return {Roo.MessageBox} This message box
3218          */
3219         alert : function(title, msg, fn, scope){
3220             this.show({
3221                 title : title,
3222                 msg : msg,
3223                 buttons: this.OK,
3224                 fn: fn,
3225                 scope : scope,
3226                 modal : true
3227             });
3228             return this;
3229         },
3230
3231         /**
3232          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3233          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3234          * You are responsible for closing the message box when the process is complete.
3235          * @param {String} msg The message box body text
3236          * @param {String} title (optional) The title bar text
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         wait : function(msg, title){
3240             this.show({
3241                 title : title,
3242                 msg : msg,
3243                 buttons: false,
3244                 closable:false,
3245                 progress:true,
3246                 modal:true,
3247                 width:300,
3248                 wait:true
3249             });
3250             waitTimer = Roo.TaskMgr.start({
3251                 run: function(i){
3252                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3253                 },
3254                 interval: 1000
3255             });
3256             return this;
3257         },
3258
3259         /**
3260          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3261          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3262          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3263          * @param {String} title The title bar text
3264          * @param {String} msg The message box body text
3265          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3266          * @param {Object} scope (optional) The scope of the callback function
3267          * @return {Roo.MessageBox} This message box
3268          */
3269         confirm : function(title, msg, fn, scope){
3270             this.show({
3271                 title : title,
3272                 msg : msg,
3273                 buttons: this.YESNO,
3274                 fn: fn,
3275                 scope : scope,
3276                 modal : true
3277             });
3278             return this;
3279         },
3280
3281         /**
3282          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3283          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3284          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3285          * (could also be the top-right close button) and the text that was entered will be passed as the two
3286          * parameters to the callback.
3287          * @param {String} title The title bar text
3288          * @param {String} msg The message box body text
3289          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3290          * @param {Object} scope (optional) The scope of the callback function
3291          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3292          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3293          * @return {Roo.MessageBox} This message box
3294          */
3295         prompt : function(title, msg, fn, scope, multiline){
3296             this.show({
3297                 title : title,
3298                 msg : msg,
3299                 buttons: this.OKCANCEL,
3300                 fn: fn,
3301                 minWidth:250,
3302                 scope : scope,
3303                 prompt:true,
3304                 multiline: multiline,
3305                 modal : true
3306             });
3307             return this;
3308         },
3309
3310         /**
3311          * Button config that displays a single OK button
3312          * @type Object
3313          */
3314         OK : {ok:true},
3315         /**
3316          * Button config that displays Yes and No buttons
3317          * @type Object
3318          */
3319         YESNO : {yes:true, no:true},
3320         /**
3321          * Button config that displays OK and Cancel buttons
3322          * @type Object
3323          */
3324         OKCANCEL : {ok:true, cancel:true},
3325         /**
3326          * Button config that displays Yes, No and Cancel buttons
3327          * @type Object
3328          */
3329         YESNOCANCEL : {yes:true, no:true, cancel:true},
3330
3331         /**
3332          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3333          * @type Number
3334          */
3335         defaultTextHeight : 75,
3336         /**
3337          * The maximum width in pixels of the message box (defaults to 600)
3338          * @type Number
3339          */
3340         maxWidth : 600,
3341         /**
3342          * The minimum width in pixels of the message box (defaults to 100)
3343          * @type Number
3344          */
3345         minWidth : 100,
3346         /**
3347          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3348          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3349          * @type Number
3350          */
3351         minProgressWidth : 250,
3352         /**
3353          * An object containing the default button text strings that can be overriden for localized language support.
3354          * Supported properties are: ok, cancel, yes and no.
3355          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3356          * @type Object
3357          */
3358         buttonText : {
3359             ok : "OK",
3360             cancel : "Cancel",
3361             yes : "Yes",
3362             no : "No"
3363         }
3364     };
3365 }();
3366
3367 /**
3368  * Shorthand for {@link Roo.MessageBox}
3369  */
3370 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3371 Roo.Msg = Roo.Msg || Roo.MessageBox;
3372 /*
3373  * - LGPL
3374  *
3375  * navbar
3376  * 
3377  */
3378
3379 /**
3380  * @class Roo.bootstrap.Navbar
3381  * @extends Roo.bootstrap.Component
3382  * Bootstrap Navbar class
3383
3384  * @constructor
3385  * Create a new Navbar
3386  * @param {Object} config The config object
3387  */
3388
3389
3390 Roo.bootstrap.Navbar = function(config){
3391     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3392     
3393 };
3394
3395 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3396     
3397     
3398    
3399     // private
3400     navItems : false,
3401     loadMask : false,
3402     
3403     
3404     getAutoCreate : function(){
3405         
3406         
3407         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3408         
3409     },
3410     
3411     initEvents :function ()
3412     {
3413         //Roo.log(this.el.select('.navbar-toggle',true));
3414         this.el.select('.navbar-toggle',true).on('click', function() {
3415            // Roo.log('click');
3416             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3417         }, this);
3418         
3419         var mark = {
3420             tag: "div",
3421             cls:"x-dlg-mask"
3422         };
3423         
3424         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3425         
3426         var size = this.el.getSize();
3427         this.maskEl.setSize(size.width, size.height);
3428         this.maskEl.enableDisplayMode("block");
3429         this.maskEl.hide();
3430         
3431         if(this.loadMask){
3432             this.maskEl.show();
3433         }
3434     },
3435     
3436     
3437     getChildContainer : function()
3438     {
3439         if (this.el.select('.collapse').getCount()) {
3440             return this.el.select('.collapse',true).first();
3441         }
3442         
3443         return this.el;
3444     },
3445     
3446     mask : function()
3447     {
3448         this.maskEl.show();
3449     },
3450     
3451     unmask : function()
3452     {
3453         this.maskEl.hide();
3454     } 
3455     
3456     
3457     
3458     
3459 });
3460
3461
3462
3463  
3464
3465  /*
3466  * - LGPL
3467  *
3468  * navbar
3469  * 
3470  */
3471
3472 /**
3473  * @class Roo.bootstrap.NavSimplebar
3474  * @extends Roo.bootstrap.Navbar
3475  * Bootstrap Sidebar class
3476  *
3477  * @cfg {Boolean} inverse is inverted color
3478  * 
3479  * @cfg {String} type (nav | pills | tabs)
3480  * @cfg {Boolean} arrangement stacked | justified
3481  * @cfg {String} align (left | right) alignment
3482  * 
3483  * @cfg {Boolean} main (true|false) main nav bar? default false
3484  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3485  * 
3486  * @cfg {String} tag (header|footer|nav|div) default is nav 
3487
3488  * 
3489  * 
3490  * 
3491  * @constructor
3492  * Create a new Sidebar
3493  * @param {Object} config The config object
3494  */
3495
3496
3497 Roo.bootstrap.NavSimplebar = function(config){
3498     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3499 };
3500
3501 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3502     
3503     inverse: false,
3504     
3505     type: false,
3506     arrangement: '',
3507     align : false,
3508     
3509     
3510     
3511     main : false,
3512     
3513     
3514     tag : false,
3515     
3516     
3517     getAutoCreate : function(){
3518         
3519         
3520         var cfg = {
3521             tag : this.tag || 'div',
3522             cls : 'navbar'
3523         };
3524           
3525         
3526         cfg.cn = [
3527             {
3528                 cls: 'nav',
3529                 tag : 'ul'
3530             }
3531         ];
3532         
3533          
3534         this.type = this.type || 'nav';
3535         if (['tabs','pills'].indexOf(this.type)!==-1) {
3536             cfg.cn[0].cls += ' nav-' + this.type
3537         
3538         
3539         } else {
3540             if (this.type!=='nav') {
3541                 Roo.log('nav type must be nav/tabs/pills')
3542             }
3543             cfg.cn[0].cls += ' navbar-nav'
3544         }
3545         
3546         
3547         
3548         
3549         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3550             cfg.cn[0].cls += ' nav-' + this.arrangement;
3551         }
3552         
3553         
3554         if (this.align === 'right') {
3555             cfg.cn[0].cls += ' navbar-right';
3556         }
3557         
3558         if (this.inverse) {
3559             cfg.cls += ' navbar-inverse';
3560             
3561         }
3562         
3563         
3564         return cfg;
3565     
3566         
3567     }
3568     
3569     
3570     
3571 });
3572
3573
3574
3575  
3576
3577  
3578        /*
3579  * - LGPL
3580  *
3581  * navbar
3582  * 
3583  */
3584
3585 /**
3586  * @class Roo.bootstrap.NavHeaderbar
3587  * @extends Roo.bootstrap.NavSimplebar
3588  * Bootstrap Sidebar class
3589  *
3590  * @cfg {String} brand what is brand
3591  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3592  * @cfg {String} brand_href href of the brand
3593  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3594  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3595  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3596  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3597  * 
3598  * @constructor
3599  * Create a new Sidebar
3600  * @param {Object} config The config object
3601  */
3602
3603
3604 Roo.bootstrap.NavHeaderbar = function(config){
3605     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3606       
3607 };
3608
3609 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3610     
3611     position: '',
3612     brand: '',
3613     brand_href: false,
3614     srButton : true,
3615     autohide : false,
3616     desktopCenter : false,
3617    
3618     
3619     getAutoCreate : function(){
3620         
3621         var   cfg = {
3622             tag: this.nav || 'nav',
3623             cls: 'navbar',
3624             role: 'navigation',
3625             cn: []
3626         };
3627         
3628         var cn = cfg.cn;
3629         if (this.desktopCenter) {
3630             cn.push({cls : 'container', cn : []});
3631             cn = cn[0].cn;
3632         }
3633         
3634         if(this.srButton){
3635             cn.push({
3636                 tag: 'div',
3637                 cls: 'navbar-header',
3638                 cn: [
3639                     {
3640                         tag: 'button',
3641                         type: 'button',
3642                         cls: 'navbar-toggle',
3643                         'data-toggle': 'collapse',
3644                         cn: [
3645                             {
3646                                 tag: 'span',
3647                                 cls: 'sr-only',
3648                                 html: 'Toggle navigation'
3649                             },
3650                             {
3651                                 tag: 'span',
3652                                 cls: 'icon-bar'
3653                             },
3654                             {
3655                                 tag: 'span',
3656                                 cls: 'icon-bar'
3657                             },
3658                             {
3659                                 tag: 'span',
3660                                 cls: 'icon-bar'
3661                             }
3662                         ]
3663                     }
3664                 ]
3665             });
3666         }
3667         
3668         cn.push({
3669             tag: 'div',
3670             cls: 'collapse navbar-collapse',
3671             cn : []
3672         });
3673         
3674         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3675         
3676         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3677             cfg.cls += ' navbar-' + this.position;
3678             
3679             // tag can override this..
3680             
3681             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3682         }
3683         
3684         if (this.brand !== '') {
3685             cn[0].cn.push({
3686                 tag: 'a',
3687                 href: this.brand_href ? this.brand_href : '#',
3688                 cls: 'navbar-brand',
3689                 cn: [
3690                 this.brand
3691                 ]
3692             });
3693         }
3694         
3695         if(this.main){
3696             cfg.cls += ' main-nav';
3697         }
3698         
3699         
3700         return cfg;
3701
3702         
3703     },
3704     getHeaderChildContainer : function()
3705     {
3706         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3707             return this.el.select('.navbar-header',true).first();
3708         }
3709         
3710         return this.getChildContainer();
3711     },
3712     
3713     
3714     initEvents : function()
3715     {
3716         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3717         
3718         if (this.autohide) {
3719             
3720             var prevScroll = 0;
3721             var ft = this.el;
3722             
3723             Roo.get(document).on('scroll',function(e) {
3724                 var ns = Roo.get(document).getScroll().top;
3725                 var os = prevScroll;
3726                 prevScroll = ns;
3727                 
3728                 if(ns > os){
3729                     ft.removeClass('slideDown');
3730                     ft.addClass('slideUp');
3731                     return;
3732                 }
3733                 ft.removeClass('slideUp');
3734                 ft.addClass('slideDown');
3735                  
3736               
3737           },this);
3738         }
3739     }    
3740     
3741 });
3742
3743
3744
3745  
3746
3747  /*
3748  * - LGPL
3749  *
3750  * navbar
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.NavSidebar
3756  * @extends Roo.bootstrap.Navbar
3757  * Bootstrap Sidebar class
3758  * 
3759  * @constructor
3760  * Create a new Sidebar
3761  * @param {Object} config The config object
3762  */
3763
3764
3765 Roo.bootstrap.NavSidebar = function(config){
3766     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3767 };
3768
3769 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3770     
3771     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3772     
3773     getAutoCreate : function(){
3774         
3775         
3776         return  {
3777             tag: 'div',
3778             cls: 'sidebar sidebar-nav'
3779         };
3780     
3781         
3782     }
3783     
3784     
3785     
3786 });
3787
3788
3789
3790  
3791
3792  /*
3793  * - LGPL
3794  *
3795  * nav group
3796  * 
3797  */
3798
3799 /**
3800  * @class Roo.bootstrap.NavGroup
3801  * @extends Roo.bootstrap.Component
3802  * Bootstrap NavGroup class
3803  * @cfg {String} align (left|right)
3804  * @cfg {Boolean} inverse
3805  * @cfg {String} type (nav|pills|tab) default nav
3806  * @cfg {String} navId - reference Id for navbar.
3807
3808  * 
3809  * @constructor
3810  * Create a new nav group
3811  * @param {Object} config The config object
3812  */
3813
3814 Roo.bootstrap.NavGroup = function(config){
3815     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3816     this.navItems = [];
3817    
3818     Roo.bootstrap.NavGroup.register(this);
3819      this.addEvents({
3820         /**
3821              * @event changed
3822              * Fires when the active item changes
3823              * @param {Roo.bootstrap.NavGroup} this
3824              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3825              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3826          */
3827         'changed': true
3828      });
3829     
3830 };
3831
3832 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3833     
3834     align: '',
3835     inverse: false,
3836     form: false,
3837     type: 'nav',
3838     navId : '',
3839     // private
3840     
3841     navItems : false, 
3842     
3843     getAutoCreate : function()
3844     {
3845         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3846         
3847         cfg = {
3848             tag : 'ul',
3849             cls: 'nav' 
3850         };
3851         
3852         if (['tabs','pills'].indexOf(this.type)!==-1) {
3853             cfg.cls += ' nav-' + this.type
3854         } else {
3855             if (this.type!=='nav') {
3856                 Roo.log('nav type must be nav/tabs/pills')
3857             }
3858             cfg.cls += ' navbar-nav'
3859         }
3860         
3861         if (this.parent().sidebar) {
3862             cfg = {
3863                 tag: 'ul',
3864                 cls: 'dashboard-menu sidebar-menu'
3865             };
3866             
3867             return cfg;
3868         }
3869         
3870         if (this.form === true) {
3871             cfg = {
3872                 tag: 'form',
3873                 cls: 'navbar-form'
3874             };
3875             
3876             if (this.align === 'right') {
3877                 cfg.cls += ' navbar-right';
3878             } else {
3879                 cfg.cls += ' navbar-left';
3880             }
3881         }
3882         
3883         if (this.align === 'right') {
3884             cfg.cls += ' navbar-right';
3885         }
3886         
3887         if (this.inverse) {
3888             cfg.cls += ' navbar-inverse';
3889             
3890         }
3891         
3892         
3893         return cfg;
3894     },
3895     /**
3896     * sets the active Navigation item
3897     * @param {Roo.bootstrap.NavItem} the new current navitem
3898     */
3899     setActiveItem : function(item)
3900     {
3901         var prev = false;
3902         Roo.each(this.navItems, function(v){
3903             if (v == item) {
3904                 return ;
3905             }
3906             if (v.isActive()) {
3907                 v.setActive(false, true);
3908                 prev = v;
3909                 
3910             }
3911             
3912         });
3913
3914         item.setActive(true, true);
3915         this.fireEvent('changed', this, item, prev);
3916         
3917         
3918     },
3919     /**
3920     * gets the active Navigation item
3921     * @return {Roo.bootstrap.NavItem} the current navitem
3922     */
3923     getActive : function()
3924     {
3925         
3926         var prev = false;
3927         Roo.each(this.navItems, function(v){
3928             
3929             if (v.isActive()) {
3930                 prev = v;
3931                 
3932             }
3933             
3934         });
3935         return prev;
3936     },
3937     
3938     indexOfNav : function()
3939     {
3940         
3941         var prev = false;
3942         Roo.each(this.navItems, function(v,i){
3943             
3944             if (v.isActive()) {
3945                 prev = i;
3946                 
3947             }
3948             
3949         });
3950         return prev;
3951     },
3952     /**
3953     * adds a Navigation item
3954     * @param {Roo.bootstrap.NavItem} the navitem to add
3955     */
3956     addItem : function(cfg)
3957     {
3958         var cn = new Roo.bootstrap.NavItem(cfg);
3959         this.register(cn);
3960         cn.parentId = this.id;
3961         cn.onRender(this.el, null);
3962         return cn;
3963     },
3964     /**
3965     * register a Navigation item
3966     * @param {Roo.bootstrap.NavItem} the navitem to add
3967     */
3968     register : function(item)
3969     {
3970         this.navItems.push( item);
3971         item.navId = this.navId;
3972     
3973     },
3974     
3975     /**
3976     * clear all the Navigation item
3977     */
3978    
3979     clearAll : function()
3980     {
3981         this.navItems = [];
3982         this.el.dom.innerHTML = '';
3983     },
3984     
3985     getNavItem: function(tabId)
3986     {
3987         var ret = false;
3988         Roo.each(this.navItems, function(e) {
3989             if (e.tabId == tabId) {
3990                ret =  e;
3991                return false;
3992             }
3993             return true;
3994             
3995         });
3996         return ret;
3997     },
3998     
3999     setActiveNext : function()
4000     {
4001         var i = this.indexOfNav(this.getActive());
4002         if (i > this.navItems.length) {
4003             return;
4004         }
4005         this.setActiveItem(this.navItems[i+1]);
4006     },
4007     setActivePrev : function()
4008     {
4009         var i = this.indexOfNav(this.getActive());
4010         if (i  < 1) {
4011             return;
4012         }
4013         this.setActiveItem(this.navItems[i-1]);
4014     },
4015     clearWasActive : function(except) {
4016         Roo.each(this.navItems, function(e) {
4017             if (e.tabId != except.tabId && e.was_active) {
4018                e.was_active = false;
4019                return false;
4020             }
4021             return true;
4022             
4023         });
4024     },
4025     getWasActive : function ()
4026     {
4027         var r = false;
4028         Roo.each(this.navItems, function(e) {
4029             if (e.was_active) {
4030                r = e;
4031                return false;
4032             }
4033             return true;
4034             
4035         });
4036         return r;
4037     }
4038     
4039     
4040 });
4041
4042  
4043 Roo.apply(Roo.bootstrap.NavGroup, {
4044     
4045     groups: {},
4046      /**
4047     * register a Navigation Group
4048     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4049     */
4050     register : function(navgrp)
4051     {
4052         this.groups[navgrp.navId] = navgrp;
4053         
4054     },
4055     /**
4056     * fetch a Navigation Group based on the navigation ID
4057     * @param {string} the navgroup to add
4058     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4059     */
4060     get: function(navId) {
4061         if (typeof(this.groups[navId]) == 'undefined') {
4062             return false;
4063             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4064         }
4065         return this.groups[navId] ;
4066     }
4067     
4068     
4069     
4070 });
4071
4072  /*
4073  * - LGPL
4074  *
4075  * row
4076  * 
4077  */
4078
4079 /**
4080  * @class Roo.bootstrap.NavItem
4081  * @extends Roo.bootstrap.Component
4082  * Bootstrap Navbar.NavItem class
4083  * @cfg {String} href  link to
4084  * @cfg {String} html content of button
4085  * @cfg {String} badge text inside badge
4086  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4087  * @cfg {String} glyphicon name of glyphicon
4088  * @cfg {String} icon name of font awesome icon
4089  * @cfg {Boolean} active Is item active
4090  * @cfg {Boolean} disabled Is item disabled
4091  
4092  * @cfg {Boolean} preventDefault (true | false) default false
4093  * @cfg {String} tabId the tab that this item activates.
4094  * @cfg {String} tagtype (a|span) render as a href or span?
4095  * @cfg {Boolean} animateRef (true|false) link to element default false  
4096   
4097  * @constructor
4098  * Create a new Navbar Item
4099  * @param {Object} config The config object
4100  */
4101 Roo.bootstrap.NavItem = function(config){
4102     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4103     this.addEvents({
4104         // raw events
4105         /**
4106          * @event click
4107          * The raw click event for the entire grid.
4108          * @param {Roo.EventObject} e
4109          */
4110         "click" : true,
4111          /**
4112             * @event changed
4113             * Fires when the active item active state changes
4114             * @param {Roo.bootstrap.NavItem} this
4115             * @param {boolean} state the new state
4116              
4117          */
4118         'changed': true,
4119         /**
4120             * @event scrollto
4121             * Fires when scroll to element
4122             * @param {Roo.bootstrap.NavItem} this
4123             * @param {Object} options
4124             * @param {Roo.EventObject} e
4125              
4126          */
4127         'scrollto': true
4128     });
4129    
4130 };
4131
4132 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4133     
4134     href: false,
4135     html: '',
4136     badge: '',
4137     icon: false,
4138     glyphicon: false,
4139     active: false,
4140     preventDefault : false,
4141     tabId : false,
4142     tagtype : 'a',
4143     disabled : false,
4144     animateRef : false,
4145     was_active : false,
4146     
4147     getAutoCreate : function(){
4148          
4149         var cfg = {
4150             tag: 'li',
4151             cls: 'nav-item'
4152             
4153         };
4154         
4155         if (this.active) {
4156             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4157         }
4158         if (this.disabled) {
4159             cfg.cls += ' disabled';
4160         }
4161         
4162         if (this.href || this.html || this.glyphicon || this.icon) {
4163             cfg.cn = [
4164                 {
4165                     tag: this.tagtype,
4166                     href : this.href || "#",
4167                     html: this.html || ''
4168                 }
4169             ];
4170             
4171             if (this.icon) {
4172                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4173             }
4174
4175             if(this.glyphicon) {
4176                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4177             }
4178             
4179             if (this.menu) {
4180                 
4181                 cfg.cn[0].html += " <span class='caret'></span>";
4182              
4183             }
4184             
4185             if (this.badge !== '') {
4186                  
4187                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4188             }
4189         }
4190         
4191         
4192         
4193         return cfg;
4194     },
4195     initEvents: function() 
4196     {
4197         if (typeof (this.menu) != 'undefined') {
4198             this.menu.parentType = this.xtype;
4199             this.menu.triggerEl = this.el;
4200             this.menu = this.addxtype(Roo.apply({}, this.menu));
4201         }
4202         
4203         this.el.select('a',true).on('click', this.onClick, this);
4204         
4205         if(this.tagtype == 'span'){
4206             this.el.select('span',true).on('click', this.onClick, this);
4207         }
4208        
4209         // at this point parent should be available..
4210         this.parent().register(this);
4211     },
4212     
4213     onClick : function(e)
4214     {
4215         if(
4216                 this.preventDefault || 
4217                 this.href == '#' 
4218         ){
4219             
4220             e.preventDefault();
4221         }
4222         
4223         if (this.disabled) {
4224             return;
4225         }
4226         
4227         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4228         if (tg && tg.transition) {
4229             Roo.log("waiting for the transitionend");
4230             return;
4231         }
4232         
4233         
4234         
4235         //Roo.log("fire event clicked");
4236         if(this.fireEvent('click', this, e) === false){
4237             return;
4238         };
4239         
4240         if(this.tagtype == 'span'){
4241             return;
4242         }
4243         
4244         //Roo.log(this.href);
4245         var ael = this.el.select('a',true).first();
4246         //Roo.log(ael);
4247         
4248         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4249             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4250             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4251                 return; // ignore... - it's a 'hash' to another page.
4252             }
4253             
4254             e.preventDefault();
4255             this.scrollToElement(e);
4256         }
4257         
4258         
4259         var p =  this.parent();
4260    
4261         if (['tabs','pills'].indexOf(p.type)!==-1) {
4262             if (typeof(p.setActiveItem) !== 'undefined') {
4263                 p.setActiveItem(this);
4264             }
4265         }
4266         
4267         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4268         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4269             // remove the collapsed menu expand...
4270             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4271         }
4272     },
4273     
4274     isActive: function () {
4275         return this.active
4276     },
4277     setActive : function(state, fire, is_was_active)
4278     {
4279         if (this.active && !state && this.navId) {
4280             this.was_active = true;
4281             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4282             if (nv) {
4283                 nv.clearWasActive(this);
4284             }
4285             
4286         }
4287         this.active = state;
4288         
4289         if (!state ) {
4290             this.el.removeClass('active');
4291         } else if (!this.el.hasClass('active')) {
4292             this.el.addClass('active');
4293         }
4294         if (fire) {
4295             this.fireEvent('changed', this, state);
4296         }
4297         
4298         // show a panel if it's registered and related..
4299         
4300         if (!this.navId || !this.tabId || !state || is_was_active) {
4301             return;
4302         }
4303         
4304         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4305         if (!tg) {
4306             return;
4307         }
4308         var pan = tg.getPanelByName(this.tabId);
4309         if (!pan) {
4310             return;
4311         }
4312         // if we can not flip to new panel - go back to old nav highlight..
4313         if (false == tg.showPanel(pan)) {
4314             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4315             if (nv) {
4316                 var onav = nv.getWasActive();
4317                 if (onav) {
4318                     onav.setActive(true, false, true);
4319                 }
4320             }
4321             
4322         }
4323         
4324         
4325         
4326     },
4327      // this should not be here...
4328     setDisabled : function(state)
4329     {
4330         this.disabled = state;
4331         if (!state ) {
4332             this.el.removeClass('disabled');
4333         } else if (!this.el.hasClass('disabled')) {
4334             this.el.addClass('disabled');
4335         }
4336         
4337     },
4338     
4339     /**
4340      * Fetch the element to display the tooltip on.
4341      * @return {Roo.Element} defaults to this.el
4342      */
4343     tooltipEl : function()
4344     {
4345         return this.el.select('' + this.tagtype + '', true).first();
4346     },
4347     
4348     scrollToElement : function(e)
4349     {
4350         var c = document.body;
4351         
4352         /*
4353          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4354          */
4355         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4356             c = document.documentElement;
4357         }
4358         
4359         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4360         
4361         if(!target){
4362             return;
4363         }
4364
4365         var o = target.calcOffsetsTo(c);
4366         
4367         var options = {
4368             target : target,
4369             value : o[1]
4370         };
4371         
4372         this.fireEvent('scrollto', this, options, e);
4373         
4374         Roo.get(c).scrollTo('top', options.value, true);
4375         
4376         return;
4377     }
4378 });
4379  
4380
4381  /*
4382  * - LGPL
4383  *
4384  * sidebar item
4385  *
4386  *  li
4387  *    <span> icon </span>
4388  *    <span> text </span>
4389  *    <span>badge </span>
4390  */
4391
4392 /**
4393  * @class Roo.bootstrap.NavSidebarItem
4394  * @extends Roo.bootstrap.NavItem
4395  * Bootstrap Navbar.NavSidebarItem class
4396  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4397  * {bool} open is the menu open
4398  * @constructor
4399  * Create a new Navbar Button
4400  * @param {Object} config The config object
4401  */
4402 Roo.bootstrap.NavSidebarItem = function(config){
4403     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4404     this.addEvents({
4405         // raw events
4406         /**
4407          * @event click
4408          * The raw click event for the entire grid.
4409          * @param {Roo.EventObject} e
4410          */
4411         "click" : true,
4412          /**
4413             * @event changed
4414             * Fires when the active item active state changes
4415             * @param {Roo.bootstrap.NavSidebarItem} this
4416             * @param {boolean} state the new state
4417              
4418          */
4419         'changed': true
4420     });
4421    
4422 };
4423
4424 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4425     
4426     badgeWeight : 'default',
4427     
4428     open: false,
4429     
4430     getAutoCreate : function(){
4431         
4432         
4433         var a = {
4434                 tag: 'a',
4435                 href : this.href || '#',
4436                 cls: '',
4437                 html : '',
4438                 cn : []
4439         };
4440         var cfg = {
4441             tag: 'li',
4442             cls: '',
4443             cn: [ a ]
4444         };
4445         var span = {
4446             tag: 'span',
4447             html : this.html || ''
4448         };
4449         
4450         
4451         if (this.active) {
4452             cfg.cls += ' active';
4453         }
4454         
4455         if (this.disabled) {
4456             cfg.cls += ' disabled';
4457         }
4458         if (this.open) {
4459             cfg.cls += ' open x-open';
4460         }
4461         // left icon..
4462         if (this.glyphicon || this.icon) {
4463             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4464             a.cn.push({ tag : 'i', cls : c }) ;
4465         }
4466         // html..
4467         a.cn.push(span);
4468         // then badge..
4469         if (this.badge !== '') {
4470             
4471             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4472         }
4473         // fi
4474         if (this.menu) {
4475             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4476             a.cls += 'dropdown-toggle treeview' ;
4477             
4478         }
4479         
4480         
4481         
4482         return cfg;
4483          
4484            
4485     },
4486     
4487     initEvents : function()
4488     { 
4489         if (typeof (this.menu) != 'undefined') {
4490             this.menu.parentType = this.xtype;
4491             this.menu.triggerEl = this.el;
4492             this.menu = this.addxtype(Roo.apply({}, this.menu));
4493         }
4494         
4495         this.el.on('click', this.onClick, this);
4496        
4497     
4498         if(this.badge !== ''){
4499  
4500             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4501         }
4502         
4503     },
4504     
4505     onClick : function(e)
4506     {
4507         if(this.disabled){
4508             e.preventDefault();
4509             return;
4510         }
4511         
4512         if(this.preventDefault){
4513             e.preventDefault();
4514         }
4515         
4516         this.fireEvent('click', this);
4517     },
4518     
4519     disable : function()
4520     {
4521         this.setDisabled(true);
4522     },
4523     
4524     enable : function()
4525     {
4526         this.setDisabled(false);
4527     },
4528     
4529     setDisabled : function(state)
4530     {
4531         if(this.disabled == state){
4532             return;
4533         }
4534         
4535         this.disabled = state;
4536         
4537         if (state) {
4538             this.el.addClass('disabled');
4539             return;
4540         }
4541         
4542         this.el.removeClass('disabled');
4543         
4544         return;
4545     },
4546     
4547     setActive : function(state)
4548     {
4549         if(this.active == state){
4550             return;
4551         }
4552         
4553         this.active = state;
4554         
4555         if (state) {
4556             this.el.addClass('active');
4557             return;
4558         }
4559         
4560         this.el.removeClass('active');
4561         
4562         return;
4563     },
4564     
4565     isActive: function () 
4566     {
4567         return this.active;
4568     },
4569     
4570     setBadge : function(str)
4571     {
4572         if(!this.badgeEl){
4573             return;
4574         }
4575         
4576         this.badgeEl.dom.innerHTML = str;
4577     }
4578     
4579    
4580      
4581  
4582 });
4583  
4584
4585  /*
4586  * - LGPL
4587  *
4588  * row
4589  * 
4590  */
4591
4592 /**
4593  * @class Roo.bootstrap.Row
4594  * @extends Roo.bootstrap.Component
4595  * Bootstrap Row class (contains columns...)
4596  * 
4597  * @constructor
4598  * Create a new Row
4599  * @param {Object} config The config object
4600  */
4601
4602 Roo.bootstrap.Row = function(config){
4603     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4604 };
4605
4606 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4607     
4608     getAutoCreate : function(){
4609        return {
4610             cls: 'row clearfix'
4611        };
4612     }
4613     
4614     
4615 });
4616
4617  
4618
4619  /*
4620  * - LGPL
4621  *
4622  * element
4623  * 
4624  */
4625
4626 /**
4627  * @class Roo.bootstrap.Element
4628  * @extends Roo.bootstrap.Component
4629  * Bootstrap Element class
4630  * @cfg {String} html contents of the element
4631  * @cfg {String} tag tag of the element
4632  * @cfg {String} cls class of the element
4633  * @cfg {Boolean} preventDefault (true|false) default false
4634  * @cfg {Boolean} clickable (true|false) default false
4635  * 
4636  * @constructor
4637  * Create a new Element
4638  * @param {Object} config The config object
4639  */
4640
4641 Roo.bootstrap.Element = function(config){
4642     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4643     
4644     this.addEvents({
4645         // raw events
4646         /**
4647          * @event click
4648          * When a element is chick
4649          * @param {Roo.bootstrap.Element} this
4650          * @param {Roo.EventObject} e
4651          */
4652         "click" : true
4653     });
4654 };
4655
4656 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4657     
4658     tag: 'div',
4659     cls: '',
4660     html: '',
4661     preventDefault: false, 
4662     clickable: false,
4663     
4664     getAutoCreate : function(){
4665         
4666         var cfg = {
4667             tag: this.tag,
4668             cls: this.cls,
4669             html: this.html
4670         };
4671         
4672         return cfg;
4673     },
4674     
4675     initEvents: function() 
4676     {
4677         Roo.bootstrap.Element.superclass.initEvents.call(this);
4678         
4679         if(this.clickable){
4680             this.el.on('click', this.onClick, this);
4681         }
4682         
4683     },
4684     
4685     onClick : function(e)
4686     {
4687         if(this.preventDefault){
4688             e.preventDefault();
4689         }
4690         
4691         this.fireEvent('click', this, e);
4692     },
4693     
4694     getValue : function()
4695     {
4696         return this.el.dom.innerHTML;
4697     },
4698     
4699     setValue : function(value)
4700     {
4701         this.el.dom.innerHTML = value;
4702     }
4703    
4704 });
4705
4706  
4707
4708  /*
4709  * - LGPL
4710  *
4711  * pagination
4712  * 
4713  */
4714
4715 /**
4716  * @class Roo.bootstrap.Pagination
4717  * @extends Roo.bootstrap.Component
4718  * Bootstrap Pagination class
4719  * @cfg {String} size xs | sm | md | lg
4720  * @cfg {Boolean} inverse false | true
4721  * 
4722  * @constructor
4723  * Create a new Pagination
4724  * @param {Object} config The config object
4725  */
4726
4727 Roo.bootstrap.Pagination = function(config){
4728     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4729 };
4730
4731 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4732     
4733     cls: false,
4734     size: false,
4735     inverse: false,
4736     
4737     getAutoCreate : function(){
4738         var cfg = {
4739             tag: 'ul',
4740                 cls: 'pagination'
4741         };
4742         if (this.inverse) {
4743             cfg.cls += ' inverse';
4744         }
4745         if (this.html) {
4746             cfg.html=this.html;
4747         }
4748         if (this.cls) {
4749             cfg.cls += " " + this.cls;
4750         }
4751         return cfg;
4752     }
4753    
4754 });
4755
4756  
4757
4758  /*
4759  * - LGPL
4760  *
4761  * Pagination item
4762  * 
4763  */
4764
4765
4766 /**
4767  * @class Roo.bootstrap.PaginationItem
4768  * @extends Roo.bootstrap.Component
4769  * Bootstrap PaginationItem class
4770  * @cfg {String} html text
4771  * @cfg {String} href the link
4772  * @cfg {Boolean} preventDefault (true | false) default true
4773  * @cfg {Boolean} active (true | false) default false
4774  * @cfg {Boolean} disabled default false
4775  * 
4776  * 
4777  * @constructor
4778  * Create a new PaginationItem
4779  * @param {Object} config The config object
4780  */
4781
4782
4783 Roo.bootstrap.PaginationItem = function(config){
4784     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4785     this.addEvents({
4786         // raw events
4787         /**
4788          * @event click
4789          * The raw click event for the entire grid.
4790          * @param {Roo.EventObject} e
4791          */
4792         "click" : true
4793     });
4794 };
4795
4796 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4797     
4798     href : false,
4799     html : false,
4800     preventDefault: true,
4801     active : false,
4802     cls : false,
4803     disabled: false,
4804     
4805     getAutoCreate : function(){
4806         var cfg= {
4807             tag: 'li',
4808             cn: [
4809                 {
4810                     tag : 'a',
4811                     href : this.href ? this.href : '#',
4812                     html : this.html ? this.html : ''
4813                 }
4814             ]
4815         };
4816         
4817         if(this.cls){
4818             cfg.cls = this.cls;
4819         }
4820         
4821         if(this.disabled){
4822             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4823         }
4824         
4825         if(this.active){
4826             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4827         }
4828         
4829         return cfg;
4830     },
4831     
4832     initEvents: function() {
4833         
4834         this.el.on('click', this.onClick, this);
4835         
4836     },
4837     onClick : function(e)
4838     {
4839         Roo.log('PaginationItem on click ');
4840         if(this.preventDefault){
4841             e.preventDefault();
4842         }
4843         
4844         if(this.disabled){
4845             return;
4846         }
4847         
4848         this.fireEvent('click', this, e);
4849     }
4850    
4851 });
4852
4853  
4854
4855  /*
4856  * - LGPL
4857  *
4858  * slider
4859  * 
4860  */
4861
4862
4863 /**
4864  * @class Roo.bootstrap.Slider
4865  * @extends Roo.bootstrap.Component
4866  * Bootstrap Slider class
4867  *    
4868  * @constructor
4869  * Create a new Slider
4870  * @param {Object} config The config object
4871  */
4872
4873 Roo.bootstrap.Slider = function(config){
4874     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4875 };
4876
4877 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4878     
4879     getAutoCreate : function(){
4880         
4881         var cfg = {
4882             tag: 'div',
4883             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4884             cn: [
4885                 {
4886                     tag: 'a',
4887                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4888                 }
4889             ]
4890         };
4891         
4892         return cfg;
4893     }
4894    
4895 });
4896
4897  /*
4898  * Based on:
4899  * Ext JS Library 1.1.1
4900  * Copyright(c) 2006-2007, Ext JS, LLC.
4901  *
4902  * Originally Released Under LGPL - original licence link has changed is not relivant.
4903  *
4904  * Fork - LGPL
4905  * <script type="text/javascript">
4906  */
4907  
4908
4909 /**
4910  * @class Roo.grid.ColumnModel
4911  * @extends Roo.util.Observable
4912  * This is the default implementation of a ColumnModel used by the Grid. It defines
4913  * the columns in the grid.
4914  * <br>Usage:<br>
4915  <pre><code>
4916  var colModel = new Roo.grid.ColumnModel([
4917         {header: "Ticker", width: 60, sortable: true, locked: true},
4918         {header: "Company Name", width: 150, sortable: true},
4919         {header: "Market Cap.", width: 100, sortable: true},
4920         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4921         {header: "Employees", width: 100, sortable: true, resizable: false}
4922  ]);
4923  </code></pre>
4924  * <p>
4925  
4926  * The config options listed for this class are options which may appear in each
4927  * individual column definition.
4928  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4929  * @constructor
4930  * @param {Object} config An Array of column config objects. See this class's
4931  * config objects for details.
4932 */
4933 Roo.grid.ColumnModel = function(config){
4934         /**
4935      * The config passed into the constructor
4936      */
4937     this.config = config;
4938     this.lookup = {};
4939
4940     // if no id, create one
4941     // if the column does not have a dataIndex mapping,
4942     // map it to the order it is in the config
4943     for(var i = 0, len = config.length; i < len; i++){
4944         var c = config[i];
4945         if(typeof c.dataIndex == "undefined"){
4946             c.dataIndex = i;
4947         }
4948         if(typeof c.renderer == "string"){
4949             c.renderer = Roo.util.Format[c.renderer];
4950         }
4951         if(typeof c.id == "undefined"){
4952             c.id = Roo.id();
4953         }
4954         if(c.editor && c.editor.xtype){
4955             c.editor  = Roo.factory(c.editor, Roo.grid);
4956         }
4957         if(c.editor && c.editor.isFormField){
4958             c.editor = new Roo.grid.GridEditor(c.editor);
4959         }
4960         this.lookup[c.id] = c;
4961     }
4962
4963     /**
4964      * The width of columns which have no width specified (defaults to 100)
4965      * @type Number
4966      */
4967     this.defaultWidth = 100;
4968
4969     /**
4970      * Default sortable of columns which have no sortable specified (defaults to false)
4971      * @type Boolean
4972      */
4973     this.defaultSortable = false;
4974
4975     this.addEvents({
4976         /**
4977              * @event widthchange
4978              * Fires when the width of a column changes.
4979              * @param {ColumnModel} this
4980              * @param {Number} columnIndex The column index
4981              * @param {Number} newWidth The new width
4982              */
4983             "widthchange": true,
4984         /**
4985              * @event headerchange
4986              * Fires when the text of a header changes.
4987              * @param {ColumnModel} this
4988              * @param {Number} columnIndex The column index
4989              * @param {Number} newText The new header text
4990              */
4991             "headerchange": true,
4992         /**
4993              * @event hiddenchange
4994              * Fires when a column is hidden or "unhidden".
4995              * @param {ColumnModel} this
4996              * @param {Number} columnIndex The column index
4997              * @param {Boolean} hidden true if hidden, false otherwise
4998              */
4999             "hiddenchange": true,
5000             /**
5001          * @event columnmoved
5002          * Fires when a column is moved.
5003          * @param {ColumnModel} this
5004          * @param {Number} oldIndex
5005          * @param {Number} newIndex
5006          */
5007         "columnmoved" : true,
5008         /**
5009          * @event columlockchange
5010          * Fires when a column's locked state is changed
5011          * @param {ColumnModel} this
5012          * @param {Number} colIndex
5013          * @param {Boolean} locked true if locked
5014          */
5015         "columnlockchange" : true
5016     });
5017     Roo.grid.ColumnModel.superclass.constructor.call(this);
5018 };
5019 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5020     /**
5021      * @cfg {String} header The header text to display in the Grid view.
5022      */
5023     /**
5024      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5025      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5026      * specified, the column's index is used as an index into the Record's data Array.
5027      */
5028     /**
5029      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5030      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5031      */
5032     /**
5033      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5034      * Defaults to the value of the {@link #defaultSortable} property.
5035      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5036      */
5037     /**
5038      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5039      */
5040     /**
5041      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5042      */
5043     /**
5044      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5045      */
5046     /**
5047      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5048      */
5049     /**
5050      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5051      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5052      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5053      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5054      */
5055        /**
5056      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5057      */
5058     /**
5059      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5060      */
5061     /**
5062      * @cfg {String} cursor (Optional)
5063      */
5064     /**
5065      * @cfg {String} tooltip (Optional)
5066      */
5067     /**
5068      * @cfg {Number} xs (Optional)
5069      */
5070     /**
5071      * @cfg {Number} sm (Optional)
5072      */
5073     /**
5074      * @cfg {Number} md (Optional)
5075      */
5076     /**
5077      * @cfg {Number} lg (Optional)
5078      */
5079     /**
5080      * Returns the id of the column at the specified index.
5081      * @param {Number} index The column index
5082      * @return {String} the id
5083      */
5084     getColumnId : function(index){
5085         return this.config[index].id;
5086     },
5087
5088     /**
5089      * Returns the column for a specified id.
5090      * @param {String} id The column id
5091      * @return {Object} the column
5092      */
5093     getColumnById : function(id){
5094         return this.lookup[id];
5095     },
5096
5097     
5098     /**
5099      * Returns the column for a specified dataIndex.
5100      * @param {String} dataIndex The column dataIndex
5101      * @return {Object|Boolean} the column or false if not found
5102      */
5103     getColumnByDataIndex: function(dataIndex){
5104         var index = this.findColumnIndex(dataIndex);
5105         return index > -1 ? this.config[index] : false;
5106     },
5107     
5108     /**
5109      * Returns the index for a specified column id.
5110      * @param {String} id The column id
5111      * @return {Number} the index, or -1 if not found
5112      */
5113     getIndexById : function(id){
5114         for(var i = 0, len = this.config.length; i < len; i++){
5115             if(this.config[i].id == id){
5116                 return i;
5117             }
5118         }
5119         return -1;
5120     },
5121     
5122     /**
5123      * Returns the index for a specified column dataIndex.
5124      * @param {String} dataIndex The column dataIndex
5125      * @return {Number} the index, or -1 if not found
5126      */
5127     
5128     findColumnIndex : function(dataIndex){
5129         for(var i = 0, len = this.config.length; i < len; i++){
5130             if(this.config[i].dataIndex == dataIndex){
5131                 return i;
5132             }
5133         }
5134         return -1;
5135     },
5136     
5137     
5138     moveColumn : function(oldIndex, newIndex){
5139         var c = this.config[oldIndex];
5140         this.config.splice(oldIndex, 1);
5141         this.config.splice(newIndex, 0, c);
5142         this.dataMap = null;
5143         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5144     },
5145
5146     isLocked : function(colIndex){
5147         return this.config[colIndex].locked === true;
5148     },
5149
5150     setLocked : function(colIndex, value, suppressEvent){
5151         if(this.isLocked(colIndex) == value){
5152             return;
5153         }
5154         this.config[colIndex].locked = value;
5155         if(!suppressEvent){
5156             this.fireEvent("columnlockchange", this, colIndex, value);
5157         }
5158     },
5159
5160     getTotalLockedWidth : function(){
5161         var totalWidth = 0;
5162         for(var i = 0; i < this.config.length; i++){
5163             if(this.isLocked(i) && !this.isHidden(i)){
5164                 this.totalWidth += this.getColumnWidth(i);
5165             }
5166         }
5167         return totalWidth;
5168     },
5169
5170     getLockedCount : function(){
5171         for(var i = 0, len = this.config.length; i < len; i++){
5172             if(!this.isLocked(i)){
5173                 return i;
5174             }
5175         }
5176         
5177         return this.config.length;
5178     },
5179
5180     /**
5181      * Returns the number of columns.
5182      * @return {Number}
5183      */
5184     getColumnCount : function(visibleOnly){
5185         if(visibleOnly === true){
5186             var c = 0;
5187             for(var i = 0, len = this.config.length; i < len; i++){
5188                 if(!this.isHidden(i)){
5189                     c++;
5190                 }
5191             }
5192             return c;
5193         }
5194         return this.config.length;
5195     },
5196
5197     /**
5198      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5199      * @param {Function} fn
5200      * @param {Object} scope (optional)
5201      * @return {Array} result
5202      */
5203     getColumnsBy : function(fn, scope){
5204         var r = [];
5205         for(var i = 0, len = this.config.length; i < len; i++){
5206             var c = this.config[i];
5207             if(fn.call(scope||this, c, i) === true){
5208                 r[r.length] = c;
5209             }
5210         }
5211         return r;
5212     },
5213
5214     /**
5215      * Returns true if the specified column is sortable.
5216      * @param {Number} col The column index
5217      * @return {Boolean}
5218      */
5219     isSortable : function(col){
5220         if(typeof this.config[col].sortable == "undefined"){
5221             return this.defaultSortable;
5222         }
5223         return this.config[col].sortable;
5224     },
5225
5226     /**
5227      * Returns the rendering (formatting) function defined for the column.
5228      * @param {Number} col The column index.
5229      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5230      */
5231     getRenderer : function(col){
5232         if(!this.config[col].renderer){
5233             return Roo.grid.ColumnModel.defaultRenderer;
5234         }
5235         return this.config[col].renderer;
5236     },
5237
5238     /**
5239      * Sets the rendering (formatting) function for a column.
5240      * @param {Number} col The column index
5241      * @param {Function} fn The function to use to process the cell's raw data
5242      * to return HTML markup for the grid view. The render function is called with
5243      * the following parameters:<ul>
5244      * <li>Data value.</li>
5245      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5246      * <li>css A CSS style string to apply to the table cell.</li>
5247      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5248      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5249      * <li>Row index</li>
5250      * <li>Column index</li>
5251      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5252      */
5253     setRenderer : function(col, fn){
5254         this.config[col].renderer = fn;
5255     },
5256
5257     /**
5258      * Returns the width for the specified column.
5259      * @param {Number} col The column index
5260      * @return {Number}
5261      */
5262     getColumnWidth : function(col){
5263         return this.config[col].width * 1 || this.defaultWidth;
5264     },
5265
5266     /**
5267      * Sets the width for a column.
5268      * @param {Number} col The column index
5269      * @param {Number} width The new width
5270      */
5271     setColumnWidth : function(col, width, suppressEvent){
5272         this.config[col].width = width;
5273         this.totalWidth = null;
5274         if(!suppressEvent){
5275              this.fireEvent("widthchange", this, col, width);
5276         }
5277     },
5278
5279     /**
5280      * Returns the total width of all columns.
5281      * @param {Boolean} includeHidden True to include hidden column widths
5282      * @return {Number}
5283      */
5284     getTotalWidth : function(includeHidden){
5285         if(!this.totalWidth){
5286             this.totalWidth = 0;
5287             for(var i = 0, len = this.config.length; i < len; i++){
5288                 if(includeHidden || !this.isHidden(i)){
5289                     this.totalWidth += this.getColumnWidth(i);
5290                 }
5291             }
5292         }
5293         return this.totalWidth;
5294     },
5295
5296     /**
5297      * Returns the header for the specified column.
5298      * @param {Number} col The column index
5299      * @return {String}
5300      */
5301     getColumnHeader : function(col){
5302         return this.config[col].header;
5303     },
5304
5305     /**
5306      * Sets the header for a column.
5307      * @param {Number} col The column index
5308      * @param {String} header The new header
5309      */
5310     setColumnHeader : function(col, header){
5311         this.config[col].header = header;
5312         this.fireEvent("headerchange", this, col, header);
5313     },
5314
5315     /**
5316      * Returns the tooltip for the specified column.
5317      * @param {Number} col The column index
5318      * @return {String}
5319      */
5320     getColumnTooltip : function(col){
5321             return this.config[col].tooltip;
5322     },
5323     /**
5324      * Sets the tooltip for a column.
5325      * @param {Number} col The column index
5326      * @param {String} tooltip The new tooltip
5327      */
5328     setColumnTooltip : function(col, tooltip){
5329             this.config[col].tooltip = tooltip;
5330     },
5331
5332     /**
5333      * Returns the dataIndex for the specified column.
5334      * @param {Number} col The column index
5335      * @return {Number}
5336      */
5337     getDataIndex : function(col){
5338         return this.config[col].dataIndex;
5339     },
5340
5341     /**
5342      * Sets the dataIndex for a column.
5343      * @param {Number} col The column index
5344      * @param {Number} dataIndex The new dataIndex
5345      */
5346     setDataIndex : function(col, dataIndex){
5347         this.config[col].dataIndex = dataIndex;
5348     },
5349
5350     
5351     
5352     /**
5353      * Returns true if the cell is editable.
5354      * @param {Number} colIndex The column index
5355      * @param {Number} rowIndex The row index - this is nto actually used..?
5356      * @return {Boolean}
5357      */
5358     isCellEditable : function(colIndex, rowIndex){
5359         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5360     },
5361
5362     /**
5363      * Returns the editor defined for the cell/column.
5364      * return false or null to disable editing.
5365      * @param {Number} colIndex The column index
5366      * @param {Number} rowIndex The row index
5367      * @return {Object}
5368      */
5369     getCellEditor : function(colIndex, rowIndex){
5370         return this.config[colIndex].editor;
5371     },
5372
5373     /**
5374      * Sets if a column is editable.
5375      * @param {Number} col The column index
5376      * @param {Boolean} editable True if the column is editable
5377      */
5378     setEditable : function(col, editable){
5379         this.config[col].editable = editable;
5380     },
5381
5382
5383     /**
5384      * Returns true if the column is hidden.
5385      * @param {Number} colIndex The column index
5386      * @return {Boolean}
5387      */
5388     isHidden : function(colIndex){
5389         return this.config[colIndex].hidden;
5390     },
5391
5392
5393     /**
5394      * Returns true if the column width cannot be changed
5395      */
5396     isFixed : function(colIndex){
5397         return this.config[colIndex].fixed;
5398     },
5399
5400     /**
5401      * Returns true if the column can be resized
5402      * @return {Boolean}
5403      */
5404     isResizable : function(colIndex){
5405         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5406     },
5407     /**
5408      * Sets if a column is hidden.
5409      * @param {Number} colIndex The column index
5410      * @param {Boolean} hidden True if the column is hidden
5411      */
5412     setHidden : function(colIndex, hidden){
5413         this.config[colIndex].hidden = hidden;
5414         this.totalWidth = null;
5415         this.fireEvent("hiddenchange", this, colIndex, hidden);
5416     },
5417
5418     /**
5419      * Sets the editor for a column.
5420      * @param {Number} col The column index
5421      * @param {Object} editor The editor object
5422      */
5423     setEditor : function(col, editor){
5424         this.config[col].editor = editor;
5425     }
5426 });
5427
5428 Roo.grid.ColumnModel.defaultRenderer = function(value){
5429         if(typeof value == "string" && value.length < 1){
5430             return "&#160;";
5431         }
5432         return value;
5433 };
5434
5435 // Alias for backwards compatibility
5436 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5437 /*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447  
5448 /**
5449  * @class Roo.LoadMask
5450  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5451  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5452  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5453  * element's UpdateManager load indicator and will be destroyed after the initial load.
5454  * @constructor
5455  * Create a new LoadMask
5456  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5457  * @param {Object} config The config object
5458  */
5459 Roo.LoadMask = function(el, config){
5460     this.el = Roo.get(el);
5461     Roo.apply(this, config);
5462     if(this.store){
5463         this.store.on('beforeload', this.onBeforeLoad, this);
5464         this.store.on('load', this.onLoad, this);
5465         this.store.on('loadexception', this.onLoadException, this);
5466         this.removeMask = false;
5467     }else{
5468         var um = this.el.getUpdateManager();
5469         um.showLoadIndicator = false; // disable the default indicator
5470         um.on('beforeupdate', this.onBeforeLoad, this);
5471         um.on('update', this.onLoad, this);
5472         um.on('failure', this.onLoad, this);
5473         this.removeMask = true;
5474     }
5475 };
5476
5477 Roo.LoadMask.prototype = {
5478     /**
5479      * @cfg {Boolean} removeMask
5480      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5481      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5482      */
5483     /**
5484      * @cfg {String} msg
5485      * The text to display in a centered loading message box (defaults to 'Loading...')
5486      */
5487     msg : 'Loading...',
5488     /**
5489      * @cfg {String} msgCls
5490      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5491      */
5492     msgCls : 'x-mask-loading',
5493
5494     /**
5495      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5496      * @type Boolean
5497      */
5498     disabled: false,
5499
5500     /**
5501      * Disables the mask to prevent it from being displayed
5502      */
5503     disable : function(){
5504        this.disabled = true;
5505     },
5506
5507     /**
5508      * Enables the mask so that it can be displayed
5509      */
5510     enable : function(){
5511         this.disabled = false;
5512     },
5513     
5514     onLoadException : function()
5515     {
5516         Roo.log(arguments);
5517         
5518         if (typeof(arguments[3]) != 'undefined') {
5519             Roo.MessageBox.alert("Error loading",arguments[3]);
5520         } 
5521         /*
5522         try {
5523             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5524                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5525             }   
5526         } catch(e) {
5527             
5528         }
5529         */
5530     
5531         
5532         
5533         this.el.unmask(this.removeMask);
5534     },
5535     // private
5536     onLoad : function()
5537     {
5538         this.el.unmask(this.removeMask);
5539     },
5540
5541     // private
5542     onBeforeLoad : function(){
5543         if(!this.disabled){
5544             this.el.mask(this.msg, this.msgCls);
5545         }
5546     },
5547
5548     // private
5549     destroy : function(){
5550         if(this.store){
5551             this.store.un('beforeload', this.onBeforeLoad, this);
5552             this.store.un('load', this.onLoad, this);
5553             this.store.un('loadexception', this.onLoadException, this);
5554         }else{
5555             var um = this.el.getUpdateManager();
5556             um.un('beforeupdate', this.onBeforeLoad, this);
5557             um.un('update', this.onLoad, this);
5558             um.un('failure', this.onLoad, this);
5559         }
5560     }
5561 };/*
5562  * - LGPL
5563  *
5564  * table
5565  * 
5566  */
5567
5568 /**
5569  * @class Roo.bootstrap.Table
5570  * @extends Roo.bootstrap.Component
5571  * Bootstrap Table class
5572  * @cfg {String} cls table class
5573  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5574  * @cfg {String} bgcolor Specifies the background color for a table
5575  * @cfg {Number} border Specifies whether the table cells should have borders or not
5576  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5577  * @cfg {Number} cellspacing Specifies the space between cells
5578  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5579  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5580  * @cfg {String} sortable Specifies that the table should be sortable
5581  * @cfg {String} summary Specifies a summary of the content of a table
5582  * @cfg {Number} width Specifies the width of a table
5583  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5584  * 
5585  * @cfg {boolean} striped Should the rows be alternative striped
5586  * @cfg {boolean} bordered Add borders to the table
5587  * @cfg {boolean} hover Add hover highlighting
5588  * @cfg {boolean} condensed Format condensed
5589  * @cfg {boolean} responsive Format condensed
5590  * @cfg {Boolean} loadMask (true|false) default false
5591  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5592  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5593  * @cfg {Boolean} rowSelection (true|false) default false
5594  * @cfg {Boolean} cellSelection (true|false) default false
5595  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5596  
5597  * 
5598  * @constructor
5599  * Create a new Table
5600  * @param {Object} config The config object
5601  */
5602
5603 Roo.bootstrap.Table = function(config){
5604     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5605     
5606     // BC...
5607     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5608     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5609     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5610     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5611     
5612     
5613     if (this.sm) {
5614         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5615         this.sm = this.selModel;
5616         this.sm.xmodule = this.xmodule || false;
5617     }
5618     if (this.cm && typeof(this.cm.config) == 'undefined') {
5619         this.colModel = new Roo.grid.ColumnModel(this.cm);
5620         this.cm = this.colModel;
5621         this.cm.xmodule = this.xmodule || false;
5622     }
5623     if (this.store) {
5624         this.store= Roo.factory(this.store, Roo.data);
5625         this.ds = this.store;
5626         this.ds.xmodule = this.xmodule || false;
5627          
5628     }
5629     if (this.footer && this.store) {
5630         this.footer.dataSource = this.ds;
5631         this.footer = Roo.factory(this.footer);
5632     }
5633     
5634     /** @private */
5635     this.addEvents({
5636         /**
5637          * @event cellclick
5638          * Fires when a cell is clicked
5639          * @param {Roo.bootstrap.Table} this
5640          * @param {Roo.Element} el
5641          * @param {Number} rowIndex
5642          * @param {Number} columnIndex
5643          * @param {Roo.EventObject} e
5644          */
5645         "cellclick" : true,
5646         /**
5647          * @event celldblclick
5648          * Fires when a cell is double clicked
5649          * @param {Roo.bootstrap.Table} this
5650          * @param {Roo.Element} el
5651          * @param {Number} rowIndex
5652          * @param {Number} columnIndex
5653          * @param {Roo.EventObject} e
5654          */
5655         "celldblclick" : true,
5656         /**
5657          * @event rowclick
5658          * Fires when a row is clicked
5659          * @param {Roo.bootstrap.Table} this
5660          * @param {Roo.Element} el
5661          * @param {Number} rowIndex
5662          * @param {Roo.EventObject} e
5663          */
5664         "rowclick" : true,
5665         /**
5666          * @event rowdblclick
5667          * Fires when a row is double clicked
5668          * @param {Roo.bootstrap.Table} this
5669          * @param {Roo.Element} el
5670          * @param {Number} rowIndex
5671          * @param {Roo.EventObject} e
5672          */
5673         "rowdblclick" : true,
5674         /**
5675          * @event mouseover
5676          * Fires when a mouseover occur
5677          * @param {Roo.bootstrap.Table} this
5678          * @param {Roo.Element} el
5679          * @param {Number} rowIndex
5680          * @param {Number} columnIndex
5681          * @param {Roo.EventObject} e
5682          */
5683         "mouseover" : true,
5684         /**
5685          * @event mouseout
5686          * Fires when a mouseout occur
5687          * @param {Roo.bootstrap.Table} this
5688          * @param {Roo.Element} el
5689          * @param {Number} rowIndex
5690          * @param {Number} columnIndex
5691          * @param {Roo.EventObject} e
5692          */
5693         "mouseout" : true,
5694         /**
5695          * @event rowclass
5696          * Fires when a row is rendered, so you can change add a style to it.
5697          * @param {Roo.bootstrap.Table} this
5698          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5699          */
5700         'rowclass' : true,
5701           /**
5702          * @event rowsrendered
5703          * Fires when all the  rows have been rendered
5704          * @param {Roo.bootstrap.Table} this
5705          */
5706         'rowsrendered' : true
5707         
5708     });
5709 };
5710
5711 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5712     
5713     cls: false,
5714     align: false,
5715     bgcolor: false,
5716     border: false,
5717     cellpadding: false,
5718     cellspacing: false,
5719     frame: false,
5720     rules: false,
5721     sortable: false,
5722     summary: false,
5723     width: false,
5724     striped : false,
5725     bordered: false,
5726     hover:  false,
5727     condensed : false,
5728     responsive : false,
5729     sm : false,
5730     cm : false,
5731     store : false,
5732     loadMask : false,
5733     footerShow : true,
5734     headerShow : true,
5735   
5736     rowSelection : false,
5737     cellSelection : false,
5738     layout : false,
5739     
5740     // Roo.Element - the tbody
5741     mainBody: false, 
5742     
5743     getAutoCreate : function(){
5744         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5745         
5746         cfg = {
5747             tag: 'table',
5748             cls : 'table',
5749             cn : []
5750         };
5751             
5752         if (this.striped) {
5753             cfg.cls += ' table-striped';
5754         }
5755         
5756         if (this.hover) {
5757             cfg.cls += ' table-hover';
5758         }
5759         if (this.bordered) {
5760             cfg.cls += ' table-bordered';
5761         }
5762         if (this.condensed) {
5763             cfg.cls += ' table-condensed';
5764         }
5765         if (this.responsive) {
5766             cfg.cls += ' table-responsive';
5767         }
5768         
5769         if (this.cls) {
5770             cfg.cls+=  ' ' +this.cls;
5771         }
5772         
5773         // this lot should be simplifed...
5774         
5775         if (this.align) {
5776             cfg.align=this.align;
5777         }
5778         if (this.bgcolor) {
5779             cfg.bgcolor=this.bgcolor;
5780         }
5781         if (this.border) {
5782             cfg.border=this.border;
5783         }
5784         if (this.cellpadding) {
5785             cfg.cellpadding=this.cellpadding;
5786         }
5787         if (this.cellspacing) {
5788             cfg.cellspacing=this.cellspacing;
5789         }
5790         if (this.frame) {
5791             cfg.frame=this.frame;
5792         }
5793         if (this.rules) {
5794             cfg.rules=this.rules;
5795         }
5796         if (this.sortable) {
5797             cfg.sortable=this.sortable;
5798         }
5799         if (this.summary) {
5800             cfg.summary=this.summary;
5801         }
5802         if (this.width) {
5803             cfg.width=this.width;
5804         }
5805         if (this.layout) {
5806             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5807         }
5808         
5809         if(this.store || this.cm){
5810             if(this.headerShow){
5811                 cfg.cn.push(this.renderHeader());
5812             }
5813             
5814             cfg.cn.push(this.renderBody());
5815             
5816             if(this.footerShow){
5817                 cfg.cn.push(this.renderFooter());
5818             }
5819             
5820             cfg.cls+=  ' TableGrid';
5821         }
5822         
5823         return { cn : [ cfg ] };
5824     },
5825     
5826     initEvents : function()
5827     {   
5828         if(!this.store || !this.cm){
5829             return;
5830         }
5831         
5832         //Roo.log('initEvents with ds!!!!');
5833         
5834         this.mainBody = this.el.select('tbody', true).first();
5835         
5836         
5837         var _this = this;
5838         
5839         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5840             e.on('click', _this.sort, _this);
5841         });
5842         
5843         this.el.on("click", this.onClick, this);
5844         this.el.on("dblclick", this.onDblClick, this);
5845         
5846         // why is this done????? = it breaks dialogs??
5847         //this.parent().el.setStyle('position', 'relative');
5848         
5849         
5850         if (this.footer) {
5851             this.footer.parentId = this.id;
5852             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5853         }
5854         
5855         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5856         
5857         this.store.on('load', this.onLoad, this);
5858         this.store.on('beforeload', this.onBeforeLoad, this);
5859         this.store.on('update', this.onUpdate, this);
5860         this.store.on('add', this.onAdd, this);
5861         
5862     },
5863     
5864     onMouseover : function(e, el)
5865     {
5866         var cell = Roo.get(el);
5867         
5868         if(!cell){
5869             return;
5870         }
5871         
5872         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5873             cell = cell.findParent('td', false, true);
5874         }
5875         
5876         var row = cell.findParent('tr', false, true);
5877         var cellIndex = cell.dom.cellIndex;
5878         var rowIndex = row.dom.rowIndex - 1; // start from 0
5879         
5880         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5881         
5882     },
5883     
5884     onMouseout : function(e, el)
5885     {
5886         var cell = Roo.get(el);
5887         
5888         if(!cell){
5889             return;
5890         }
5891         
5892         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5893             cell = cell.findParent('td', false, true);
5894         }
5895         
5896         var row = cell.findParent('tr', false, true);
5897         var cellIndex = cell.dom.cellIndex;
5898         var rowIndex = row.dom.rowIndex - 1; // start from 0
5899         
5900         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5901         
5902     },
5903     
5904     onClick : function(e, el)
5905     {
5906         var cell = Roo.get(el);
5907         
5908         if(!cell || (!this.cellSelection && !this.rowSelection)){
5909             return;
5910         }
5911         
5912         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5913             cell = cell.findParent('td', false, true);
5914         }
5915         
5916         if(!cell || typeof(cell) == 'undefined'){
5917             return;
5918         }
5919         
5920         var row = cell.findParent('tr', false, true);
5921         
5922         if(!row || typeof(row) == 'undefined'){
5923             return;
5924         }
5925         
5926         var cellIndex = cell.dom.cellIndex;
5927         var rowIndex = this.getRowIndex(row);
5928         
5929         // why??? - should these not be based on SelectionModel?
5930         if(this.cellSelection){
5931             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5932         }
5933         
5934         if(this.rowSelection){
5935             this.fireEvent('rowclick', this, row, rowIndex, e);
5936         }
5937         
5938         
5939     },
5940     
5941     onDblClick : function(e,el)
5942     {
5943         var cell = Roo.get(el);
5944         
5945         if(!cell || (!this.CellSelection && !this.RowSelection)){
5946             return;
5947         }
5948         
5949         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5950             cell = cell.findParent('td', false, true);
5951         }
5952         
5953         if(!cell || typeof(cell) == 'undefined'){
5954             return;
5955         }
5956         
5957         var row = cell.findParent('tr', false, true);
5958         
5959         if(!row || typeof(row) == 'undefined'){
5960             return;
5961         }
5962         
5963         var cellIndex = cell.dom.cellIndex;
5964         var rowIndex = this.getRowIndex(row);
5965         
5966         if(this.CellSelection){
5967             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5968         }
5969         
5970         if(this.RowSelection){
5971             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5972         }
5973     },
5974     
5975     sort : function(e,el)
5976     {
5977         var col = Roo.get(el);
5978         
5979         if(!col.hasClass('sortable')){
5980             return;
5981         }
5982         
5983         var sort = col.attr('sort');
5984         var dir = 'ASC';
5985         
5986         if(col.hasClass('glyphicon-arrow-up')){
5987             dir = 'DESC';
5988         }
5989         
5990         this.store.sortInfo = {field : sort, direction : dir};
5991         
5992         if (this.footer) {
5993             Roo.log("calling footer first");
5994             this.footer.onClick('first');
5995         } else {
5996         
5997             this.store.load({ params : { start : 0 } });
5998         }
5999     },
6000     
6001     renderHeader : function()
6002     {
6003         var header = {
6004             tag: 'thead',
6005             cn : []
6006         };
6007         
6008         var cm = this.cm;
6009         
6010         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6011             
6012             var config = cm.config[i];
6013             
6014             var c = {
6015                 tag: 'th',
6016                 style : '',
6017                 html: cm.getColumnHeader(i)
6018             };
6019             
6020             var hh = '';
6021             
6022             if(typeof(config.lgHeader) != 'undefined'){
6023                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6024             }
6025             
6026             if(typeof(config.mdHeader) != 'undefined'){
6027                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6028             }
6029             
6030             if(typeof(config.smHeader) != 'undefined'){
6031                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6032             }
6033             
6034             if(typeof(config.xsHeader) != 'undefined'){
6035                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6036             }
6037             
6038             if(hh.length){
6039                 c.html = hh;
6040             }
6041             
6042             if(typeof(config.tooltip) != 'undefined'){
6043                 c.tooltip = config.tooltip;
6044             }
6045             
6046             if(typeof(config.colspan) != 'undefined'){
6047                 c.colspan = config.colspan;
6048             }
6049             
6050             if(typeof(config.hidden) != 'undefined' && config.hidden){
6051                 c.style += ' display:none;';
6052             }
6053             
6054             if(typeof(config.dataIndex) != 'undefined'){
6055                 c.sort = config.dataIndex;
6056             }
6057             
6058             if(typeof(config.sortable) != 'undefined' && config.sortable){
6059                 c.cls = 'sortable';
6060             }
6061             
6062             if(typeof(config.align) != 'undefined' && config.align.length){
6063                 c.style += ' text-align:' + config.align + ';';
6064             }
6065             
6066             if(typeof(config.width) != 'undefined'){
6067                 c.style += ' width:' + config.width + 'px;';
6068             }
6069             
6070             if(typeof(config.cls) != 'undefined'){
6071                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6072             }
6073             
6074             ['xs','sm','md','lg'].map(function(size){
6075                 
6076                 if(typeof(config[size]) == 'undefined'){
6077                     return;
6078                 }
6079                 
6080                 if (!config[size]) { // 0 = hidden
6081                     c.cls += ' hidden-' + size;
6082                     return;
6083                 }
6084                 
6085                 c.cls += ' col-' + size + '-' + config[size];
6086
6087             });
6088             
6089             header.cn.push(c)
6090         }
6091         
6092         return header;
6093     },
6094     
6095     renderBody : function()
6096     {
6097         var body = {
6098             tag: 'tbody',
6099             cn : [
6100                 {
6101                     tag: 'tr',
6102                     cn : [
6103                         {
6104                             tag : 'td',
6105                             colspan :  this.cm.getColumnCount()
6106                         }
6107                     ]
6108                 }
6109             ]
6110         };
6111         
6112         return body;
6113     },
6114     
6115     renderFooter : function()
6116     {
6117         var footer = {
6118             tag: 'tfoot',
6119             cn : [
6120                 {
6121                     tag: 'tr',
6122                     cn : [
6123                         {
6124                             tag : 'td',
6125                             colspan :  this.cm.getColumnCount()
6126                         }
6127                     ]
6128                 }
6129             ]
6130         };
6131         
6132         return footer;
6133     },
6134     
6135     
6136     
6137     onLoad : function()
6138     {
6139 //        Roo.log('ds onload');
6140         this.clear();
6141         
6142         var _this = this;
6143         var cm = this.cm;
6144         var ds = this.store;
6145         
6146         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6147             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6148             
6149             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6150                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6151             }
6152             
6153             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6154                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6155             }
6156         });
6157         
6158         var tbody =  this.mainBody;
6159               
6160         if(ds.getCount() > 0){
6161             ds.data.each(function(d,rowIndex){
6162                 var row =  this.renderRow(cm, ds, rowIndex);
6163                 
6164                 tbody.createChild(row);
6165                 
6166                 var _this = this;
6167                 
6168                 if(row.cellObjects.length){
6169                     Roo.each(row.cellObjects, function(r){
6170                         _this.renderCellObject(r);
6171                     })
6172                 }
6173                 
6174             }, this);
6175         }
6176         
6177         Roo.each(this.el.select('tbody td', true).elements, function(e){
6178             e.on('mouseover', _this.onMouseover, _this);
6179         });
6180         
6181         Roo.each(this.el.select('tbody td', true).elements, function(e){
6182             e.on('mouseout', _this.onMouseout, _this);
6183         });
6184         this.fireEvent('rowsrendered', this);
6185         //if(this.loadMask){
6186         //    this.maskEl.hide();
6187         //}
6188     },
6189     
6190     
6191     onUpdate : function(ds,record)
6192     {
6193         this.refreshRow(record);
6194     },
6195     
6196     onRemove : function(ds, record, index, isUpdate){
6197         if(isUpdate !== true){
6198             this.fireEvent("beforerowremoved", this, index, record);
6199         }
6200         var bt = this.mainBody.dom;
6201         
6202         var rows = this.el.select('tbody > tr', true).elements;
6203         
6204         if(typeof(rows[index]) != 'undefined'){
6205             bt.removeChild(rows[index].dom);
6206         }
6207         
6208 //        if(bt.rows[index]){
6209 //            bt.removeChild(bt.rows[index]);
6210 //        }
6211         
6212         if(isUpdate !== true){
6213             //this.stripeRows(index);
6214             //this.syncRowHeights(index, index);
6215             //this.layout();
6216             this.fireEvent("rowremoved", this, index, record);
6217         }
6218     },
6219     
6220     onAdd : function(ds, records, rowIndex)
6221     {
6222         //Roo.log('on Add called');
6223         // - note this does not handle multiple adding very well..
6224         var bt = this.mainBody.dom;
6225         for (var i =0 ; i < records.length;i++) {
6226             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6227             //Roo.log(records[i]);
6228             //Roo.log(this.store.getAt(rowIndex+i));
6229             this.insertRow(this.store, rowIndex + i, false);
6230             return;
6231         }
6232         
6233     },
6234     
6235     
6236     refreshRow : function(record){
6237         var ds = this.store, index;
6238         if(typeof record == 'number'){
6239             index = record;
6240             record = ds.getAt(index);
6241         }else{
6242             index = ds.indexOf(record);
6243         }
6244         this.insertRow(ds, index, true);
6245         this.onRemove(ds, record, index+1, true);
6246         //this.syncRowHeights(index, index);
6247         //this.layout();
6248         this.fireEvent("rowupdated", this, index, record);
6249     },
6250     
6251     insertRow : function(dm, rowIndex, isUpdate){
6252         
6253         if(!isUpdate){
6254             this.fireEvent("beforerowsinserted", this, rowIndex);
6255         }
6256             //var s = this.getScrollState();
6257         var row = this.renderRow(this.cm, this.store, rowIndex);
6258         // insert before rowIndex..
6259         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6260         
6261         var _this = this;
6262                 
6263         if(row.cellObjects.length){
6264             Roo.each(row.cellObjects, function(r){
6265                 _this.renderCellObject(r);
6266             })
6267         }
6268             
6269         if(!isUpdate){
6270             this.fireEvent("rowsinserted", this, rowIndex);
6271             //this.syncRowHeights(firstRow, lastRow);
6272             //this.stripeRows(firstRow);
6273             //this.layout();
6274         }
6275         
6276     },
6277     
6278     
6279     getRowDom : function(rowIndex)
6280     {
6281         var rows = this.el.select('tbody > tr', true).elements;
6282         
6283         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6284         
6285     },
6286     // returns the object tree for a tr..
6287   
6288     
6289     renderRow : function(cm, ds, rowIndex) 
6290     {
6291         
6292         var d = ds.getAt(rowIndex);
6293         
6294         var row = {
6295             tag : 'tr',
6296             cn : []
6297         };
6298             
6299         var cellObjects = [];
6300         
6301         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6302             var config = cm.config[i];
6303             
6304             var renderer = cm.getRenderer(i);
6305             var value = '';
6306             var id = false;
6307             
6308             if(typeof(renderer) !== 'undefined'){
6309                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6310             }
6311             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6312             // and are rendered into the cells after the row is rendered - using the id for the element.
6313             
6314             if(typeof(value) === 'object'){
6315                 id = Roo.id();
6316                 cellObjects.push({
6317                     container : id,
6318                     cfg : value 
6319                 })
6320             }
6321             
6322             var rowcfg = {
6323                 record: d,
6324                 rowIndex : rowIndex,
6325                 colIndex : i,
6326                 rowClass : ''
6327             };
6328
6329             this.fireEvent('rowclass', this, rowcfg);
6330             
6331             var td = {
6332                 tag: 'td',
6333                 cls : rowcfg.rowClass,
6334                 style: '',
6335                 html: (typeof(value) === 'object') ? '' : value
6336             };
6337             
6338             if (id) {
6339                 td.id = id;
6340             }
6341             
6342             if(typeof(config.colspan) != 'undefined'){
6343                 td.colspan = config.colspan;
6344             }
6345             
6346             if(typeof(config.hidden) != 'undefined' && config.hidden){
6347                 td.style += ' display:none;';
6348             }
6349             
6350             if(typeof(config.align) != 'undefined' && config.align.length){
6351                 td.style += ' text-align:' + config.align + ';';
6352             }
6353             
6354             if(typeof(config.width) != 'undefined'){
6355                 td.style += ' width:' +  config.width + 'px;';
6356             }
6357             
6358             if(typeof(config.cursor) != 'undefined'){
6359                 td.style += ' cursor:' +  config.cursor + ';';
6360             }
6361             
6362             if(typeof(config.cls) != 'undefined'){
6363                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6364             }
6365             
6366             ['xs','sm','md','lg'].map(function(size){
6367                 
6368                 if(typeof(config[size]) == 'undefined'){
6369                     return;
6370                 }
6371                 
6372                 if (!config[size]) { // 0 = hidden
6373                     td.cls += ' hidden-' + size;
6374                     return;
6375                 }
6376                 
6377                 td.cls += ' col-' + size + '-' + config[size];
6378
6379             });
6380              
6381             row.cn.push(td);
6382            
6383         }
6384         
6385         row.cellObjects = cellObjects;
6386         
6387         return row;
6388           
6389     },
6390     
6391     
6392     
6393     onBeforeLoad : function()
6394     {
6395         //Roo.log('ds onBeforeLoad');
6396         
6397         //this.clear();
6398         
6399         //if(this.loadMask){
6400         //    this.maskEl.show();
6401         //}
6402     },
6403      /**
6404      * Remove all rows
6405      */
6406     clear : function()
6407     {
6408         this.el.select('tbody', true).first().dom.innerHTML = '';
6409     },
6410     /**
6411      * Show or hide a row.
6412      * @param {Number} rowIndex to show or hide
6413      * @param {Boolean} state hide
6414      */
6415     setRowVisibility : function(rowIndex, state)
6416     {
6417         var bt = this.mainBody.dom;
6418         
6419         var rows = this.el.select('tbody > tr', true).elements;
6420         
6421         if(typeof(rows[rowIndex]) == 'undefined'){
6422             return;
6423         }
6424         rows[rowIndex].dom.style.display = state ? '' : 'none';
6425     },
6426     
6427     
6428     getSelectionModel : function(){
6429         if(!this.selModel){
6430             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6431         }
6432         return this.selModel;
6433     },
6434     /*
6435      * Render the Roo.bootstrap object from renderder
6436      */
6437     renderCellObject : function(r)
6438     {
6439         var _this = this;
6440         
6441         var t = r.cfg.render(r.container);
6442         
6443         if(r.cfg.cn){
6444             Roo.each(r.cfg.cn, function(c){
6445                 var child = {
6446                     container: t.getChildContainer(),
6447                     cfg: c
6448                 };
6449                 _this.renderCellObject(child);
6450             })
6451         }
6452     },
6453     
6454     getRowIndex : function(row)
6455     {
6456         var rowIndex = -1;
6457         
6458         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6459             if(el != row){
6460                 return;
6461             }
6462             
6463             rowIndex = index;
6464         });
6465         
6466         return rowIndex;
6467     }
6468    
6469 });
6470
6471  
6472
6473  /*
6474  * - LGPL
6475  *
6476  * table cell
6477  * 
6478  */
6479
6480 /**
6481  * @class Roo.bootstrap.TableCell
6482  * @extends Roo.bootstrap.Component
6483  * Bootstrap TableCell class
6484  * @cfg {String} html cell contain text
6485  * @cfg {String} cls cell class
6486  * @cfg {String} tag cell tag (td|th) default td
6487  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6488  * @cfg {String} align Aligns the content in a cell
6489  * @cfg {String} axis Categorizes cells
6490  * @cfg {String} bgcolor Specifies the background color of a cell
6491  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6492  * @cfg {Number} colspan Specifies the number of columns a cell should span
6493  * @cfg {String} headers Specifies one or more header cells a cell is related to
6494  * @cfg {Number} height Sets the height of a cell
6495  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6496  * @cfg {Number} rowspan Sets the number of rows a cell should span
6497  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6498  * @cfg {String} valign Vertical aligns the content in a cell
6499  * @cfg {Number} width Specifies the width of a cell
6500  * 
6501  * @constructor
6502  * Create a new TableCell
6503  * @param {Object} config The config object
6504  */
6505
6506 Roo.bootstrap.TableCell = function(config){
6507     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6508 };
6509
6510 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6511     
6512     html: false,
6513     cls: false,
6514     tag: false,
6515     abbr: false,
6516     align: false,
6517     axis: false,
6518     bgcolor: false,
6519     charoff: false,
6520     colspan: false,
6521     headers: false,
6522     height: false,
6523     nowrap: false,
6524     rowspan: false,
6525     scope: false,
6526     valign: false,
6527     width: false,
6528     
6529     
6530     getAutoCreate : function(){
6531         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6532         
6533         cfg = {
6534             tag: 'td'
6535         };
6536         
6537         if(this.tag){
6538             cfg.tag = this.tag;
6539         }
6540         
6541         if (this.html) {
6542             cfg.html=this.html
6543         }
6544         if (this.cls) {
6545             cfg.cls=this.cls
6546         }
6547         if (this.abbr) {
6548             cfg.abbr=this.abbr
6549         }
6550         if (this.align) {
6551             cfg.align=this.align
6552         }
6553         if (this.axis) {
6554             cfg.axis=this.axis
6555         }
6556         if (this.bgcolor) {
6557             cfg.bgcolor=this.bgcolor
6558         }
6559         if (this.charoff) {
6560             cfg.charoff=this.charoff
6561         }
6562         if (this.colspan) {
6563             cfg.colspan=this.colspan
6564         }
6565         if (this.headers) {
6566             cfg.headers=this.headers
6567         }
6568         if (this.height) {
6569             cfg.height=this.height
6570         }
6571         if (this.nowrap) {
6572             cfg.nowrap=this.nowrap
6573         }
6574         if (this.rowspan) {
6575             cfg.rowspan=this.rowspan
6576         }
6577         if (this.scope) {
6578             cfg.scope=this.scope
6579         }
6580         if (this.valign) {
6581             cfg.valign=this.valign
6582         }
6583         if (this.width) {
6584             cfg.width=this.width
6585         }
6586         
6587         
6588         return cfg;
6589     }
6590    
6591 });
6592
6593  
6594
6595  /*
6596  * - LGPL
6597  *
6598  * table row
6599  * 
6600  */
6601
6602 /**
6603  * @class Roo.bootstrap.TableRow
6604  * @extends Roo.bootstrap.Component
6605  * Bootstrap TableRow class
6606  * @cfg {String} cls row class
6607  * @cfg {String} align Aligns the content in a table row
6608  * @cfg {String} bgcolor Specifies a background color for a table row
6609  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6610  * @cfg {String} valign Vertical aligns the content in a table row
6611  * 
6612  * @constructor
6613  * Create a new TableRow
6614  * @param {Object} config The config object
6615  */
6616
6617 Roo.bootstrap.TableRow = function(config){
6618     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6619 };
6620
6621 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6622     
6623     cls: false,
6624     align: false,
6625     bgcolor: false,
6626     charoff: false,
6627     valign: false,
6628     
6629     getAutoCreate : function(){
6630         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6631         
6632         cfg = {
6633             tag: 'tr'
6634         };
6635             
6636         if(this.cls){
6637             cfg.cls = this.cls;
6638         }
6639         if(this.align){
6640             cfg.align = this.align;
6641         }
6642         if(this.bgcolor){
6643             cfg.bgcolor = this.bgcolor;
6644         }
6645         if(this.charoff){
6646             cfg.charoff = this.charoff;
6647         }
6648         if(this.valign){
6649             cfg.valign = this.valign;
6650         }
6651         
6652         return cfg;
6653     }
6654    
6655 });
6656
6657  
6658
6659  /*
6660  * - LGPL
6661  *
6662  * table body
6663  * 
6664  */
6665
6666 /**
6667  * @class Roo.bootstrap.TableBody
6668  * @extends Roo.bootstrap.Component
6669  * Bootstrap TableBody class
6670  * @cfg {String} cls element class
6671  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6672  * @cfg {String} align Aligns the content inside the element
6673  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6674  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6675  * 
6676  * @constructor
6677  * Create a new TableBody
6678  * @param {Object} config The config object
6679  */
6680
6681 Roo.bootstrap.TableBody = function(config){
6682     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6683 };
6684
6685 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6686     
6687     cls: false,
6688     tag: false,
6689     align: false,
6690     charoff: false,
6691     valign: false,
6692     
6693     getAutoCreate : function(){
6694         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6695         
6696         cfg = {
6697             tag: 'tbody'
6698         };
6699             
6700         if (this.cls) {
6701             cfg.cls=this.cls
6702         }
6703         if(this.tag){
6704             cfg.tag = this.tag;
6705         }
6706         
6707         if(this.align){
6708             cfg.align = this.align;
6709         }
6710         if(this.charoff){
6711             cfg.charoff = this.charoff;
6712         }
6713         if(this.valign){
6714             cfg.valign = this.valign;
6715         }
6716         
6717         return cfg;
6718     }
6719     
6720     
6721 //    initEvents : function()
6722 //    {
6723 //        
6724 //        if(!this.store){
6725 //            return;
6726 //        }
6727 //        
6728 //        this.store = Roo.factory(this.store, Roo.data);
6729 //        this.store.on('load', this.onLoad, this);
6730 //        
6731 //        this.store.load();
6732 //        
6733 //    },
6734 //    
6735 //    onLoad: function () 
6736 //    {   
6737 //        this.fireEvent('load', this);
6738 //    }
6739 //    
6740 //   
6741 });
6742
6743  
6744
6745  /*
6746  * Based on:
6747  * Ext JS Library 1.1.1
6748  * Copyright(c) 2006-2007, Ext JS, LLC.
6749  *
6750  * Originally Released Under LGPL - original licence link has changed is not relivant.
6751  *
6752  * Fork - LGPL
6753  * <script type="text/javascript">
6754  */
6755
6756 // as we use this in bootstrap.
6757 Roo.namespace('Roo.form');
6758  /**
6759  * @class Roo.form.Action
6760  * Internal Class used to handle form actions
6761  * @constructor
6762  * @param {Roo.form.BasicForm} el The form element or its id
6763  * @param {Object} config Configuration options
6764  */
6765
6766  
6767  
6768 // define the action interface
6769 Roo.form.Action = function(form, options){
6770     this.form = form;
6771     this.options = options || {};
6772 };
6773 /**
6774  * Client Validation Failed
6775  * @const 
6776  */
6777 Roo.form.Action.CLIENT_INVALID = 'client';
6778 /**
6779  * Server Validation Failed
6780  * @const 
6781  */
6782 Roo.form.Action.SERVER_INVALID = 'server';
6783  /**
6784  * Connect to Server Failed
6785  * @const 
6786  */
6787 Roo.form.Action.CONNECT_FAILURE = 'connect';
6788 /**
6789  * Reading Data from Server Failed
6790  * @const 
6791  */
6792 Roo.form.Action.LOAD_FAILURE = 'load';
6793
6794 Roo.form.Action.prototype = {
6795     type : 'default',
6796     failureType : undefined,
6797     response : undefined,
6798     result : undefined,
6799
6800     // interface method
6801     run : function(options){
6802
6803     },
6804
6805     // interface method
6806     success : function(response){
6807
6808     },
6809
6810     // interface method
6811     handleResponse : function(response){
6812
6813     },
6814
6815     // default connection failure
6816     failure : function(response){
6817         
6818         this.response = response;
6819         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6820         this.form.afterAction(this, false);
6821     },
6822
6823     processResponse : function(response){
6824         this.response = response;
6825         if(!response.responseText){
6826             return true;
6827         }
6828         this.result = this.handleResponse(response);
6829         return this.result;
6830     },
6831
6832     // utility functions used internally
6833     getUrl : function(appendParams){
6834         var url = this.options.url || this.form.url || this.form.el.dom.action;
6835         if(appendParams){
6836             var p = this.getParams();
6837             if(p){
6838                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6839             }
6840         }
6841         return url;
6842     },
6843
6844     getMethod : function(){
6845         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6846     },
6847
6848     getParams : function(){
6849         var bp = this.form.baseParams;
6850         var p = this.options.params;
6851         if(p){
6852             if(typeof p == "object"){
6853                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6854             }else if(typeof p == 'string' && bp){
6855                 p += '&' + Roo.urlEncode(bp);
6856             }
6857         }else if(bp){
6858             p = Roo.urlEncode(bp);
6859         }
6860         return p;
6861     },
6862
6863     createCallback : function(){
6864         return {
6865             success: this.success,
6866             failure: this.failure,
6867             scope: this,
6868             timeout: (this.form.timeout*1000),
6869             upload: this.form.fileUpload ? this.success : undefined
6870         };
6871     }
6872 };
6873
6874 Roo.form.Action.Submit = function(form, options){
6875     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6876 };
6877
6878 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6879     type : 'submit',
6880
6881     haveProgress : false,
6882     uploadComplete : false,
6883     
6884     // uploadProgress indicator.
6885     uploadProgress : function()
6886     {
6887         if (!this.form.progressUrl) {
6888             return;
6889         }
6890         
6891         if (!this.haveProgress) {
6892             Roo.MessageBox.progress("Uploading", "Uploading");
6893         }
6894         if (this.uploadComplete) {
6895            Roo.MessageBox.hide();
6896            return;
6897         }
6898         
6899         this.haveProgress = true;
6900    
6901         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6902         
6903         var c = new Roo.data.Connection();
6904         c.request({
6905             url : this.form.progressUrl,
6906             params: {
6907                 id : uid
6908             },
6909             method: 'GET',
6910             success : function(req){
6911                //console.log(data);
6912                 var rdata = false;
6913                 var edata;
6914                 try  {
6915                    rdata = Roo.decode(req.responseText)
6916                 } catch (e) {
6917                     Roo.log("Invalid data from server..");
6918                     Roo.log(edata);
6919                     return;
6920                 }
6921                 if (!rdata || !rdata.success) {
6922                     Roo.log(rdata);
6923                     Roo.MessageBox.alert(Roo.encode(rdata));
6924                     return;
6925                 }
6926                 var data = rdata.data;
6927                 
6928                 if (this.uploadComplete) {
6929                    Roo.MessageBox.hide();
6930                    return;
6931                 }
6932                    
6933                 if (data){
6934                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6935                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6936                     );
6937                 }
6938                 this.uploadProgress.defer(2000,this);
6939             },
6940        
6941             failure: function(data) {
6942                 Roo.log('progress url failed ');
6943                 Roo.log(data);
6944             },
6945             scope : this
6946         });
6947            
6948     },
6949     
6950     
6951     run : function()
6952     {
6953         // run get Values on the form, so it syncs any secondary forms.
6954         this.form.getValues();
6955         
6956         var o = this.options;
6957         var method = this.getMethod();
6958         var isPost = method == 'POST';
6959         if(o.clientValidation === false || this.form.isValid()){
6960             
6961             if (this.form.progressUrl) {
6962                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6963                     (new Date() * 1) + '' + Math.random());
6964                     
6965             } 
6966             
6967             
6968             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6969                 form:this.form.el.dom,
6970                 url:this.getUrl(!isPost),
6971                 method: method,
6972                 params:isPost ? this.getParams() : null,
6973                 isUpload: this.form.fileUpload
6974             }));
6975             
6976             this.uploadProgress();
6977
6978         }else if (o.clientValidation !== false){ // client validation failed
6979             this.failureType = Roo.form.Action.CLIENT_INVALID;
6980             this.form.afterAction(this, false);
6981         }
6982     },
6983
6984     success : function(response)
6985     {
6986         this.uploadComplete= true;
6987         if (this.haveProgress) {
6988             Roo.MessageBox.hide();
6989         }
6990         
6991         
6992         var result = this.processResponse(response);
6993         if(result === true || result.success){
6994             this.form.afterAction(this, true);
6995             return;
6996         }
6997         if(result.errors){
6998             this.form.markInvalid(result.errors);
6999             this.failureType = Roo.form.Action.SERVER_INVALID;
7000         }
7001         this.form.afterAction(this, false);
7002     },
7003     failure : function(response)
7004     {
7005         this.uploadComplete= true;
7006         if (this.haveProgress) {
7007             Roo.MessageBox.hide();
7008         }
7009         
7010         this.response = response;
7011         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7012         this.form.afterAction(this, false);
7013     },
7014     
7015     handleResponse : function(response){
7016         if(this.form.errorReader){
7017             var rs = this.form.errorReader.read(response);
7018             var errors = [];
7019             if(rs.records){
7020                 for(var i = 0, len = rs.records.length; i < len; i++) {
7021                     var r = rs.records[i];
7022                     errors[i] = r.data;
7023                 }
7024             }
7025             if(errors.length < 1){
7026                 errors = null;
7027             }
7028             return {
7029                 success : rs.success,
7030                 errors : errors
7031             };
7032         }
7033         var ret = false;
7034         try {
7035             ret = Roo.decode(response.responseText);
7036         } catch (e) {
7037             ret = {
7038                 success: false,
7039                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7040                 errors : []
7041             };
7042         }
7043         return ret;
7044         
7045     }
7046 });
7047
7048
7049 Roo.form.Action.Load = function(form, options){
7050     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7051     this.reader = this.form.reader;
7052 };
7053
7054 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7055     type : 'load',
7056
7057     run : function(){
7058         
7059         Roo.Ajax.request(Roo.apply(
7060                 this.createCallback(), {
7061                     method:this.getMethod(),
7062                     url:this.getUrl(false),
7063                     params:this.getParams()
7064         }));
7065     },
7066
7067     success : function(response){
7068         
7069         var result = this.processResponse(response);
7070         if(result === true || !result.success || !result.data){
7071             this.failureType = Roo.form.Action.LOAD_FAILURE;
7072             this.form.afterAction(this, false);
7073             return;
7074         }
7075         this.form.clearInvalid();
7076         this.form.setValues(result.data);
7077         this.form.afterAction(this, true);
7078     },
7079
7080     handleResponse : function(response){
7081         if(this.form.reader){
7082             var rs = this.form.reader.read(response);
7083             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7084             return {
7085                 success : rs.success,
7086                 data : data
7087             };
7088         }
7089         return Roo.decode(response.responseText);
7090     }
7091 });
7092
7093 Roo.form.Action.ACTION_TYPES = {
7094     'load' : Roo.form.Action.Load,
7095     'submit' : Roo.form.Action.Submit
7096 };/*
7097  * - LGPL
7098  *
7099  * form
7100  * 
7101  */
7102
7103 /**
7104  * @class Roo.bootstrap.Form
7105  * @extends Roo.bootstrap.Component
7106  * Bootstrap Form class
7107  * @cfg {String} method  GET | POST (default POST)
7108  * @cfg {String} labelAlign top | left (default top)
7109  * @cfg {String} align left  | right - for navbars
7110  * @cfg {Boolean} loadMask load mask when submit (default true)
7111
7112  * 
7113  * @constructor
7114  * Create a new Form
7115  * @param {Object} config The config object
7116  */
7117
7118
7119 Roo.bootstrap.Form = function(config){
7120     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7121     this.addEvents({
7122         /**
7123          * @event clientvalidation
7124          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7125          * @param {Form} this
7126          * @param {Boolean} valid true if the form has passed client-side validation
7127          */
7128         clientvalidation: true,
7129         /**
7130          * @event beforeaction
7131          * Fires before any action is performed. Return false to cancel the action.
7132          * @param {Form} this
7133          * @param {Action} action The action to be performed
7134          */
7135         beforeaction: true,
7136         /**
7137          * @event actionfailed
7138          * Fires when an action fails.
7139          * @param {Form} this
7140          * @param {Action} action The action that failed
7141          */
7142         actionfailed : true,
7143         /**
7144          * @event actioncomplete
7145          * Fires when an action is completed.
7146          * @param {Form} this
7147          * @param {Action} action The action that completed
7148          */
7149         actioncomplete : true
7150     });
7151     
7152 };
7153
7154 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7155       
7156      /**
7157      * @cfg {String} method
7158      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7159      */
7160     method : 'POST',
7161     /**
7162      * @cfg {String} url
7163      * The URL to use for form actions if one isn't supplied in the action options.
7164      */
7165     /**
7166      * @cfg {Boolean} fileUpload
7167      * Set to true if this form is a file upload.
7168      */
7169      
7170     /**
7171      * @cfg {Object} baseParams
7172      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7173      */
7174       
7175     /**
7176      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7177      */
7178     timeout: 30,
7179     /**
7180      * @cfg {Sting} align (left|right) for navbar forms
7181      */
7182     align : 'left',
7183
7184     // private
7185     activeAction : null,
7186  
7187     /**
7188      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7189      * element by passing it or its id or mask the form itself by passing in true.
7190      * @type Mixed
7191      */
7192     waitMsgTarget : false,
7193     
7194     loadMask : true,
7195     
7196     getAutoCreate : function(){
7197         
7198         var cfg = {
7199             tag: 'form',
7200             method : this.method || 'POST',
7201             id : this.id || Roo.id(),
7202             cls : ''
7203         };
7204         if (this.parent().xtype.match(/^Nav/)) {
7205             cfg.cls = 'navbar-form navbar-' + this.align;
7206             
7207         }
7208         
7209         if (this.labelAlign == 'left' ) {
7210             cfg.cls += ' form-horizontal';
7211         }
7212         
7213         
7214         return cfg;
7215     },
7216     initEvents : function()
7217     {
7218         this.el.on('submit', this.onSubmit, this);
7219         // this was added as random key presses on the form where triggering form submit.
7220         this.el.on('keypress', function(e) {
7221             if (e.getCharCode() != 13) {
7222                 return true;
7223             }
7224             // we might need to allow it for textareas.. and some other items.
7225             // check e.getTarget().
7226             
7227             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7228                 return true;
7229             }
7230         
7231             Roo.log("keypress blocked");
7232             
7233             e.preventDefault();
7234             return false;
7235         });
7236         
7237     },
7238     // private
7239     onSubmit : function(e){
7240         e.stopEvent();
7241     },
7242     
7243      /**
7244      * Returns true if client-side validation on the form is successful.
7245      * @return Boolean
7246      */
7247     isValid : function(){
7248         var items = this.getItems();
7249         var valid = true;
7250         items.each(function(f){
7251            if(!f.validate()){
7252                valid = false;
7253                
7254            }
7255         });
7256         return valid;
7257     },
7258     /**
7259      * Returns true if any fields in this form have changed since their original load.
7260      * @return Boolean
7261      */
7262     isDirty : function(){
7263         var dirty = false;
7264         var items = this.getItems();
7265         items.each(function(f){
7266            if(f.isDirty()){
7267                dirty = true;
7268                return false;
7269            }
7270            return true;
7271         });
7272         return dirty;
7273     },
7274      /**
7275      * Performs a predefined action (submit or load) or custom actions you define on this form.
7276      * @param {String} actionName The name of the action type
7277      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7278      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7279      * accept other config options):
7280      * <pre>
7281 Property          Type             Description
7282 ----------------  ---------------  ----------------------------------------------------------------------------------
7283 url               String           The url for the action (defaults to the form's url)
7284 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7285 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7286 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7287                                    validate the form on the client (defaults to false)
7288      * </pre>
7289      * @return {BasicForm} this
7290      */
7291     doAction : function(action, options){
7292         if(typeof action == 'string'){
7293             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7294         }
7295         if(this.fireEvent('beforeaction', this, action) !== false){
7296             this.beforeAction(action);
7297             action.run.defer(100, action);
7298         }
7299         return this;
7300     },
7301     
7302     // private
7303     beforeAction : function(action){
7304         var o = action.options;
7305         
7306         if(this.loadMask){
7307             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7308         }
7309         // not really supported yet.. ??
7310         
7311         //if(this.waitMsgTarget === true){
7312         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7313         //}else if(this.waitMsgTarget){
7314         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7315         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7316         //}else {
7317         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7318        // }
7319          
7320     },
7321
7322     // private
7323     afterAction : function(action, success){
7324         this.activeAction = null;
7325         var o = action.options;
7326         
7327         //if(this.waitMsgTarget === true){
7328             this.el.unmask();
7329         //}else if(this.waitMsgTarget){
7330         //    this.waitMsgTarget.unmask();
7331         //}else{
7332         //    Roo.MessageBox.updateProgress(1);
7333         //    Roo.MessageBox.hide();
7334        // }
7335         // 
7336         if(success){
7337             if(o.reset){
7338                 this.reset();
7339             }
7340             Roo.callback(o.success, o.scope, [this, action]);
7341             this.fireEvent('actioncomplete', this, action);
7342             
7343         }else{
7344             
7345             // failure condition..
7346             // we have a scenario where updates need confirming.
7347             // eg. if a locking scenario exists..
7348             // we look for { errors : { needs_confirm : true }} in the response.
7349             if (
7350                 (typeof(action.result) != 'undefined')  &&
7351                 (typeof(action.result.errors) != 'undefined')  &&
7352                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7353            ){
7354                 var _t = this;
7355                 Roo.log("not supported yet");
7356                  /*
7357                 
7358                 Roo.MessageBox.confirm(
7359                     "Change requires confirmation",
7360                     action.result.errorMsg,
7361                     function(r) {
7362                         if (r != 'yes') {
7363                             return;
7364                         }
7365                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7366                     }
7367                     
7368                 );
7369                 */
7370                 
7371                 
7372                 return;
7373             }
7374             
7375             Roo.callback(o.failure, o.scope, [this, action]);
7376             // show an error message if no failed handler is set..
7377             if (!this.hasListener('actionfailed')) {
7378                 Roo.log("need to add dialog support");
7379                 /*
7380                 Roo.MessageBox.alert("Error",
7381                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7382                         action.result.errorMsg :
7383                         "Saving Failed, please check your entries or try again"
7384                 );
7385                 */
7386             }
7387             
7388             this.fireEvent('actionfailed', this, action);
7389         }
7390         
7391     },
7392     /**
7393      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7394      * @param {String} id The value to search for
7395      * @return Field
7396      */
7397     findField : function(id){
7398         var items = this.getItems();
7399         var field = items.get(id);
7400         if(!field){
7401              items.each(function(f){
7402                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7403                     field = f;
7404                     return false;
7405                 }
7406                 return true;
7407             });
7408         }
7409         return field || null;
7410     },
7411      /**
7412      * Mark fields in this form invalid in bulk.
7413      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7414      * @return {BasicForm} this
7415      */
7416     markInvalid : function(errors){
7417         if(errors instanceof Array){
7418             for(var i = 0, len = errors.length; i < len; i++){
7419                 var fieldError = errors[i];
7420                 var f = this.findField(fieldError.id);
7421                 if(f){
7422                     f.markInvalid(fieldError.msg);
7423                 }
7424             }
7425         }else{
7426             var field, id;
7427             for(id in errors){
7428                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7429                     field.markInvalid(errors[id]);
7430                 }
7431             }
7432         }
7433         //Roo.each(this.childForms || [], function (f) {
7434         //    f.markInvalid(errors);
7435         //});
7436         
7437         return this;
7438     },
7439
7440     /**
7441      * Set values for fields in this form in bulk.
7442      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7443      * @return {BasicForm} this
7444      */
7445     setValues : function(values){
7446         if(values instanceof Array){ // array of objects
7447             for(var i = 0, len = values.length; i < len; i++){
7448                 var v = values[i];
7449                 var f = this.findField(v.id);
7450                 if(f){
7451                     f.setValue(v.value);
7452                     if(this.trackResetOnLoad){
7453                         f.originalValue = f.getValue();
7454                     }
7455                 }
7456             }
7457         }else{ // object hash
7458             var field, id;
7459             for(id in values){
7460                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7461                     
7462                     if (field.setFromData && 
7463                         field.valueField && 
7464                         field.displayField &&
7465                         // combos' with local stores can 
7466                         // be queried via setValue()
7467                         // to set their value..
7468                         (field.store && !field.store.isLocal)
7469                         ) {
7470                         // it's a combo
7471                         var sd = { };
7472                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7473                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7474                         field.setFromData(sd);
7475                         
7476                     } else {
7477                         field.setValue(values[id]);
7478                     }
7479                     
7480                     
7481                     if(this.trackResetOnLoad){
7482                         field.originalValue = field.getValue();
7483                     }
7484                 }
7485             }
7486         }
7487          
7488         //Roo.each(this.childForms || [], function (f) {
7489         //    f.setValues(values);
7490         //});
7491                 
7492         return this;
7493     },
7494
7495     /**
7496      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7497      * they are returned as an array.
7498      * @param {Boolean} asString
7499      * @return {Object}
7500      */
7501     getValues : function(asString){
7502         //if (this.childForms) {
7503             // copy values from the child forms
7504         //    Roo.each(this.childForms, function (f) {
7505         //        this.setValues(f.getValues());
7506         //    }, this);
7507         //}
7508         
7509         
7510         
7511         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7512         if(asString === true){
7513             return fs;
7514         }
7515         return Roo.urlDecode(fs);
7516     },
7517     
7518     /**
7519      * Returns the fields in this form as an object with key/value pairs. 
7520      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7521      * @return {Object}
7522      */
7523     getFieldValues : function(with_hidden)
7524     {
7525         var items = this.getItems();
7526         var ret = {};
7527         items.each(function(f){
7528             if (!f.getName()) {
7529                 return;
7530             }
7531             var v = f.getValue();
7532             if (f.inputType =='radio') {
7533                 if (typeof(ret[f.getName()]) == 'undefined') {
7534                     ret[f.getName()] = ''; // empty..
7535                 }
7536                 
7537                 if (!f.el.dom.checked) {
7538                     return;
7539                     
7540                 }
7541                 v = f.el.dom.value;
7542                 
7543             }
7544             
7545             // not sure if this supported any more..
7546             if ((typeof(v) == 'object') && f.getRawValue) {
7547                 v = f.getRawValue() ; // dates..
7548             }
7549             // combo boxes where name != hiddenName...
7550             if (f.name != f.getName()) {
7551                 ret[f.name] = f.getRawValue();
7552             }
7553             ret[f.getName()] = v;
7554         });
7555         
7556         return ret;
7557     },
7558
7559     /**
7560      * Clears all invalid messages in this form.
7561      * @return {BasicForm} this
7562      */
7563     clearInvalid : function(){
7564         var items = this.getItems();
7565         
7566         items.each(function(f){
7567            f.clearInvalid();
7568         });
7569         
7570         
7571         
7572         return this;
7573     },
7574
7575     /**
7576      * Resets this form.
7577      * @return {BasicForm} this
7578      */
7579     reset : function(){
7580         var items = this.getItems();
7581         items.each(function(f){
7582             f.reset();
7583         });
7584         
7585         Roo.each(this.childForms || [], function (f) {
7586             f.reset();
7587         });
7588        
7589         
7590         return this;
7591     },
7592     getItems : function()
7593     {
7594         var r=new Roo.util.MixedCollection(false, function(o){
7595             return o.id || (o.id = Roo.id());
7596         });
7597         var iter = function(el) {
7598             if (el.inputEl) {
7599                 r.add(el);
7600             }
7601             if (!el.items) {
7602                 return;
7603             }
7604             Roo.each(el.items,function(e) {
7605                 iter(e);
7606             });
7607             
7608             
7609         };
7610         
7611         iter(this);
7612         return r;
7613         
7614         
7615         
7616         
7617     }
7618     
7619 });
7620
7621  
7622 /*
7623  * Based on:
7624  * Ext JS Library 1.1.1
7625  * Copyright(c) 2006-2007, Ext JS, LLC.
7626  *
7627  * Originally Released Under LGPL - original licence link has changed is not relivant.
7628  *
7629  * Fork - LGPL
7630  * <script type="text/javascript">
7631  */
7632 /**
7633  * @class Roo.form.VTypes
7634  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7635  * @singleton
7636  */
7637 Roo.form.VTypes = function(){
7638     // closure these in so they are only created once.
7639     var alpha = /^[a-zA-Z_]+$/;
7640     var alphanum = /^[a-zA-Z0-9_]+$/;
7641     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7642     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7643
7644     // All these messages and functions are configurable
7645     return {
7646         /**
7647          * The function used to validate email addresses
7648          * @param {String} value The email address
7649          */
7650         'email' : function(v){
7651             return email.test(v);
7652         },
7653         /**
7654          * The error text to display when the email validation function returns false
7655          * @type String
7656          */
7657         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7658         /**
7659          * The keystroke filter mask to be applied on email input
7660          * @type RegExp
7661          */
7662         'emailMask' : /[a-z0-9_\.\-@]/i,
7663
7664         /**
7665          * The function used to validate URLs
7666          * @param {String} value The URL
7667          */
7668         'url' : function(v){
7669             return url.test(v);
7670         },
7671         /**
7672          * The error text to display when the url validation function returns false
7673          * @type String
7674          */
7675         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7676         
7677         /**
7678          * The function used to validate alpha values
7679          * @param {String} value The value
7680          */
7681         'alpha' : function(v){
7682             return alpha.test(v);
7683         },
7684         /**
7685          * The error text to display when the alpha validation function returns false
7686          * @type String
7687          */
7688         'alphaText' : 'This field should only contain letters and _',
7689         /**
7690          * The keystroke filter mask to be applied on alpha input
7691          * @type RegExp
7692          */
7693         'alphaMask' : /[a-z_]/i,
7694
7695         /**
7696          * The function used to validate alphanumeric values
7697          * @param {String} value The value
7698          */
7699         'alphanum' : function(v){
7700             return alphanum.test(v);
7701         },
7702         /**
7703          * The error text to display when the alphanumeric validation function returns false
7704          * @type String
7705          */
7706         'alphanumText' : 'This field should only contain letters, numbers and _',
7707         /**
7708          * The keystroke filter mask to be applied on alphanumeric input
7709          * @type RegExp
7710          */
7711         'alphanumMask' : /[a-z0-9_]/i
7712     };
7713 }();/*
7714  * - LGPL
7715  *
7716  * Input
7717  * 
7718  */
7719
7720 /**
7721  * @class Roo.bootstrap.Input
7722  * @extends Roo.bootstrap.Component
7723  * Bootstrap Input class
7724  * @cfg {Boolean} disabled is it disabled
7725  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7726  * @cfg {String} name name of the input
7727  * @cfg {string} fieldLabel - the label associated
7728  * @cfg {string} placeholder - placeholder to put in text.
7729  * @cfg {string}  before - input group add on before
7730  * @cfg {string} after - input group add on after
7731  * @cfg {string} size - (lg|sm) or leave empty..
7732  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7733  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7734  * @cfg {Number} md colspan out of 12 for computer-sized screens
7735  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7736  * @cfg {string} value default value of the input
7737  * @cfg {Number} labelWidth set the width of label (0-12)
7738  * @cfg {String} labelAlign (top|left)
7739  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7740  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7741
7742  * @cfg {String} align (left|center|right) Default left
7743  * @cfg {Boolean} forceFeedback (true|false) Default false
7744  * 
7745  * 
7746  * 
7747  * 
7748  * @constructor
7749  * Create a new Input
7750  * @param {Object} config The config object
7751  */
7752
7753 Roo.bootstrap.Input = function(config){
7754     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7755    
7756         this.addEvents({
7757             /**
7758              * @event focus
7759              * Fires when this field receives input focus.
7760              * @param {Roo.form.Field} this
7761              */
7762             focus : true,
7763             /**
7764              * @event blur
7765              * Fires when this field loses input focus.
7766              * @param {Roo.form.Field} this
7767              */
7768             blur : true,
7769             /**
7770              * @event specialkey
7771              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7772              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7773              * @param {Roo.form.Field} this
7774              * @param {Roo.EventObject} e The event object
7775              */
7776             specialkey : true,
7777             /**
7778              * @event change
7779              * Fires just before the field blurs if the field value has changed.
7780              * @param {Roo.form.Field} this
7781              * @param {Mixed} newValue The new value
7782              * @param {Mixed} oldValue The original value
7783              */
7784             change : true,
7785             /**
7786              * @event invalid
7787              * Fires after the field has been marked as invalid.
7788              * @param {Roo.form.Field} this
7789              * @param {String} msg The validation message
7790              */
7791             invalid : true,
7792             /**
7793              * @event valid
7794              * Fires after the field has been validated with no errors.
7795              * @param {Roo.form.Field} this
7796              */
7797             valid : true,
7798              /**
7799              * @event keyup
7800              * Fires after the key up
7801              * @param {Roo.form.Field} this
7802              * @param {Roo.EventObject}  e The event Object
7803              */
7804             keyup : true
7805         });
7806 };
7807
7808 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7809      /**
7810      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7811       automatic validation (defaults to "keyup").
7812      */
7813     validationEvent : "keyup",
7814      /**
7815      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7816      */
7817     validateOnBlur : true,
7818     /**
7819      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7820      */
7821     validationDelay : 250,
7822      /**
7823      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7824      */
7825     focusClass : "x-form-focus",  // not needed???
7826     
7827        
7828     /**
7829      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7830      */
7831     invalidClass : "has-warning",
7832     
7833     /**
7834      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7835      */
7836     validClass : "has-success",
7837     
7838     /**
7839      * @cfg {Boolean} hasFeedback (true|false) default true
7840      */
7841     hasFeedback : true,
7842     
7843     /**
7844      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7845      */
7846     invalidFeedbackClass : "glyphicon-warning-sign",
7847     
7848     /**
7849      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7850      */
7851     validFeedbackClass : "glyphicon-ok",
7852     
7853     /**
7854      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7855      */
7856     selectOnFocus : false,
7857     
7858      /**
7859      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7860      */
7861     maskRe : null,
7862        /**
7863      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7864      */
7865     vtype : null,
7866     
7867       /**
7868      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7869      */
7870     disableKeyFilter : false,
7871     
7872        /**
7873      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7874      */
7875     disabled : false,
7876      /**
7877      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7878      */
7879     allowBlank : true,
7880     /**
7881      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7882      */
7883     blankText : "This field is required",
7884     
7885      /**
7886      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7887      */
7888     minLength : 0,
7889     /**
7890      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7891      */
7892     maxLength : Number.MAX_VALUE,
7893     /**
7894      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7895      */
7896     minLengthText : "The minimum length for this field is {0}",
7897     /**
7898      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7899      */
7900     maxLengthText : "The maximum length for this field is {0}",
7901   
7902     
7903     /**
7904      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7905      * If available, this function will be called only after the basic validators all return true, and will be passed the
7906      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7907      */
7908     validator : null,
7909     /**
7910      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7911      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7912      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7913      */
7914     regex : null,
7915     /**
7916      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7917      */
7918     regexText : "",
7919     
7920     autocomplete: false,
7921     
7922     
7923     fieldLabel : '',
7924     inputType : 'text',
7925     
7926     name : false,
7927     placeholder: false,
7928     before : false,
7929     after : false,
7930     size : false,
7931     hasFocus : false,
7932     preventMark: false,
7933     isFormField : true,
7934     value : '',
7935     labelWidth : 2,
7936     labelAlign : false,
7937     readOnly : false,
7938     align : false,
7939     formatedValue : false,
7940     forceFeedback : false,
7941     
7942     parentLabelAlign : function()
7943     {
7944         var parent = this;
7945         while (parent.parent()) {
7946             parent = parent.parent();
7947             if (typeof(parent.labelAlign) !='undefined') {
7948                 return parent.labelAlign;
7949             }
7950         }
7951         return 'left';
7952         
7953     },
7954     
7955     getAutoCreate : function(){
7956         
7957         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7958         
7959         var id = Roo.id();
7960         
7961         var cfg = {};
7962         
7963         if(this.inputType != 'hidden'){
7964             cfg.cls = 'form-group' //input-group
7965         }
7966         
7967         var input =  {
7968             tag: 'input',
7969             id : id,
7970             type : this.inputType,
7971             value : this.value,
7972             cls : 'form-control',
7973             placeholder : this.placeholder || '',
7974             autocomplete : this.autocomplete || 'new-password'
7975         };
7976         
7977         
7978         if(this.align){
7979             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7980         }
7981         
7982         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7983             input.maxLength = this.maxLength;
7984         }
7985         
7986         if (this.disabled) {
7987             input.disabled=true;
7988         }
7989         
7990         if (this.readOnly) {
7991             input.readonly=true;
7992         }
7993         
7994         if (this.name) {
7995             input.name = this.name;
7996         }
7997         if (this.size) {
7998             input.cls += ' input-' + this.size;
7999         }
8000         var settings=this;
8001         ['xs','sm','md','lg'].map(function(size){
8002             if (settings[size]) {
8003                 cfg.cls += ' col-' + size + '-' + settings[size];
8004             }
8005         });
8006         
8007         var inputblock = input;
8008         
8009         var feedback = {
8010             tag: 'span',
8011             cls: 'glyphicon form-control-feedback'
8012         };
8013             
8014         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8015             
8016             inputblock = {
8017                 cls : 'has-feedback',
8018                 cn :  [
8019                     input,
8020                     feedback
8021                 ] 
8022             };  
8023         }
8024         
8025         if (this.before || this.after) {
8026             
8027             inputblock = {
8028                 cls : 'input-group',
8029                 cn :  [] 
8030             };
8031             
8032             if (this.before && typeof(this.before) == 'string') {
8033                 
8034                 inputblock.cn.push({
8035                     tag :'span',
8036                     cls : 'roo-input-before input-group-addon',
8037                     html : this.before
8038                 });
8039             }
8040             if (this.before && typeof(this.before) == 'object') {
8041                 this.before = Roo.factory(this.before);
8042                 
8043                 inputblock.cn.push({
8044                     tag :'span',
8045                     cls : 'roo-input-before input-group-' +
8046                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8047                 });
8048             }
8049             
8050             inputblock.cn.push(input);
8051             
8052             if (this.after && typeof(this.after) == 'string') {
8053                 inputblock.cn.push({
8054                     tag :'span',
8055                     cls : 'roo-input-after input-group-addon',
8056                     html : this.after
8057                 });
8058             }
8059             if (this.after && typeof(this.after) == 'object') {
8060                 this.after = Roo.factory(this.after);
8061                 
8062                 inputblock.cn.push({
8063                     tag :'span',
8064                     cls : 'roo-input-after input-group-' +
8065                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8066                 });
8067             }
8068             
8069             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8070                 inputblock.cls += ' has-feedback';
8071                 inputblock.cn.push(feedback);
8072             }
8073         };
8074         
8075         if (align ==='left' && this.fieldLabel.length) {
8076                 
8077                 cfg.cn = [
8078                     
8079                     {
8080                         tag: 'label',
8081                         'for' :  id,
8082                         cls : 'control-label col-sm-' + this.labelWidth,
8083                         html : this.fieldLabel
8084                         
8085                     },
8086                     {
8087                         cls : "col-sm-" + (12 - this.labelWidth), 
8088                         cn: [
8089                             inputblock
8090                         ]
8091                     }
8092                     
8093                 ];
8094         } else if ( this.fieldLabel.length) {
8095                 
8096                  cfg.cn = [
8097                    
8098                     {
8099                         tag: 'label',
8100                         //cls : 'input-group-addon',
8101                         html : this.fieldLabel
8102                         
8103                     },
8104                     
8105                     inputblock
8106                     
8107                 ];
8108
8109         } else {
8110             
8111                 cfg.cn = [
8112                     
8113                         inputblock
8114                     
8115                 ];
8116                 
8117                 
8118         };
8119         
8120         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8121            cfg.cls += ' navbar-form';
8122         }
8123         
8124         return cfg;
8125         
8126     },
8127     /**
8128      * return the real input element.
8129      */
8130     inputEl: function ()
8131     {
8132         return this.el.select('input.form-control',true).first();
8133     },
8134     
8135     tooltipEl : function()
8136     {
8137         return this.inputEl();
8138     },
8139     
8140     setDisabled : function(v)
8141     {
8142         var i  = this.inputEl().dom;
8143         if (!v) {
8144             i.removeAttribute('disabled');
8145             return;
8146             
8147         }
8148         i.setAttribute('disabled','true');
8149     },
8150     initEvents : function()
8151     {
8152           
8153         this.inputEl().on("keydown" , this.fireKey,  this);
8154         this.inputEl().on("focus", this.onFocus,  this);
8155         this.inputEl().on("blur", this.onBlur,  this);
8156         
8157         this.inputEl().relayEvent('keyup', this);
8158  
8159         // reference to original value for reset
8160         this.originalValue = this.getValue();
8161         //Roo.form.TextField.superclass.initEvents.call(this);
8162         if(this.validationEvent == 'keyup'){
8163             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8164             this.inputEl().on('keyup', this.filterValidation, this);
8165         }
8166         else if(this.validationEvent !== false){
8167             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8168         }
8169         
8170         if(this.selectOnFocus){
8171             this.on("focus", this.preFocus, this);
8172             
8173         }
8174         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8175             this.inputEl().on("keypress", this.filterKeys, this);
8176         }
8177        /* if(this.grow){
8178             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8179             this.el.on("click", this.autoSize,  this);
8180         }
8181         */
8182         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8183             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8184         }
8185         
8186         if (typeof(this.before) == 'object') {
8187             this.before.render(this.el.select('.roo-input-before',true).first());
8188         }
8189         if (typeof(this.after) == 'object') {
8190             this.after.render(this.el.select('.roo-input-after',true).first());
8191         }
8192         
8193         
8194     },
8195     filterValidation : function(e){
8196         if(!e.isNavKeyPress()){
8197             this.validationTask.delay(this.validationDelay);
8198         }
8199     },
8200      /**
8201      * Validates the field value
8202      * @return {Boolean} True if the value is valid, else false
8203      */
8204     validate : function(){
8205         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8206         if(this.disabled || this.validateValue(this.getRawValue())){
8207             this.markValid();
8208             return true;
8209         }
8210         
8211         this.markInvalid();
8212         return false;
8213     },
8214     
8215     
8216     /**
8217      * Validates a value according to the field's validation rules and marks the field as invalid
8218      * if the validation fails
8219      * @param {Mixed} value The value to validate
8220      * @return {Boolean} True if the value is valid, else false
8221      */
8222     validateValue : function(value){
8223         if(value.length < 1)  { // if it's blank
8224             if(this.allowBlank){
8225                 return true;
8226             }
8227             return false;
8228         }
8229         
8230         if(value.length < this.minLength){
8231             return false;
8232         }
8233         if(value.length > this.maxLength){
8234             return false;
8235         }
8236         if(this.vtype){
8237             var vt = Roo.form.VTypes;
8238             if(!vt[this.vtype](value, this)){
8239                 return false;
8240             }
8241         }
8242         if(typeof this.validator == "function"){
8243             var msg = this.validator(value);
8244             if(msg !== true){
8245                 return false;
8246             }
8247         }
8248         
8249         if(this.regex && !this.regex.test(value)){
8250             return false;
8251         }
8252         
8253         return true;
8254     },
8255
8256     
8257     
8258      // private
8259     fireKey : function(e){
8260         //Roo.log('field ' + e.getKey());
8261         if(e.isNavKeyPress()){
8262             this.fireEvent("specialkey", this, e);
8263         }
8264     },
8265     focus : function (selectText){
8266         if(this.rendered){
8267             this.inputEl().focus();
8268             if(selectText === true){
8269                 this.inputEl().dom.select();
8270             }
8271         }
8272         return this;
8273     } ,
8274     
8275     onFocus : function(){
8276         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8277            // this.el.addClass(this.focusClass);
8278         }
8279         if(!this.hasFocus){
8280             this.hasFocus = true;
8281             this.startValue = this.getValue();
8282             this.fireEvent("focus", this);
8283         }
8284     },
8285     
8286     beforeBlur : Roo.emptyFn,
8287
8288     
8289     // private
8290     onBlur : function(){
8291         this.beforeBlur();
8292         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8293             //this.el.removeClass(this.focusClass);
8294         }
8295         this.hasFocus = false;
8296         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8297             this.validate();
8298         }
8299         var v = this.getValue();
8300         if(String(v) !== String(this.startValue)){
8301             this.fireEvent('change', this, v, this.startValue);
8302         }
8303         this.fireEvent("blur", this);
8304     },
8305     
8306     /**
8307      * Resets the current field value to the originally loaded value and clears any validation messages
8308      */
8309     reset : function(){
8310         this.setValue(this.originalValue);
8311         this.validate();
8312     },
8313      /**
8314      * Returns the name of the field
8315      * @return {Mixed} name The name field
8316      */
8317     getName: function(){
8318         return this.name;
8319     },
8320      /**
8321      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8322      * @return {Mixed} value The field value
8323      */
8324     getValue : function(){
8325         
8326         var v = this.inputEl().getValue();
8327         
8328         return v;
8329     },
8330     /**
8331      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8332      * @return {Mixed} value The field value
8333      */
8334     getRawValue : function(){
8335         var v = this.inputEl().getValue();
8336         
8337         return v;
8338     },
8339     
8340     /**
8341      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8342      * @param {Mixed} value The value to set
8343      */
8344     setRawValue : function(v){
8345         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8346     },
8347     
8348     selectText : function(start, end){
8349         var v = this.getRawValue();
8350         if(v.length > 0){
8351             start = start === undefined ? 0 : start;
8352             end = end === undefined ? v.length : end;
8353             var d = this.inputEl().dom;
8354             if(d.setSelectionRange){
8355                 d.setSelectionRange(start, end);
8356             }else if(d.createTextRange){
8357                 var range = d.createTextRange();
8358                 range.moveStart("character", start);
8359                 range.moveEnd("character", v.length-end);
8360                 range.select();
8361             }
8362         }
8363     },
8364     
8365     /**
8366      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8367      * @param {Mixed} value The value to set
8368      */
8369     setValue : function(v){
8370         this.value = v;
8371         if(this.rendered){
8372             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8373             this.validate();
8374         }
8375     },
8376     
8377     /*
8378     processValue : function(value){
8379         if(this.stripCharsRe){
8380             var newValue = value.replace(this.stripCharsRe, '');
8381             if(newValue !== value){
8382                 this.setRawValue(newValue);
8383                 return newValue;
8384             }
8385         }
8386         return value;
8387     },
8388   */
8389     preFocus : function(){
8390         
8391         if(this.selectOnFocus){
8392             this.inputEl().dom.select();
8393         }
8394     },
8395     filterKeys : function(e){
8396         var k = e.getKey();
8397         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8398             return;
8399         }
8400         var c = e.getCharCode(), cc = String.fromCharCode(c);
8401         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8402             return;
8403         }
8404         if(!this.maskRe.test(cc)){
8405             e.stopEvent();
8406         }
8407     },
8408      /**
8409      * Clear any invalid styles/messages for this field
8410      */
8411     clearInvalid : function(){
8412         
8413         if(!this.el || this.preventMark){ // not rendered
8414             return;
8415         }
8416         
8417         var label = this.el.select('label', true).first();
8418         var icon = this.el.select('i.fa-star', true).first();
8419         
8420         if(label && icon){
8421             icon.remove();
8422         }
8423         
8424         this.el.removeClass(this.invalidClass);
8425         
8426         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8427             
8428             var feedback = this.el.select('.form-control-feedback', true).first();
8429             
8430             if(feedback){
8431                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8432             }
8433             
8434         }
8435         
8436         this.fireEvent('valid', this);
8437     },
8438     
8439      /**
8440      * Mark this field as valid
8441      */
8442     markValid : function()
8443     {
8444         if(!this.el  || this.preventMark){ // not rendered
8445             return;
8446         }
8447         
8448         this.el.removeClass([this.invalidClass, this.validClass]);
8449         
8450         var feedback = this.el.select('.form-control-feedback', true).first();
8451             
8452         if(feedback){
8453             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8454         }
8455
8456         if(this.disabled || this.allowBlank){
8457             return;
8458         }
8459         
8460         var formGroup = this.el.findParent('.form-group', false, true);
8461         
8462         if(formGroup){
8463             
8464             var label = formGroup.select('label', true).first();
8465             var icon = formGroup.select('i.fa-star', true).first();
8466             
8467             if(label && icon){
8468                 icon.remove();
8469             }
8470         }
8471         
8472         this.el.addClass(this.validClass);
8473         
8474         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8475             
8476             var feedback = this.el.select('.form-control-feedback', true).first();
8477             
8478             if(feedback){
8479                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8480                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8481             }
8482             
8483         }
8484         
8485         this.fireEvent('valid', this);
8486     },
8487     
8488      /**
8489      * Mark this field as invalid
8490      * @param {String} msg The validation message
8491      */
8492     markInvalid : function(msg)
8493     {
8494         if(!this.el  || this.preventMark){ // not rendered
8495             return;
8496         }
8497         
8498         this.el.removeClass([this.invalidClass, this.validClass]);
8499         
8500         var feedback = this.el.select('.form-control-feedback', true).first();
8501             
8502         if(feedback){
8503             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8504         }
8505
8506         if(this.disabled || this.allowBlank){
8507             return;
8508         }
8509         
8510         var formGroup = this.el.findParent('.form-group', false, true);
8511         
8512         if(formGroup){
8513             var label = formGroup.select('label', true).first();
8514             var icon = formGroup.select('i.fa-star', true).first();
8515
8516             if(!this.getValue().length && label && !icon){
8517                 this.el.findParent('.form-group', false, true).createChild({
8518                     tag : 'i',
8519                     cls : 'text-danger fa fa-lg fa-star',
8520                     tooltip : 'This field is required',
8521                     style : 'margin-right:5px;'
8522                 }, label, true);
8523             }
8524         }
8525         
8526         
8527         this.el.addClass(this.invalidClass);
8528         
8529         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8530             
8531             var feedback = this.el.select('.form-control-feedback', true).first();
8532             
8533             if(feedback){
8534                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8535                 
8536                 if(this.getValue().length || this.forceFeedback){
8537                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8538                 }
8539                 
8540             }
8541             
8542         }
8543         
8544         this.fireEvent('invalid', this, msg);
8545     },
8546     // private
8547     SafariOnKeyDown : function(event)
8548     {
8549         // this is a workaround for a password hang bug on chrome/ webkit.
8550         
8551         var isSelectAll = false;
8552         
8553         if(this.inputEl().dom.selectionEnd > 0){
8554             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8555         }
8556         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8557             event.preventDefault();
8558             this.setValue('');
8559             return;
8560         }
8561         
8562         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8563             
8564             event.preventDefault();
8565             // this is very hacky as keydown always get's upper case.
8566             //
8567             var cc = String.fromCharCode(event.getCharCode());
8568             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8569             
8570         }
8571     },
8572     adjustWidth : function(tag, w){
8573         tag = tag.toLowerCase();
8574         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8575             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8576                 if(tag == 'input'){
8577                     return w + 2;
8578                 }
8579                 if(tag == 'textarea'){
8580                     return w-2;
8581                 }
8582             }else if(Roo.isOpera){
8583                 if(tag == 'input'){
8584                     return w + 2;
8585                 }
8586                 if(tag == 'textarea'){
8587                     return w-2;
8588                 }
8589             }
8590         }
8591         return w;
8592     }
8593     
8594 });
8595
8596  
8597 /*
8598  * - LGPL
8599  *
8600  * Input
8601  * 
8602  */
8603
8604 /**
8605  * @class Roo.bootstrap.TextArea
8606  * @extends Roo.bootstrap.Input
8607  * Bootstrap TextArea class
8608  * @cfg {Number} cols Specifies the visible width of a text area
8609  * @cfg {Number} rows Specifies the visible number of lines in a text area
8610  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8611  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8612  * @cfg {string} html text
8613  * 
8614  * @constructor
8615  * Create a new TextArea
8616  * @param {Object} config The config object
8617  */
8618
8619 Roo.bootstrap.TextArea = function(config){
8620     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8621    
8622 };
8623
8624 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8625      
8626     cols : false,
8627     rows : 5,
8628     readOnly : false,
8629     warp : 'soft',
8630     resize : false,
8631     value: false,
8632     html: false,
8633     
8634     getAutoCreate : function(){
8635         
8636         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8637         
8638         var id = Roo.id();
8639         
8640         var cfg = {};
8641         
8642         var input =  {
8643             tag: 'textarea',
8644             id : id,
8645             warp : this.warp,
8646             rows : this.rows,
8647             value : this.value || '',
8648             html: this.html || '',
8649             cls : 'form-control',
8650             placeholder : this.placeholder || '' 
8651             
8652         };
8653         
8654         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8655             input.maxLength = this.maxLength;
8656         }
8657         
8658         if(this.resize){
8659             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8660         }
8661         
8662         if(this.cols){
8663             input.cols = this.cols;
8664         }
8665         
8666         if (this.readOnly) {
8667             input.readonly = true;
8668         }
8669         
8670         if (this.name) {
8671             input.name = this.name;
8672         }
8673         
8674         if (this.size) {
8675             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8676         }
8677         
8678         var settings=this;
8679         ['xs','sm','md','lg'].map(function(size){
8680             if (settings[size]) {
8681                 cfg.cls += ' col-' + size + '-' + settings[size];
8682             }
8683         });
8684         
8685         var inputblock = input;
8686         
8687         if(this.hasFeedback && !this.allowBlank){
8688             
8689             var feedback = {
8690                 tag: 'span',
8691                 cls: 'glyphicon form-control-feedback'
8692             };
8693
8694             inputblock = {
8695                 cls : 'has-feedback',
8696                 cn :  [
8697                     input,
8698                     feedback
8699                 ] 
8700             };  
8701         }
8702         
8703         
8704         if (this.before || this.after) {
8705             
8706             inputblock = {
8707                 cls : 'input-group',
8708                 cn :  [] 
8709             };
8710             if (this.before) {
8711                 inputblock.cn.push({
8712                     tag :'span',
8713                     cls : 'input-group-addon',
8714                     html : this.before
8715                 });
8716             }
8717             
8718             inputblock.cn.push(input);
8719             
8720             if(this.hasFeedback && !this.allowBlank){
8721                 inputblock.cls += ' has-feedback';
8722                 inputblock.cn.push(feedback);
8723             }
8724             
8725             if (this.after) {
8726                 inputblock.cn.push({
8727                     tag :'span',
8728                     cls : 'input-group-addon',
8729                     html : this.after
8730                 });
8731             }
8732             
8733         }
8734         
8735         if (align ==='left' && this.fieldLabel.length) {
8736 //                Roo.log("left and has label");
8737                 cfg.cn = [
8738                     
8739                     {
8740                         tag: 'label',
8741                         'for' :  id,
8742                         cls : 'control-label col-sm-' + this.labelWidth,
8743                         html : this.fieldLabel
8744                         
8745                     },
8746                     {
8747                         cls : "col-sm-" + (12 - this.labelWidth), 
8748                         cn: [
8749                             inputblock
8750                         ]
8751                     }
8752                     
8753                 ];
8754         } else if ( this.fieldLabel.length) {
8755 //                Roo.log(" label");
8756                  cfg.cn = [
8757                    
8758                     {
8759                         tag: 'label',
8760                         //cls : 'input-group-addon',
8761                         html : this.fieldLabel
8762                         
8763                     },
8764                     
8765                     inputblock
8766                     
8767                 ];
8768
8769         } else {
8770             
8771 //                   Roo.log(" no label && no align");
8772                 cfg.cn = [
8773                     
8774                         inputblock
8775                     
8776                 ];
8777                 
8778                 
8779         }
8780         
8781         if (this.disabled) {
8782             input.disabled=true;
8783         }
8784         
8785         return cfg;
8786         
8787     },
8788     /**
8789      * return the real textarea element.
8790      */
8791     inputEl: function ()
8792     {
8793         return this.el.select('textarea.form-control',true).first();
8794     },
8795     
8796     /**
8797      * Clear any invalid styles/messages for this field
8798      */
8799     clearInvalid : function()
8800     {
8801         
8802         if(!this.el || this.preventMark){ // not rendered
8803             return;
8804         }
8805         
8806         var label = this.el.select('label', true).first();
8807         var icon = this.el.select('i.fa-star', true).first();
8808         
8809         if(label && icon){
8810             icon.remove();
8811         }
8812         
8813         this.el.removeClass(this.invalidClass);
8814         
8815         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8816             
8817             var feedback = this.el.select('.form-control-feedback', true).first();
8818             
8819             if(feedback){
8820                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8821             }
8822             
8823         }
8824         
8825         this.fireEvent('valid', this);
8826     },
8827     
8828      /**
8829      * Mark this field as valid
8830      */
8831     markValid : function()
8832     {
8833         if(!this.el  || this.preventMark){ // not rendered
8834             return;
8835         }
8836         
8837         this.el.removeClass([this.invalidClass, this.validClass]);
8838         
8839         var feedback = this.el.select('.form-control-feedback', true).first();
8840             
8841         if(feedback){
8842             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8843         }
8844
8845         if(this.disabled || this.allowBlank){
8846             return;
8847         }
8848         
8849         var label = this.el.select('label', true).first();
8850         var icon = this.el.select('i.fa-star', true).first();
8851         
8852         if(label && icon){
8853             icon.remove();
8854         }
8855         
8856         this.el.addClass(this.validClass);
8857         
8858         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8859             
8860             var feedback = this.el.select('.form-control-feedback', true).first();
8861             
8862             if(feedback){
8863                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8864                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8865             }
8866             
8867         }
8868         
8869         this.fireEvent('valid', this);
8870     },
8871     
8872      /**
8873      * Mark this field as invalid
8874      * @param {String} msg The validation message
8875      */
8876     markInvalid : function(msg)
8877     {
8878         if(!this.el  || this.preventMark){ // not rendered
8879             return;
8880         }
8881         
8882         this.el.removeClass([this.invalidClass, this.validClass]);
8883         
8884         var feedback = this.el.select('.form-control-feedback', true).first();
8885             
8886         if(feedback){
8887             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8888         }
8889
8890         if(this.disabled || this.allowBlank){
8891             return;
8892         }
8893         
8894         var label = this.el.select('label', true).first();
8895         var icon = this.el.select('i.fa-star', true).first();
8896         
8897         if(!this.getValue().length && label && !icon){
8898             this.el.createChild({
8899                 tag : 'i',
8900                 cls : 'text-danger fa fa-lg fa-star',
8901                 tooltip : 'This field is required',
8902                 style : 'margin-right:5px;'
8903             }, label, true);
8904         }
8905
8906         this.el.addClass(this.invalidClass);
8907         
8908         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8909             
8910             var feedback = this.el.select('.form-control-feedback', true).first();
8911             
8912             if(feedback){
8913                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8914                 
8915                 if(this.getValue().length || this.forceFeedback){
8916                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8917                 }
8918                 
8919             }
8920             
8921         }
8922         
8923         this.fireEvent('invalid', this, msg);
8924     }
8925 });
8926
8927  
8928 /*
8929  * - LGPL
8930  *
8931  * trigger field - base class for combo..
8932  * 
8933  */
8934  
8935 /**
8936  * @class Roo.bootstrap.TriggerField
8937  * @extends Roo.bootstrap.Input
8938  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8939  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8940  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8941  * for which you can provide a custom implementation.  For example:
8942  * <pre><code>
8943 var trigger = new Roo.bootstrap.TriggerField();
8944 trigger.onTriggerClick = myTriggerFn;
8945 trigger.applyTo('my-field');
8946 </code></pre>
8947  *
8948  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8949  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8950  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8951  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8952  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8953
8954  * @constructor
8955  * Create a new TriggerField.
8956  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8957  * to the base TextField)
8958  */
8959 Roo.bootstrap.TriggerField = function(config){
8960     this.mimicing = false;
8961     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8962 };
8963
8964 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8965     /**
8966      * @cfg {String} triggerClass A CSS class to apply to the trigger
8967      */
8968      /**
8969      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8970      */
8971     hideTrigger:false,
8972
8973     /**
8974      * @cfg {Boolean} removable (true|false) special filter default false
8975      */
8976     removable : false,
8977     
8978     /** @cfg {Boolean} grow @hide */
8979     /** @cfg {Number} growMin @hide */
8980     /** @cfg {Number} growMax @hide */
8981
8982     /**
8983      * @hide 
8984      * @method
8985      */
8986     autoSize: Roo.emptyFn,
8987     // private
8988     monitorTab : true,
8989     // private
8990     deferHeight : true,
8991
8992     
8993     actionMode : 'wrap',
8994     
8995     caret : false,
8996     
8997     
8998     getAutoCreate : function(){
8999        
9000         var align = this.labelAlign || this.parentLabelAlign();
9001         
9002         var id = Roo.id();
9003         
9004         var cfg = {
9005             cls: 'form-group' //input-group
9006         };
9007         
9008         
9009         var input =  {
9010             tag: 'input',
9011             id : id,
9012             type : this.inputType,
9013             cls : 'form-control',
9014             autocomplete: 'new-password',
9015             placeholder : this.placeholder || '' 
9016             
9017         };
9018         if (this.name) {
9019             input.name = this.name;
9020         }
9021         if (this.size) {
9022             input.cls += ' input-' + this.size;
9023         }
9024         
9025         if (this.disabled) {
9026             input.disabled=true;
9027         }
9028         
9029         var inputblock = input;
9030         
9031         if(this.hasFeedback && !this.allowBlank){
9032             
9033             var feedback = {
9034                 tag: 'span',
9035                 cls: 'glyphicon form-control-feedback'
9036             };
9037             
9038             if(this.removable && !this.editable && !this.tickable){
9039                 inputblock = {
9040                     cls : 'has-feedback',
9041                     cn :  [
9042                         inputblock,
9043                         {
9044                             tag: 'button',
9045                             html : 'x',
9046                             cls : 'roo-combo-removable-btn close'
9047                         },
9048                         feedback
9049                     ] 
9050                 };
9051             } else {
9052                 inputblock = {
9053                     cls : 'has-feedback',
9054                     cn :  [
9055                         inputblock,
9056                         feedback
9057                     ] 
9058                 };
9059             }
9060
9061         } else {
9062             if(this.removable && !this.editable && !this.tickable){
9063                 inputblock = {
9064                     cls : 'roo-removable',
9065                     cn :  [
9066                         inputblock,
9067                         {
9068                             tag: 'button',
9069                             html : 'x',
9070                             cls : 'roo-combo-removable-btn close'
9071                         }
9072                     ] 
9073                 };
9074             }
9075         }
9076         
9077         if (this.before || this.after) {
9078             
9079             inputblock = {
9080                 cls : 'input-group',
9081                 cn :  [] 
9082             };
9083             if (this.before) {
9084                 inputblock.cn.push({
9085                     tag :'span',
9086                     cls : 'input-group-addon',
9087                     html : this.before
9088                 });
9089             }
9090             
9091             inputblock.cn.push(input);
9092             
9093             if(this.hasFeedback && !this.allowBlank){
9094                 inputblock.cls += ' has-feedback';
9095                 inputblock.cn.push(feedback);
9096             }
9097             
9098             if (this.after) {
9099                 inputblock.cn.push({
9100                     tag :'span',
9101                     cls : 'input-group-addon',
9102                     html : this.after
9103                 });
9104             }
9105             
9106         };
9107         
9108         var box = {
9109             tag: 'div',
9110             cn: [
9111                 {
9112                     tag: 'input',
9113                     type : 'hidden',
9114                     cls: 'form-hidden-field'
9115                 },
9116                 inputblock
9117             ]
9118             
9119         };
9120         
9121         if(this.multiple){
9122             box = {
9123                 tag: 'div',
9124                 cn: [
9125                     {
9126                         tag: 'input',
9127                         type : 'hidden',
9128                         cls: 'form-hidden-field'
9129                     },
9130                     {
9131                         tag: 'ul',
9132                         cls: 'select2-choices',
9133                         cn:[
9134                             {
9135                                 tag: 'li',
9136                                 cls: 'select2-search-field',
9137                                 cn: [
9138
9139                                     inputblock
9140                                 ]
9141                             }
9142                         ]
9143                     }
9144                 ]
9145             }
9146         };
9147         
9148         var combobox = {
9149             cls: 'select2-container input-group',
9150             cn: [
9151                 box
9152 //                {
9153 //                    tag: 'ul',
9154 //                    cls: 'typeahead typeahead-long dropdown-menu',
9155 //                    style: 'display:none'
9156 //                }
9157             ]
9158         };
9159         
9160         if(!this.multiple && this.showToggleBtn){
9161             
9162             var caret = {
9163                         tag: 'span',
9164                         cls: 'caret'
9165              };
9166             if (this.caret != false) {
9167                 caret = {
9168                      tag: 'i',
9169                      cls: 'fa fa-' + this.caret
9170                 };
9171                 
9172             }
9173             
9174             combobox.cn.push({
9175                 tag :'span',
9176                 cls : 'input-group-addon btn dropdown-toggle',
9177                 cn : [
9178                     caret,
9179                     {
9180                         tag: 'span',
9181                         cls: 'combobox-clear',
9182                         cn  : [
9183                             {
9184                                 tag : 'i',
9185                                 cls: 'icon-remove'
9186                             }
9187                         ]
9188                     }
9189                 ]
9190
9191             })
9192         }
9193         
9194         if(this.multiple){
9195             combobox.cls += ' select2-container-multi';
9196         }
9197         
9198         if (align ==='left' && this.fieldLabel.length) {
9199             
9200 //                Roo.log("left and has label");
9201                 cfg.cn = [
9202                     
9203                     {
9204                         tag: 'label',
9205                         'for' :  id,
9206                         cls : 'control-label col-sm-' + this.labelWidth,
9207                         html : this.fieldLabel
9208                         
9209                     },
9210                     {
9211                         cls : "col-sm-" + (12 - this.labelWidth), 
9212                         cn: [
9213                             combobox
9214                         ]
9215                     }
9216                     
9217                 ];
9218         } else if ( this.fieldLabel.length) {
9219 //                Roo.log(" label");
9220                  cfg.cn = [
9221                    
9222                     {
9223                         tag: 'label',
9224                         //cls : 'input-group-addon',
9225                         html : this.fieldLabel
9226                         
9227                     },
9228                     
9229                     combobox
9230                     
9231                 ];
9232
9233         } else {
9234             
9235 //                Roo.log(" no label && no align");
9236                 cfg = combobox
9237                      
9238                 
9239         }
9240          
9241         var settings=this;
9242         ['xs','sm','md','lg'].map(function(size){
9243             if (settings[size]) {
9244                 cfg.cls += ' col-' + size + '-' + settings[size];
9245             }
9246         });
9247         
9248         return cfg;
9249         
9250     },
9251     
9252     
9253     
9254     // private
9255     onResize : function(w, h){
9256 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9257 //        if(typeof w == 'number'){
9258 //            var x = w - this.trigger.getWidth();
9259 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9260 //            this.trigger.setStyle('left', x+'px');
9261 //        }
9262     },
9263
9264     // private
9265     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9266
9267     // private
9268     getResizeEl : function(){
9269         return this.inputEl();
9270     },
9271
9272     // private
9273     getPositionEl : function(){
9274         return this.inputEl();
9275     },
9276
9277     // private
9278     alignErrorIcon : function(){
9279         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9280     },
9281
9282     // private
9283     initEvents : function(){
9284         
9285         this.createList();
9286         
9287         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9288         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9289         if(!this.multiple && this.showToggleBtn){
9290             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9291             if(this.hideTrigger){
9292                 this.trigger.setDisplayed(false);
9293             }
9294             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9295         }
9296         
9297         if(this.multiple){
9298             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9299         }
9300         
9301         if(this.removable && !this.editable && !this.tickable){
9302             var close = this.closeTriggerEl();
9303             
9304             if(close){
9305                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9306                 close.on('click', this.removeBtnClick, this, close);
9307             }
9308         }
9309         
9310         //this.trigger.addClassOnOver('x-form-trigger-over');
9311         //this.trigger.addClassOnClick('x-form-trigger-click');
9312         
9313         //if(!this.width){
9314         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9315         //}
9316     },
9317     
9318     closeTriggerEl : function()
9319     {
9320         var close = this.el.select('.roo-combo-removable-btn', true).first();
9321         return close ? close : false;
9322     },
9323     
9324     removeBtnClick : function(e, h, el)
9325     {
9326         e.preventDefault();
9327         
9328         if(this.fireEvent("remove", this) !== false){
9329             this.reset();
9330         }
9331     },
9332     
9333     createList : function()
9334     {
9335         this.list = Roo.get(document.body).createChild({
9336             tag: 'ul',
9337             cls: 'typeahead typeahead-long dropdown-menu',
9338             style: 'display:none'
9339         });
9340         
9341         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9342         
9343     },
9344
9345     // private
9346     initTrigger : function(){
9347        
9348     },
9349
9350     // private
9351     onDestroy : function(){
9352         if(this.trigger){
9353             this.trigger.removeAllListeners();
9354           //  this.trigger.remove();
9355         }
9356         //if(this.wrap){
9357         //    this.wrap.remove();
9358         //}
9359         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9360     },
9361
9362     // private
9363     onFocus : function(){
9364         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9365         /*
9366         if(!this.mimicing){
9367             this.wrap.addClass('x-trigger-wrap-focus');
9368             this.mimicing = true;
9369             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9370             if(this.monitorTab){
9371                 this.el.on("keydown", this.checkTab, this);
9372             }
9373         }
9374         */
9375     },
9376
9377     // private
9378     checkTab : function(e){
9379         if(e.getKey() == e.TAB){
9380             this.triggerBlur();
9381         }
9382     },
9383
9384     // private
9385     onBlur : function(){
9386         // do nothing
9387     },
9388
9389     // private
9390     mimicBlur : function(e, t){
9391         /*
9392         if(!this.wrap.contains(t) && this.validateBlur()){
9393             this.triggerBlur();
9394         }
9395         */
9396     },
9397
9398     // private
9399     triggerBlur : function(){
9400         this.mimicing = false;
9401         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9402         if(this.monitorTab){
9403             this.el.un("keydown", this.checkTab, this);
9404         }
9405         //this.wrap.removeClass('x-trigger-wrap-focus');
9406         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9407     },
9408
9409     // private
9410     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9411     validateBlur : function(e, t){
9412         return true;
9413     },
9414
9415     // private
9416     onDisable : function(){
9417         this.inputEl().dom.disabled = true;
9418         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9419         //if(this.wrap){
9420         //    this.wrap.addClass('x-item-disabled');
9421         //}
9422     },
9423
9424     // private
9425     onEnable : function(){
9426         this.inputEl().dom.disabled = false;
9427         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9428         //if(this.wrap){
9429         //    this.el.removeClass('x-item-disabled');
9430         //}
9431     },
9432
9433     // private
9434     onShow : function(){
9435         var ae = this.getActionEl();
9436         
9437         if(ae){
9438             ae.dom.style.display = '';
9439             ae.dom.style.visibility = 'visible';
9440         }
9441     },
9442
9443     // private
9444     
9445     onHide : function(){
9446         var ae = this.getActionEl();
9447         ae.dom.style.display = 'none';
9448     },
9449
9450     /**
9451      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9452      * by an implementing function.
9453      * @method
9454      * @param {EventObject} e
9455      */
9456     onTriggerClick : Roo.emptyFn
9457 });
9458  /*
9459  * Based on:
9460  * Ext JS Library 1.1.1
9461  * Copyright(c) 2006-2007, Ext JS, LLC.
9462  *
9463  * Originally Released Under LGPL - original licence link has changed is not relivant.
9464  *
9465  * Fork - LGPL
9466  * <script type="text/javascript">
9467  */
9468
9469
9470 /**
9471  * @class Roo.data.SortTypes
9472  * @singleton
9473  * Defines the default sorting (casting?) comparison functions used when sorting data.
9474  */
9475 Roo.data.SortTypes = {
9476     /**
9477      * Default sort that does nothing
9478      * @param {Mixed} s The value being converted
9479      * @return {Mixed} The comparison value
9480      */
9481     none : function(s){
9482         return s;
9483     },
9484     
9485     /**
9486      * The regular expression used to strip tags
9487      * @type {RegExp}
9488      * @property
9489      */
9490     stripTagsRE : /<\/?[^>]+>/gi,
9491     
9492     /**
9493      * Strips all HTML tags to sort on text only
9494      * @param {Mixed} s The value being converted
9495      * @return {String} The comparison value
9496      */
9497     asText : function(s){
9498         return String(s).replace(this.stripTagsRE, "");
9499     },
9500     
9501     /**
9502      * Strips all HTML tags to sort on text only - Case insensitive
9503      * @param {Mixed} s The value being converted
9504      * @return {String} The comparison value
9505      */
9506     asUCText : function(s){
9507         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9508     },
9509     
9510     /**
9511      * Case insensitive string
9512      * @param {Mixed} s The value being converted
9513      * @return {String} The comparison value
9514      */
9515     asUCString : function(s) {
9516         return String(s).toUpperCase();
9517     },
9518     
9519     /**
9520      * Date sorting
9521      * @param {Mixed} s The value being converted
9522      * @return {Number} The comparison value
9523      */
9524     asDate : function(s) {
9525         if(!s){
9526             return 0;
9527         }
9528         if(s instanceof Date){
9529             return s.getTime();
9530         }
9531         return Date.parse(String(s));
9532     },
9533     
9534     /**
9535      * Float sorting
9536      * @param {Mixed} s The value being converted
9537      * @return {Float} The comparison value
9538      */
9539     asFloat : function(s) {
9540         var val = parseFloat(String(s).replace(/,/g, ""));
9541         if(isNaN(val)) {
9542             val = 0;
9543         }
9544         return val;
9545     },
9546     
9547     /**
9548      * Integer sorting
9549      * @param {Mixed} s The value being converted
9550      * @return {Number} The comparison value
9551      */
9552     asInt : function(s) {
9553         var val = parseInt(String(s).replace(/,/g, ""));
9554         if(isNaN(val)) {
9555             val = 0;
9556         }
9557         return val;
9558     }
9559 };/*
9560  * Based on:
9561  * Ext JS Library 1.1.1
9562  * Copyright(c) 2006-2007, Ext JS, LLC.
9563  *
9564  * Originally Released Under LGPL - original licence link has changed is not relivant.
9565  *
9566  * Fork - LGPL
9567  * <script type="text/javascript">
9568  */
9569
9570 /**
9571 * @class Roo.data.Record
9572  * Instances of this class encapsulate both record <em>definition</em> information, and record
9573  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9574  * to access Records cached in an {@link Roo.data.Store} object.<br>
9575  * <p>
9576  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9577  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9578  * objects.<br>
9579  * <p>
9580  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9581  * @constructor
9582  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9583  * {@link #create}. The parameters are the same.
9584  * @param {Array} data An associative Array of data values keyed by the field name.
9585  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9586  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9587  * not specified an integer id is generated.
9588  */
9589 Roo.data.Record = function(data, id){
9590     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9591     this.data = data;
9592 };
9593
9594 /**
9595  * Generate a constructor for a specific record layout.
9596  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9597  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9598  * Each field definition object may contain the following properties: <ul>
9599  * <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,
9600  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9601  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9602  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9603  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9604  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9605  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9606  * this may be omitted.</p></li>
9607  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9608  * <ul><li>auto (Default, implies no conversion)</li>
9609  * <li>string</li>
9610  * <li>int</li>
9611  * <li>float</li>
9612  * <li>boolean</li>
9613  * <li>date</li></ul></p></li>
9614  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9615  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9616  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9617  * by the Reader into an object that will be stored in the Record. It is passed the
9618  * following parameters:<ul>
9619  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9620  * </ul></p></li>
9621  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9622  * </ul>
9623  * <br>usage:<br><pre><code>
9624 var TopicRecord = Roo.data.Record.create(
9625     {name: 'title', mapping: 'topic_title'},
9626     {name: 'author', mapping: 'username'},
9627     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9628     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9629     {name: 'lastPoster', mapping: 'user2'},
9630     {name: 'excerpt', mapping: 'post_text'}
9631 );
9632
9633 var myNewRecord = new TopicRecord({
9634     title: 'Do my job please',
9635     author: 'noobie',
9636     totalPosts: 1,
9637     lastPost: new Date(),
9638     lastPoster: 'Animal',
9639     excerpt: 'No way dude!'
9640 });
9641 myStore.add(myNewRecord);
9642 </code></pre>
9643  * @method create
9644  * @static
9645  */
9646 Roo.data.Record.create = function(o){
9647     var f = function(){
9648         f.superclass.constructor.apply(this, arguments);
9649     };
9650     Roo.extend(f, Roo.data.Record);
9651     var p = f.prototype;
9652     p.fields = new Roo.util.MixedCollection(false, function(field){
9653         return field.name;
9654     });
9655     for(var i = 0, len = o.length; i < len; i++){
9656         p.fields.add(new Roo.data.Field(o[i]));
9657     }
9658     f.getField = function(name){
9659         return p.fields.get(name);  
9660     };
9661     return f;
9662 };
9663
9664 Roo.data.Record.AUTO_ID = 1000;
9665 Roo.data.Record.EDIT = 'edit';
9666 Roo.data.Record.REJECT = 'reject';
9667 Roo.data.Record.COMMIT = 'commit';
9668
9669 Roo.data.Record.prototype = {
9670     /**
9671      * Readonly flag - true if this record has been modified.
9672      * @type Boolean
9673      */
9674     dirty : false,
9675     editing : false,
9676     error: null,
9677     modified: null,
9678
9679     // private
9680     join : function(store){
9681         this.store = store;
9682     },
9683
9684     /**
9685      * Set the named field to the specified value.
9686      * @param {String} name The name of the field to set.
9687      * @param {Object} value The value to set the field to.
9688      */
9689     set : function(name, value){
9690         if(this.data[name] == value){
9691             return;
9692         }
9693         this.dirty = true;
9694         if(!this.modified){
9695             this.modified = {};
9696         }
9697         if(typeof this.modified[name] == 'undefined'){
9698             this.modified[name] = this.data[name];
9699         }
9700         this.data[name] = value;
9701         if(!this.editing && this.store){
9702             this.store.afterEdit(this);
9703         }       
9704     },
9705
9706     /**
9707      * Get the value of the named field.
9708      * @param {String} name The name of the field to get the value of.
9709      * @return {Object} The value of the field.
9710      */
9711     get : function(name){
9712         return this.data[name]; 
9713     },
9714
9715     // private
9716     beginEdit : function(){
9717         this.editing = true;
9718         this.modified = {}; 
9719     },
9720
9721     // private
9722     cancelEdit : function(){
9723         this.editing = false;
9724         delete this.modified;
9725     },
9726
9727     // private
9728     endEdit : function(){
9729         this.editing = false;
9730         if(this.dirty && this.store){
9731             this.store.afterEdit(this);
9732         }
9733     },
9734
9735     /**
9736      * Usually called by the {@link Roo.data.Store} which owns the Record.
9737      * Rejects all changes made to the Record since either creation, or the last commit operation.
9738      * Modified fields are reverted to their original values.
9739      * <p>
9740      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9741      * of reject operations.
9742      */
9743     reject : function(){
9744         var m = this.modified;
9745         for(var n in m){
9746             if(typeof m[n] != "function"){
9747                 this.data[n] = m[n];
9748             }
9749         }
9750         this.dirty = false;
9751         delete this.modified;
9752         this.editing = false;
9753         if(this.store){
9754             this.store.afterReject(this);
9755         }
9756     },
9757
9758     /**
9759      * Usually called by the {@link Roo.data.Store} which owns the Record.
9760      * Commits all changes made to the Record since either creation, or the last commit operation.
9761      * <p>
9762      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9763      * of commit operations.
9764      */
9765     commit : function(){
9766         this.dirty = false;
9767         delete this.modified;
9768         this.editing = false;
9769         if(this.store){
9770             this.store.afterCommit(this);
9771         }
9772     },
9773
9774     // private
9775     hasError : function(){
9776         return this.error != null;
9777     },
9778
9779     // private
9780     clearError : function(){
9781         this.error = null;
9782     },
9783
9784     /**
9785      * Creates a copy of this record.
9786      * @param {String} id (optional) A new record id if you don't want to use this record's id
9787      * @return {Record}
9788      */
9789     copy : function(newId) {
9790         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9791     }
9792 };/*
9793  * Based on:
9794  * Ext JS Library 1.1.1
9795  * Copyright(c) 2006-2007, Ext JS, LLC.
9796  *
9797  * Originally Released Under LGPL - original licence link has changed is not relivant.
9798  *
9799  * Fork - LGPL
9800  * <script type="text/javascript">
9801  */
9802
9803
9804
9805 /**
9806  * @class Roo.data.Store
9807  * @extends Roo.util.Observable
9808  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9809  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9810  * <p>
9811  * 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
9812  * has no knowledge of the format of the data returned by the Proxy.<br>
9813  * <p>
9814  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9815  * instances from the data object. These records are cached and made available through accessor functions.
9816  * @constructor
9817  * Creates a new Store.
9818  * @param {Object} config A config object containing the objects needed for the Store to access data,
9819  * and read the data into Records.
9820  */
9821 Roo.data.Store = function(config){
9822     this.data = new Roo.util.MixedCollection(false);
9823     this.data.getKey = function(o){
9824         return o.id;
9825     };
9826     this.baseParams = {};
9827     // private
9828     this.paramNames = {
9829         "start" : "start",
9830         "limit" : "limit",
9831         "sort" : "sort",
9832         "dir" : "dir",
9833         "multisort" : "_multisort"
9834     };
9835
9836     if(config && config.data){
9837         this.inlineData = config.data;
9838         delete config.data;
9839     }
9840
9841     Roo.apply(this, config);
9842     
9843     if(this.reader){ // reader passed
9844         this.reader = Roo.factory(this.reader, Roo.data);
9845         this.reader.xmodule = this.xmodule || false;
9846         if(!this.recordType){
9847             this.recordType = this.reader.recordType;
9848         }
9849         if(this.reader.onMetaChange){
9850             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9851         }
9852     }
9853
9854     if(this.recordType){
9855         this.fields = this.recordType.prototype.fields;
9856     }
9857     this.modified = [];
9858
9859     this.addEvents({
9860         /**
9861          * @event datachanged
9862          * Fires when the data cache has changed, and a widget which is using this Store
9863          * as a Record cache should refresh its view.
9864          * @param {Store} this
9865          */
9866         datachanged : true,
9867         /**
9868          * @event metachange
9869          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9870          * @param {Store} this
9871          * @param {Object} meta The JSON metadata
9872          */
9873         metachange : true,
9874         /**
9875          * @event add
9876          * Fires when Records have been added to the Store
9877          * @param {Store} this
9878          * @param {Roo.data.Record[]} records The array of Records added
9879          * @param {Number} index The index at which the record(s) were added
9880          */
9881         add : true,
9882         /**
9883          * @event remove
9884          * Fires when a Record has been removed from the Store
9885          * @param {Store} this
9886          * @param {Roo.data.Record} record The Record that was removed
9887          * @param {Number} index The index at which the record was removed
9888          */
9889         remove : true,
9890         /**
9891          * @event update
9892          * Fires when a Record has been updated
9893          * @param {Store} this
9894          * @param {Roo.data.Record} record The Record that was updated
9895          * @param {String} operation The update operation being performed.  Value may be one of:
9896          * <pre><code>
9897  Roo.data.Record.EDIT
9898  Roo.data.Record.REJECT
9899  Roo.data.Record.COMMIT
9900          * </code></pre>
9901          */
9902         update : true,
9903         /**
9904          * @event clear
9905          * Fires when the data cache has been cleared.
9906          * @param {Store} this
9907          */
9908         clear : true,
9909         /**
9910          * @event beforeload
9911          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9912          * the load action will be canceled.
9913          * @param {Store} this
9914          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9915          */
9916         beforeload : true,
9917         /**
9918          * @event beforeloadadd
9919          * Fires after a new set of Records has been loaded.
9920          * @param {Store} this
9921          * @param {Roo.data.Record[]} records The Records that were loaded
9922          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9923          */
9924         beforeloadadd : true,
9925         /**
9926          * @event load
9927          * Fires after a new set of Records has been loaded, before they are added to the store.
9928          * @param {Store} this
9929          * @param {Roo.data.Record[]} records The Records that were loaded
9930          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9931          * @params {Object} return from reader
9932          */
9933         load : true,
9934         /**
9935          * @event loadexception
9936          * Fires if an exception occurs in the Proxy during loading.
9937          * Called with the signature of the Proxy's "loadexception" event.
9938          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9939          * 
9940          * @param {Proxy} 
9941          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9942          * @param {Object} load options 
9943          * @param {Object} jsonData from your request (normally this contains the Exception)
9944          */
9945         loadexception : true
9946     });
9947     
9948     if(this.proxy){
9949         this.proxy = Roo.factory(this.proxy, Roo.data);
9950         this.proxy.xmodule = this.xmodule || false;
9951         this.relayEvents(this.proxy,  ["loadexception"]);
9952     }
9953     this.sortToggle = {};
9954     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9955
9956     Roo.data.Store.superclass.constructor.call(this);
9957
9958     if(this.inlineData){
9959         this.loadData(this.inlineData);
9960         delete this.inlineData;
9961     }
9962 };
9963
9964 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9965      /**
9966     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9967     * without a remote query - used by combo/forms at present.
9968     */
9969     
9970     /**
9971     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9972     */
9973     /**
9974     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9975     */
9976     /**
9977     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9978     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9979     */
9980     /**
9981     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9982     * on any HTTP request
9983     */
9984     /**
9985     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9986     */
9987     /**
9988     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9989     */
9990     multiSort: false,
9991     /**
9992     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9993     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9994     */
9995     remoteSort : false,
9996
9997     /**
9998     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9999      * loaded or when a record is removed. (defaults to false).
10000     */
10001     pruneModifiedRecords : false,
10002
10003     // private
10004     lastOptions : null,
10005
10006     /**
10007      * Add Records to the Store and fires the add event.
10008      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10009      */
10010     add : function(records){
10011         records = [].concat(records);
10012         for(var i = 0, len = records.length; i < len; i++){
10013             records[i].join(this);
10014         }
10015         var index = this.data.length;
10016         this.data.addAll(records);
10017         this.fireEvent("add", this, records, index);
10018     },
10019
10020     /**
10021      * Remove a Record from the Store and fires the remove event.
10022      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10023      */
10024     remove : function(record){
10025         var index = this.data.indexOf(record);
10026         this.data.removeAt(index);
10027         if(this.pruneModifiedRecords){
10028             this.modified.remove(record);
10029         }
10030         this.fireEvent("remove", this, record, index);
10031     },
10032
10033     /**
10034      * Remove all Records from the Store and fires the clear event.
10035      */
10036     removeAll : function(){
10037         this.data.clear();
10038         if(this.pruneModifiedRecords){
10039             this.modified = [];
10040         }
10041         this.fireEvent("clear", this);
10042     },
10043
10044     /**
10045      * Inserts Records to the Store at the given index and fires the add event.
10046      * @param {Number} index The start index at which to insert the passed Records.
10047      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10048      */
10049     insert : function(index, records){
10050         records = [].concat(records);
10051         for(var i = 0, len = records.length; i < len; i++){
10052             this.data.insert(index, records[i]);
10053             records[i].join(this);
10054         }
10055         this.fireEvent("add", this, records, index);
10056     },
10057
10058     /**
10059      * Get the index within the cache of the passed Record.
10060      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10061      * @return {Number} The index of the passed Record. Returns -1 if not found.
10062      */
10063     indexOf : function(record){
10064         return this.data.indexOf(record);
10065     },
10066
10067     /**
10068      * Get the index within the cache of the Record with the passed id.
10069      * @param {String} id The id of the Record to find.
10070      * @return {Number} The index of the Record. Returns -1 if not found.
10071      */
10072     indexOfId : function(id){
10073         return this.data.indexOfKey(id);
10074     },
10075
10076     /**
10077      * Get the Record with the specified id.
10078      * @param {String} id The id of the Record to find.
10079      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10080      */
10081     getById : function(id){
10082         return this.data.key(id);
10083     },
10084
10085     /**
10086      * Get the Record at the specified index.
10087      * @param {Number} index The index of the Record to find.
10088      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10089      */
10090     getAt : function(index){
10091         return this.data.itemAt(index);
10092     },
10093
10094     /**
10095      * Returns a range of Records between specified indices.
10096      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10097      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10098      * @return {Roo.data.Record[]} An array of Records
10099      */
10100     getRange : function(start, end){
10101         return this.data.getRange(start, end);
10102     },
10103
10104     // private
10105     storeOptions : function(o){
10106         o = Roo.apply({}, o);
10107         delete o.callback;
10108         delete o.scope;
10109         this.lastOptions = o;
10110     },
10111
10112     /**
10113      * Loads the Record cache from the configured Proxy using the configured Reader.
10114      * <p>
10115      * If using remote paging, then the first load call must specify the <em>start</em>
10116      * and <em>limit</em> properties in the options.params property to establish the initial
10117      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10118      * <p>
10119      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10120      * and this call will return before the new data has been loaded. Perform any post-processing
10121      * in a callback function, or in a "load" event handler.</strong>
10122      * <p>
10123      * @param {Object} options An object containing properties which control loading options:<ul>
10124      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10125      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10126      * passed the following arguments:<ul>
10127      * <li>r : Roo.data.Record[]</li>
10128      * <li>options: Options object from the load call</li>
10129      * <li>success: Boolean success indicator</li></ul></li>
10130      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10131      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10132      * </ul>
10133      */
10134     load : function(options){
10135         options = options || {};
10136         if(this.fireEvent("beforeload", this, options) !== false){
10137             this.storeOptions(options);
10138             var p = Roo.apply(options.params || {}, this.baseParams);
10139             // if meta was not loaded from remote source.. try requesting it.
10140             if (!this.reader.metaFromRemote) {
10141                 p._requestMeta = 1;
10142             }
10143             if(this.sortInfo && this.remoteSort){
10144                 var pn = this.paramNames;
10145                 p[pn["sort"]] = this.sortInfo.field;
10146                 p[pn["dir"]] = this.sortInfo.direction;
10147             }
10148             if (this.multiSort) {
10149                 var pn = this.paramNames;
10150                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10151             }
10152             
10153             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10154         }
10155     },
10156
10157     /**
10158      * Reloads the Record cache from the configured Proxy using the configured Reader and
10159      * the options from the last load operation performed.
10160      * @param {Object} options (optional) An object containing properties which may override the options
10161      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10162      * the most recently used options are reused).
10163      */
10164     reload : function(options){
10165         this.load(Roo.applyIf(options||{}, this.lastOptions));
10166     },
10167
10168     // private
10169     // Called as a callback by the Reader during a load operation.
10170     loadRecords : function(o, options, success){
10171         if(!o || success === false){
10172             if(success !== false){
10173                 this.fireEvent("load", this, [], options, o);
10174             }
10175             if(options.callback){
10176                 options.callback.call(options.scope || this, [], options, false);
10177             }
10178             return;
10179         }
10180         // if data returned failure - throw an exception.
10181         if (o.success === false) {
10182             // show a message if no listener is registered.
10183             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10184                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10185             }
10186             // loadmask wil be hooked into this..
10187             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10188             return;
10189         }
10190         var r = o.records, t = o.totalRecords || r.length;
10191         
10192         this.fireEvent("beforeloadadd", this, r, options, o);
10193         
10194         if(!options || options.add !== true){
10195             if(this.pruneModifiedRecords){
10196                 this.modified = [];
10197             }
10198             for(var i = 0, len = r.length; i < len; i++){
10199                 r[i].join(this);
10200             }
10201             if(this.snapshot){
10202                 this.data = this.snapshot;
10203                 delete this.snapshot;
10204             }
10205             this.data.clear();
10206             this.data.addAll(r);
10207             this.totalLength = t;
10208             this.applySort();
10209             this.fireEvent("datachanged", this);
10210         }else{
10211             this.totalLength = Math.max(t, this.data.length+r.length);
10212             this.add(r);
10213         }
10214         this.fireEvent("load", this, r, options, o);
10215         if(options.callback){
10216             options.callback.call(options.scope || this, r, options, true);
10217         }
10218     },
10219
10220
10221     /**
10222      * Loads data from a passed data block. A Reader which understands the format of the data
10223      * must have been configured in the constructor.
10224      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10225      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10226      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10227      */
10228     loadData : function(o, append){
10229         var r = this.reader.readRecords(o);
10230         this.loadRecords(r, {add: append}, true);
10231     },
10232
10233     /**
10234      * Gets the number of cached records.
10235      * <p>
10236      * <em>If using paging, this may not be the total size of the dataset. If the data object
10237      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10238      * the data set size</em>
10239      */
10240     getCount : function(){
10241         return this.data.length || 0;
10242     },
10243
10244     /**
10245      * Gets the total number of records in the dataset as returned by the server.
10246      * <p>
10247      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10248      * the dataset size</em>
10249      */
10250     getTotalCount : function(){
10251         return this.totalLength || 0;
10252     },
10253
10254     /**
10255      * Returns the sort state of the Store as an object with two properties:
10256      * <pre><code>
10257  field {String} The name of the field by which the Records are sorted
10258  direction {String} The sort order, "ASC" or "DESC"
10259      * </code></pre>
10260      */
10261     getSortState : function(){
10262         return this.sortInfo;
10263     },
10264
10265     // private
10266     applySort : function(){
10267         if(this.sortInfo && !this.remoteSort){
10268             var s = this.sortInfo, f = s.field;
10269             var st = this.fields.get(f).sortType;
10270             var fn = function(r1, r2){
10271                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10272                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10273             };
10274             this.data.sort(s.direction, fn);
10275             if(this.snapshot && this.snapshot != this.data){
10276                 this.snapshot.sort(s.direction, fn);
10277             }
10278         }
10279     },
10280
10281     /**
10282      * Sets the default sort column and order to be used by the next load operation.
10283      * @param {String} fieldName The name of the field to sort by.
10284      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10285      */
10286     setDefaultSort : function(field, dir){
10287         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10288     },
10289
10290     /**
10291      * Sort the Records.
10292      * If remote sorting is used, the sort is performed on the server, and the cache is
10293      * reloaded. If local sorting is used, the cache is sorted internally.
10294      * @param {String} fieldName The name of the field to sort by.
10295      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10296      */
10297     sort : function(fieldName, dir){
10298         var f = this.fields.get(fieldName);
10299         if(!dir){
10300             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10301             
10302             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10303                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10304             }else{
10305                 dir = f.sortDir;
10306             }
10307         }
10308         this.sortToggle[f.name] = dir;
10309         this.sortInfo = {field: f.name, direction: dir};
10310         if(!this.remoteSort){
10311             this.applySort();
10312             this.fireEvent("datachanged", this);
10313         }else{
10314             this.load(this.lastOptions);
10315         }
10316     },
10317
10318     /**
10319      * Calls the specified function for each of the Records in the cache.
10320      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10321      * Returning <em>false</em> aborts and exits the iteration.
10322      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10323      */
10324     each : function(fn, scope){
10325         this.data.each(fn, scope);
10326     },
10327
10328     /**
10329      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10330      * (e.g., during paging).
10331      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10332      */
10333     getModifiedRecords : function(){
10334         return this.modified;
10335     },
10336
10337     // private
10338     createFilterFn : function(property, value, anyMatch){
10339         if(!value.exec){ // not a regex
10340             value = String(value);
10341             if(value.length == 0){
10342                 return false;
10343             }
10344             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10345         }
10346         return function(r){
10347             return value.test(r.data[property]);
10348         };
10349     },
10350
10351     /**
10352      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10353      * @param {String} property A field on your records
10354      * @param {Number} start The record index to start at (defaults to 0)
10355      * @param {Number} end The last record index to include (defaults to length - 1)
10356      * @return {Number} The sum
10357      */
10358     sum : function(property, start, end){
10359         var rs = this.data.items, v = 0;
10360         start = start || 0;
10361         end = (end || end === 0) ? end : rs.length-1;
10362
10363         for(var i = start; i <= end; i++){
10364             v += (rs[i].data[property] || 0);
10365         }
10366         return v;
10367     },
10368
10369     /**
10370      * Filter the records by a specified property.
10371      * @param {String} field A field on your records
10372      * @param {String/RegExp} value Either a string that the field
10373      * should start with or a RegExp to test against the field
10374      * @param {Boolean} anyMatch True to match any part not just the beginning
10375      */
10376     filter : function(property, value, anyMatch){
10377         var fn = this.createFilterFn(property, value, anyMatch);
10378         return fn ? this.filterBy(fn) : this.clearFilter();
10379     },
10380
10381     /**
10382      * Filter by a function. The specified function will be called with each
10383      * record in this data source. If the function returns true the record is included,
10384      * otherwise it is filtered.
10385      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10386      * @param {Object} scope (optional) The scope of the function (defaults to this)
10387      */
10388     filterBy : function(fn, scope){
10389         this.snapshot = this.snapshot || this.data;
10390         this.data = this.queryBy(fn, scope||this);
10391         this.fireEvent("datachanged", this);
10392     },
10393
10394     /**
10395      * Query the records by a specified property.
10396      * @param {String} field A field on your records
10397      * @param {String/RegExp} value Either a string that the field
10398      * should start with or a RegExp to test against the field
10399      * @param {Boolean} anyMatch True to match any part not just the beginning
10400      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10401      */
10402     query : function(property, value, anyMatch){
10403         var fn = this.createFilterFn(property, value, anyMatch);
10404         return fn ? this.queryBy(fn) : this.data.clone();
10405     },
10406
10407     /**
10408      * Query by a function. The specified function will be called with each
10409      * record in this data source. If the function returns true the record is included
10410      * in the results.
10411      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10412      * @param {Object} scope (optional) The scope of the function (defaults to this)
10413       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10414      **/
10415     queryBy : function(fn, scope){
10416         var data = this.snapshot || this.data;
10417         return data.filterBy(fn, scope||this);
10418     },
10419
10420     /**
10421      * Collects unique values for a particular dataIndex from this store.
10422      * @param {String} dataIndex The property to collect
10423      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10424      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10425      * @return {Array} An array of the unique values
10426      **/
10427     collect : function(dataIndex, allowNull, bypassFilter){
10428         var d = (bypassFilter === true && this.snapshot) ?
10429                 this.snapshot.items : this.data.items;
10430         var v, sv, r = [], l = {};
10431         for(var i = 0, len = d.length; i < len; i++){
10432             v = d[i].data[dataIndex];
10433             sv = String(v);
10434             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10435                 l[sv] = true;
10436                 r[r.length] = v;
10437             }
10438         }
10439         return r;
10440     },
10441
10442     /**
10443      * Revert to a view of the Record cache with no filtering applied.
10444      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10445      */
10446     clearFilter : function(suppressEvent){
10447         if(this.snapshot && this.snapshot != this.data){
10448             this.data = this.snapshot;
10449             delete this.snapshot;
10450             if(suppressEvent !== true){
10451                 this.fireEvent("datachanged", this);
10452             }
10453         }
10454     },
10455
10456     // private
10457     afterEdit : function(record){
10458         if(this.modified.indexOf(record) == -1){
10459             this.modified.push(record);
10460         }
10461         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10462     },
10463     
10464     // private
10465     afterReject : function(record){
10466         this.modified.remove(record);
10467         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10468     },
10469
10470     // private
10471     afterCommit : function(record){
10472         this.modified.remove(record);
10473         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10474     },
10475
10476     /**
10477      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10478      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10479      */
10480     commitChanges : function(){
10481         var m = this.modified.slice(0);
10482         this.modified = [];
10483         for(var i = 0, len = m.length; i < len; i++){
10484             m[i].commit();
10485         }
10486     },
10487
10488     /**
10489      * Cancel outstanding changes on all changed records.
10490      */
10491     rejectChanges : function(){
10492         var m = this.modified.slice(0);
10493         this.modified = [];
10494         for(var i = 0, len = m.length; i < len; i++){
10495             m[i].reject();
10496         }
10497     },
10498
10499     onMetaChange : function(meta, rtype, o){
10500         this.recordType = rtype;
10501         this.fields = rtype.prototype.fields;
10502         delete this.snapshot;
10503         this.sortInfo = meta.sortInfo || this.sortInfo;
10504         this.modified = [];
10505         this.fireEvent('metachange', this, this.reader.meta);
10506     },
10507     
10508     moveIndex : function(data, type)
10509     {
10510         var index = this.indexOf(data);
10511         
10512         var newIndex = index + type;
10513         
10514         this.remove(data);
10515         
10516         this.insert(newIndex, data);
10517         
10518     }
10519 });/*
10520  * Based on:
10521  * Ext JS Library 1.1.1
10522  * Copyright(c) 2006-2007, Ext JS, LLC.
10523  *
10524  * Originally Released Under LGPL - original licence link has changed is not relivant.
10525  *
10526  * Fork - LGPL
10527  * <script type="text/javascript">
10528  */
10529
10530 /**
10531  * @class Roo.data.SimpleStore
10532  * @extends Roo.data.Store
10533  * Small helper class to make creating Stores from Array data easier.
10534  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10535  * @cfg {Array} fields An array of field definition objects, or field name strings.
10536  * @cfg {Array} data The multi-dimensional array of data
10537  * @constructor
10538  * @param {Object} config
10539  */
10540 Roo.data.SimpleStore = function(config){
10541     Roo.data.SimpleStore.superclass.constructor.call(this, {
10542         isLocal : true,
10543         reader: new Roo.data.ArrayReader({
10544                 id: config.id
10545             },
10546             Roo.data.Record.create(config.fields)
10547         ),
10548         proxy : new Roo.data.MemoryProxy(config.data)
10549     });
10550     this.load();
10551 };
10552 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10553  * Based on:
10554  * Ext JS Library 1.1.1
10555  * Copyright(c) 2006-2007, Ext JS, LLC.
10556  *
10557  * Originally Released Under LGPL - original licence link has changed is not relivant.
10558  *
10559  * Fork - LGPL
10560  * <script type="text/javascript">
10561  */
10562
10563 /**
10564 /**
10565  * @extends Roo.data.Store
10566  * @class Roo.data.JsonStore
10567  * Small helper class to make creating Stores for JSON data easier. <br/>
10568 <pre><code>
10569 var store = new Roo.data.JsonStore({
10570     url: 'get-images.php',
10571     root: 'images',
10572     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10573 });
10574 </code></pre>
10575  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10576  * JsonReader and HttpProxy (unless inline data is provided).</b>
10577  * @cfg {Array} fields An array of field definition objects, or field name strings.
10578  * @constructor
10579  * @param {Object} config
10580  */
10581 Roo.data.JsonStore = function(c){
10582     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10583         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10584         reader: new Roo.data.JsonReader(c, c.fields)
10585     }));
10586 };
10587 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10588  * Based on:
10589  * Ext JS Library 1.1.1
10590  * Copyright(c) 2006-2007, Ext JS, LLC.
10591  *
10592  * Originally Released Under LGPL - original licence link has changed is not relivant.
10593  *
10594  * Fork - LGPL
10595  * <script type="text/javascript">
10596  */
10597
10598  
10599 Roo.data.Field = function(config){
10600     if(typeof config == "string"){
10601         config = {name: config};
10602     }
10603     Roo.apply(this, config);
10604     
10605     if(!this.type){
10606         this.type = "auto";
10607     }
10608     
10609     var st = Roo.data.SortTypes;
10610     // named sortTypes are supported, here we look them up
10611     if(typeof this.sortType == "string"){
10612         this.sortType = st[this.sortType];
10613     }
10614     
10615     // set default sortType for strings and dates
10616     if(!this.sortType){
10617         switch(this.type){
10618             case "string":
10619                 this.sortType = st.asUCString;
10620                 break;
10621             case "date":
10622                 this.sortType = st.asDate;
10623                 break;
10624             default:
10625                 this.sortType = st.none;
10626         }
10627     }
10628
10629     // define once
10630     var stripRe = /[\$,%]/g;
10631
10632     // prebuilt conversion function for this field, instead of
10633     // switching every time we're reading a value
10634     if(!this.convert){
10635         var cv, dateFormat = this.dateFormat;
10636         switch(this.type){
10637             case "":
10638             case "auto":
10639             case undefined:
10640                 cv = function(v){ return v; };
10641                 break;
10642             case "string":
10643                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10644                 break;
10645             case "int":
10646                 cv = function(v){
10647                     return v !== undefined && v !== null && v !== '' ?
10648                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10649                     };
10650                 break;
10651             case "float":
10652                 cv = function(v){
10653                     return v !== undefined && v !== null && v !== '' ?
10654                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10655                     };
10656                 break;
10657             case "bool":
10658             case "boolean":
10659                 cv = function(v){ return v === true || v === "true" || v == 1; };
10660                 break;
10661             case "date":
10662                 cv = function(v){
10663                     if(!v){
10664                         return '';
10665                     }
10666                     if(v instanceof Date){
10667                         return v;
10668                     }
10669                     if(dateFormat){
10670                         if(dateFormat == "timestamp"){
10671                             return new Date(v*1000);
10672                         }
10673                         return Date.parseDate(v, dateFormat);
10674                     }
10675                     var parsed = Date.parse(v);
10676                     return parsed ? new Date(parsed) : null;
10677                 };
10678              break;
10679             
10680         }
10681         this.convert = cv;
10682     }
10683 };
10684
10685 Roo.data.Field.prototype = {
10686     dateFormat: null,
10687     defaultValue: "",
10688     mapping: null,
10689     sortType : null,
10690     sortDir : "ASC"
10691 };/*
10692  * Based on:
10693  * Ext JS Library 1.1.1
10694  * Copyright(c) 2006-2007, Ext JS, LLC.
10695  *
10696  * Originally Released Under LGPL - original licence link has changed is not relivant.
10697  *
10698  * Fork - LGPL
10699  * <script type="text/javascript">
10700  */
10701  
10702 // Base class for reading structured data from a data source.  This class is intended to be
10703 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10704
10705 /**
10706  * @class Roo.data.DataReader
10707  * Base class for reading structured data from a data source.  This class is intended to be
10708  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10709  */
10710
10711 Roo.data.DataReader = function(meta, recordType){
10712     
10713     this.meta = meta;
10714     
10715     this.recordType = recordType instanceof Array ? 
10716         Roo.data.Record.create(recordType) : recordType;
10717 };
10718
10719 Roo.data.DataReader.prototype = {
10720      /**
10721      * Create an empty record
10722      * @param {Object} data (optional) - overlay some values
10723      * @return {Roo.data.Record} record created.
10724      */
10725     newRow :  function(d) {
10726         var da =  {};
10727         this.recordType.prototype.fields.each(function(c) {
10728             switch( c.type) {
10729                 case 'int' : da[c.name] = 0; break;
10730                 case 'date' : da[c.name] = new Date(); break;
10731                 case 'float' : da[c.name] = 0.0; break;
10732                 case 'boolean' : da[c.name] = false; break;
10733                 default : da[c.name] = ""; break;
10734             }
10735             
10736         });
10737         return new this.recordType(Roo.apply(da, d));
10738     }
10739     
10740 };/*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751 /**
10752  * @class Roo.data.DataProxy
10753  * @extends Roo.data.Observable
10754  * This class is an abstract base class for implementations which provide retrieval of
10755  * unformatted data objects.<br>
10756  * <p>
10757  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10758  * (of the appropriate type which knows how to parse the data object) to provide a block of
10759  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10760  * <p>
10761  * Custom implementations must implement the load method as described in
10762  * {@link Roo.data.HttpProxy#load}.
10763  */
10764 Roo.data.DataProxy = function(){
10765     this.addEvents({
10766         /**
10767          * @event beforeload
10768          * Fires before a network request is made to retrieve a data object.
10769          * @param {Object} This DataProxy object.
10770          * @param {Object} params The params parameter to the load function.
10771          */
10772         beforeload : true,
10773         /**
10774          * @event load
10775          * Fires before the load method's callback is called.
10776          * @param {Object} This DataProxy object.
10777          * @param {Object} o The data object.
10778          * @param {Object} arg The callback argument object passed to the load function.
10779          */
10780         load : true,
10781         /**
10782          * @event loadexception
10783          * Fires if an Exception occurs during data retrieval.
10784          * @param {Object} This DataProxy object.
10785          * @param {Object} o The data object.
10786          * @param {Object} arg The callback argument object passed to the load function.
10787          * @param {Object} e The Exception.
10788          */
10789         loadexception : true
10790     });
10791     Roo.data.DataProxy.superclass.constructor.call(this);
10792 };
10793
10794 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10795
10796     /**
10797      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10798      */
10799 /*
10800  * Based on:
10801  * Ext JS Library 1.1.1
10802  * Copyright(c) 2006-2007, Ext JS, LLC.
10803  *
10804  * Originally Released Under LGPL - original licence link has changed is not relivant.
10805  *
10806  * Fork - LGPL
10807  * <script type="text/javascript">
10808  */
10809 /**
10810  * @class Roo.data.MemoryProxy
10811  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10812  * to the Reader when its load method is called.
10813  * @constructor
10814  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10815  */
10816 Roo.data.MemoryProxy = function(data){
10817     if (data.data) {
10818         data = data.data;
10819     }
10820     Roo.data.MemoryProxy.superclass.constructor.call(this);
10821     this.data = data;
10822 };
10823
10824 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10825     /**
10826      * Load data from the requested source (in this case an in-memory
10827      * data object passed to the constructor), read the data object into
10828      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10829      * process that block using the passed callback.
10830      * @param {Object} params This parameter is not used by the MemoryProxy class.
10831      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10832      * object into a block of Roo.data.Records.
10833      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10834      * The function must be passed <ul>
10835      * <li>The Record block object</li>
10836      * <li>The "arg" argument from the load function</li>
10837      * <li>A boolean success indicator</li>
10838      * </ul>
10839      * @param {Object} scope The scope in which to call the callback
10840      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10841      */
10842     load : function(params, reader, callback, scope, arg){
10843         params = params || {};
10844         var result;
10845         try {
10846             result = reader.readRecords(this.data);
10847         }catch(e){
10848             this.fireEvent("loadexception", this, arg, null, e);
10849             callback.call(scope, null, arg, false);
10850             return;
10851         }
10852         callback.call(scope, result, arg, true);
10853     },
10854     
10855     // private
10856     update : function(params, records){
10857         
10858     }
10859 });/*
10860  * Based on:
10861  * Ext JS Library 1.1.1
10862  * Copyright(c) 2006-2007, Ext JS, LLC.
10863  *
10864  * Originally Released Under LGPL - original licence link has changed is not relivant.
10865  *
10866  * Fork - LGPL
10867  * <script type="text/javascript">
10868  */
10869 /**
10870  * @class Roo.data.HttpProxy
10871  * @extends Roo.data.DataProxy
10872  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10873  * configured to reference a certain URL.<br><br>
10874  * <p>
10875  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10876  * from which the running page was served.<br><br>
10877  * <p>
10878  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10879  * <p>
10880  * Be aware that to enable the browser to parse an XML document, the server must set
10881  * the Content-Type header in the HTTP response to "text/xml".
10882  * @constructor
10883  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10884  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10885  * will be used to make the request.
10886  */
10887 Roo.data.HttpProxy = function(conn){
10888     Roo.data.HttpProxy.superclass.constructor.call(this);
10889     // is conn a conn config or a real conn?
10890     this.conn = conn;
10891     this.useAjax = !conn || !conn.events;
10892   
10893 };
10894
10895 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10896     // thse are take from connection...
10897     
10898     /**
10899      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10900      */
10901     /**
10902      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10903      * extra parameters to each request made by this object. (defaults to undefined)
10904      */
10905     /**
10906      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10907      *  to each request made by this object. (defaults to undefined)
10908      */
10909     /**
10910      * @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)
10911      */
10912     /**
10913      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10914      */
10915      /**
10916      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10917      * @type Boolean
10918      */
10919   
10920
10921     /**
10922      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10923      * @type Boolean
10924      */
10925     /**
10926      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10927      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10928      * a finer-grained basis than the DataProxy events.
10929      */
10930     getConnection : function(){
10931         return this.useAjax ? Roo.Ajax : this.conn;
10932     },
10933
10934     /**
10935      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10936      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10937      * process that block using the passed callback.
10938      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10939      * for the request to the remote server.
10940      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10941      * object into a block of Roo.data.Records.
10942      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10943      * The function must be passed <ul>
10944      * <li>The Record block object</li>
10945      * <li>The "arg" argument from the load function</li>
10946      * <li>A boolean success indicator</li>
10947      * </ul>
10948      * @param {Object} scope The scope in which to call the callback
10949      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10950      */
10951     load : function(params, reader, callback, scope, arg){
10952         if(this.fireEvent("beforeload", this, params) !== false){
10953             var  o = {
10954                 params : params || {},
10955                 request: {
10956                     callback : callback,
10957                     scope : scope,
10958                     arg : arg
10959                 },
10960                 reader: reader,
10961                 callback : this.loadResponse,
10962                 scope: this
10963             };
10964             if(this.useAjax){
10965                 Roo.applyIf(o, this.conn);
10966                 if(this.activeRequest){
10967                     Roo.Ajax.abort(this.activeRequest);
10968                 }
10969                 this.activeRequest = Roo.Ajax.request(o);
10970             }else{
10971                 this.conn.request(o);
10972             }
10973         }else{
10974             callback.call(scope||this, null, arg, false);
10975         }
10976     },
10977
10978     // private
10979     loadResponse : function(o, success, response){
10980         delete this.activeRequest;
10981         if(!success){
10982             this.fireEvent("loadexception", this, o, response);
10983             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10984             return;
10985         }
10986         var result;
10987         try {
10988             result = o.reader.read(response);
10989         }catch(e){
10990             this.fireEvent("loadexception", this, o, response, e);
10991             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10992             return;
10993         }
10994         
10995         this.fireEvent("load", this, o, o.request.arg);
10996         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10997     },
10998
10999     // private
11000     update : function(dataSet){
11001
11002     },
11003
11004     // private
11005     updateResponse : function(dataSet){
11006
11007     }
11008 });/*
11009  * Based on:
11010  * Ext JS Library 1.1.1
11011  * Copyright(c) 2006-2007, Ext JS, LLC.
11012  *
11013  * Originally Released Under LGPL - original licence link has changed is not relivant.
11014  *
11015  * Fork - LGPL
11016  * <script type="text/javascript">
11017  */
11018
11019 /**
11020  * @class Roo.data.ScriptTagProxy
11021  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11022  * other than the originating domain of the running page.<br><br>
11023  * <p>
11024  * <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
11025  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11026  * <p>
11027  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11028  * source code that is used as the source inside a &lt;script> tag.<br><br>
11029  * <p>
11030  * In order for the browser to process the returned data, the server must wrap the data object
11031  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11032  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11033  * depending on whether the callback name was passed:
11034  * <p>
11035  * <pre><code>
11036 boolean scriptTag = false;
11037 String cb = request.getParameter("callback");
11038 if (cb != null) {
11039     scriptTag = true;
11040     response.setContentType("text/javascript");
11041 } else {
11042     response.setContentType("application/x-json");
11043 }
11044 Writer out = response.getWriter();
11045 if (scriptTag) {
11046     out.write(cb + "(");
11047 }
11048 out.print(dataBlock.toJsonString());
11049 if (scriptTag) {
11050     out.write(");");
11051 }
11052 </pre></code>
11053  *
11054  * @constructor
11055  * @param {Object} config A configuration object.
11056  */
11057 Roo.data.ScriptTagProxy = function(config){
11058     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11059     Roo.apply(this, config);
11060     this.head = document.getElementsByTagName("head")[0];
11061 };
11062
11063 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11064
11065 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11066     /**
11067      * @cfg {String} url The URL from which to request the data object.
11068      */
11069     /**
11070      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11071      */
11072     timeout : 30000,
11073     /**
11074      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11075      * the server the name of the callback function set up by the load call to process the returned data object.
11076      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11077      * javascript output which calls this named function passing the data object as its only parameter.
11078      */
11079     callbackParam : "callback",
11080     /**
11081      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11082      * name to the request.
11083      */
11084     nocache : true,
11085
11086     /**
11087      * Load data from the configured URL, read the data object into
11088      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11089      * process that block using the passed callback.
11090      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11091      * for the request to the remote server.
11092      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11093      * object into a block of Roo.data.Records.
11094      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11095      * The function must be passed <ul>
11096      * <li>The Record block object</li>
11097      * <li>The "arg" argument from the load function</li>
11098      * <li>A boolean success indicator</li>
11099      * </ul>
11100      * @param {Object} scope The scope in which to call the callback
11101      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11102      */
11103     load : function(params, reader, callback, scope, arg){
11104         if(this.fireEvent("beforeload", this, params) !== false){
11105
11106             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11107
11108             var url = this.url;
11109             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11110             if(this.nocache){
11111                 url += "&_dc=" + (new Date().getTime());
11112             }
11113             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11114             var trans = {
11115                 id : transId,
11116                 cb : "stcCallback"+transId,
11117                 scriptId : "stcScript"+transId,
11118                 params : params,
11119                 arg : arg,
11120                 url : url,
11121                 callback : callback,
11122                 scope : scope,
11123                 reader : reader
11124             };
11125             var conn = this;
11126
11127             window[trans.cb] = function(o){
11128                 conn.handleResponse(o, trans);
11129             };
11130
11131             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11132
11133             if(this.autoAbort !== false){
11134                 this.abort();
11135             }
11136
11137             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11138
11139             var script = document.createElement("script");
11140             script.setAttribute("src", url);
11141             script.setAttribute("type", "text/javascript");
11142             script.setAttribute("id", trans.scriptId);
11143             this.head.appendChild(script);
11144
11145             this.trans = trans;
11146         }else{
11147             callback.call(scope||this, null, arg, false);
11148         }
11149     },
11150
11151     // private
11152     isLoading : function(){
11153         return this.trans ? true : false;
11154     },
11155
11156     /**
11157      * Abort the current server request.
11158      */
11159     abort : function(){
11160         if(this.isLoading()){
11161             this.destroyTrans(this.trans);
11162         }
11163     },
11164
11165     // private
11166     destroyTrans : function(trans, isLoaded){
11167         this.head.removeChild(document.getElementById(trans.scriptId));
11168         clearTimeout(trans.timeoutId);
11169         if(isLoaded){
11170             window[trans.cb] = undefined;
11171             try{
11172                 delete window[trans.cb];
11173             }catch(e){}
11174         }else{
11175             // if hasn't been loaded, wait for load to remove it to prevent script error
11176             window[trans.cb] = function(){
11177                 window[trans.cb] = undefined;
11178                 try{
11179                     delete window[trans.cb];
11180                 }catch(e){}
11181             };
11182         }
11183     },
11184
11185     // private
11186     handleResponse : function(o, trans){
11187         this.trans = false;
11188         this.destroyTrans(trans, true);
11189         var result;
11190         try {
11191             result = trans.reader.readRecords(o);
11192         }catch(e){
11193             this.fireEvent("loadexception", this, o, trans.arg, e);
11194             trans.callback.call(trans.scope||window, null, trans.arg, false);
11195             return;
11196         }
11197         this.fireEvent("load", this, o, trans.arg);
11198         trans.callback.call(trans.scope||window, result, trans.arg, true);
11199     },
11200
11201     // private
11202     handleFailure : function(trans){
11203         this.trans = false;
11204         this.destroyTrans(trans, false);
11205         this.fireEvent("loadexception", this, null, trans.arg);
11206         trans.callback.call(trans.scope||window, null, trans.arg, false);
11207     }
11208 });/*
11209  * Based on:
11210  * Ext JS Library 1.1.1
11211  * Copyright(c) 2006-2007, Ext JS, LLC.
11212  *
11213  * Originally Released Under LGPL - original licence link has changed is not relivant.
11214  *
11215  * Fork - LGPL
11216  * <script type="text/javascript">
11217  */
11218
11219 /**
11220  * @class Roo.data.JsonReader
11221  * @extends Roo.data.DataReader
11222  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11223  * based on mappings in a provided Roo.data.Record constructor.
11224  * 
11225  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11226  * in the reply previously. 
11227  * 
11228  * <p>
11229  * Example code:
11230  * <pre><code>
11231 var RecordDef = Roo.data.Record.create([
11232     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11233     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11234 ]);
11235 var myReader = new Roo.data.JsonReader({
11236     totalProperty: "results",    // The property which contains the total dataset size (optional)
11237     root: "rows",                // The property which contains an Array of row objects
11238     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11239 }, RecordDef);
11240 </code></pre>
11241  * <p>
11242  * This would consume a JSON file like this:
11243  * <pre><code>
11244 { 'results': 2, 'rows': [
11245     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11246     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11247 }
11248 </code></pre>
11249  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11250  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11251  * paged from the remote server.
11252  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11253  * @cfg {String} root name of the property which contains the Array of row objects.
11254  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11255  * @cfg {Array} fields Array of field definition objects
11256  * @constructor
11257  * Create a new JsonReader
11258  * @param {Object} meta Metadata configuration options
11259  * @param {Object} recordType Either an Array of field definition objects,
11260  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11261  */
11262 Roo.data.JsonReader = function(meta, recordType){
11263     
11264     meta = meta || {};
11265     // set some defaults:
11266     Roo.applyIf(meta, {
11267         totalProperty: 'total',
11268         successProperty : 'success',
11269         root : 'data',
11270         id : 'id'
11271     });
11272     
11273     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11274 };
11275 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11276     
11277     /**
11278      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11279      * Used by Store query builder to append _requestMeta to params.
11280      * 
11281      */
11282     metaFromRemote : false,
11283     /**
11284      * This method is only used by a DataProxy which has retrieved data from a remote server.
11285      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11286      * @return {Object} data A data block which is used by an Roo.data.Store object as
11287      * a cache of Roo.data.Records.
11288      */
11289     read : function(response){
11290         var json = response.responseText;
11291        
11292         var o = /* eval:var:o */ eval("("+json+")");
11293         if(!o) {
11294             throw {message: "JsonReader.read: Json object not found"};
11295         }
11296         
11297         if(o.metaData){
11298             
11299             delete this.ef;
11300             this.metaFromRemote = true;
11301             this.meta = o.metaData;
11302             this.recordType = Roo.data.Record.create(o.metaData.fields);
11303             this.onMetaChange(this.meta, this.recordType, o);
11304         }
11305         return this.readRecords(o);
11306     },
11307
11308     // private function a store will implement
11309     onMetaChange : function(meta, recordType, o){
11310
11311     },
11312
11313     /**
11314          * @ignore
11315          */
11316     simpleAccess: function(obj, subsc) {
11317         return obj[subsc];
11318     },
11319
11320         /**
11321          * @ignore
11322          */
11323     getJsonAccessor: function(){
11324         var re = /[\[\.]/;
11325         return function(expr) {
11326             try {
11327                 return(re.test(expr))
11328                     ? new Function("obj", "return obj." + expr)
11329                     : function(obj){
11330                         return obj[expr];
11331                     };
11332             } catch(e){}
11333             return Roo.emptyFn;
11334         };
11335     }(),
11336
11337     /**
11338      * Create a data block containing Roo.data.Records from an XML document.
11339      * @param {Object} o An object which contains an Array of row objects in the property specified
11340      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11341      * which contains the total size of the dataset.
11342      * @return {Object} data A data block which is used by an Roo.data.Store object as
11343      * a cache of Roo.data.Records.
11344      */
11345     readRecords : function(o){
11346         /**
11347          * After any data loads, the raw JSON data is available for further custom processing.
11348          * @type Object
11349          */
11350         this.o = o;
11351         var s = this.meta, Record = this.recordType,
11352             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11353
11354 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11355         if (!this.ef) {
11356             if(s.totalProperty) {
11357                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11358                 }
11359                 if(s.successProperty) {
11360                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11361                 }
11362                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11363                 if (s.id) {
11364                         var g = this.getJsonAccessor(s.id);
11365                         this.getId = function(rec) {
11366                                 var r = g(rec);  
11367                                 return (r === undefined || r === "") ? null : r;
11368                         };
11369                 } else {
11370                         this.getId = function(){return null;};
11371                 }
11372             this.ef = [];
11373             for(var jj = 0; jj < fl; jj++){
11374                 f = fi[jj];
11375                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11376                 this.ef[jj] = this.getJsonAccessor(map);
11377             }
11378         }
11379
11380         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11381         if(s.totalProperty){
11382             var vt = parseInt(this.getTotal(o), 10);
11383             if(!isNaN(vt)){
11384                 totalRecords = vt;
11385             }
11386         }
11387         if(s.successProperty){
11388             var vs = this.getSuccess(o);
11389             if(vs === false || vs === 'false'){
11390                 success = false;
11391             }
11392         }
11393         var records = [];
11394         for(var i = 0; i < c; i++){
11395                 var n = root[i];
11396             var values = {};
11397             var id = this.getId(n);
11398             for(var j = 0; j < fl; j++){
11399                 f = fi[j];
11400             var v = this.ef[j](n);
11401             if (!f.convert) {
11402                 Roo.log('missing convert for ' + f.name);
11403                 Roo.log(f);
11404                 continue;
11405             }
11406             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11407             }
11408             var record = new Record(values, id);
11409             record.json = n;
11410             records[i] = record;
11411         }
11412         return {
11413             raw : o,
11414             success : success,
11415             records : records,
11416             totalRecords : totalRecords
11417         };
11418     }
11419 });/*
11420  * Based on:
11421  * Ext JS Library 1.1.1
11422  * Copyright(c) 2006-2007, Ext JS, LLC.
11423  *
11424  * Originally Released Under LGPL - original licence link has changed is not relivant.
11425  *
11426  * Fork - LGPL
11427  * <script type="text/javascript">
11428  */
11429
11430 /**
11431  * @class Roo.data.ArrayReader
11432  * @extends Roo.data.DataReader
11433  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11434  * Each element of that Array represents a row of data fields. The
11435  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11436  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11437  * <p>
11438  * Example code:.
11439  * <pre><code>
11440 var RecordDef = Roo.data.Record.create([
11441     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11442     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11443 ]);
11444 var myReader = new Roo.data.ArrayReader({
11445     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11446 }, RecordDef);
11447 </code></pre>
11448  * <p>
11449  * This would consume an Array like this:
11450  * <pre><code>
11451 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11452   </code></pre>
11453  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11454  * @constructor
11455  * Create a new JsonReader
11456  * @param {Object} meta Metadata configuration options.
11457  * @param {Object} recordType Either an Array of field definition objects
11458  * as specified to {@link Roo.data.Record#create},
11459  * or an {@link Roo.data.Record} object
11460  * created using {@link Roo.data.Record#create}.
11461  */
11462 Roo.data.ArrayReader = function(meta, recordType){
11463     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11464 };
11465
11466 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11467     /**
11468      * Create a data block containing Roo.data.Records from an XML document.
11469      * @param {Object} o An Array of row objects which represents the dataset.
11470      * @return {Object} data A data block which is used by an Roo.data.Store object as
11471      * a cache of Roo.data.Records.
11472      */
11473     readRecords : function(o){
11474         var sid = this.meta ? this.meta.id : null;
11475         var recordType = this.recordType, fields = recordType.prototype.fields;
11476         var records = [];
11477         var root = o;
11478             for(var i = 0; i < root.length; i++){
11479                     var n = root[i];
11480                 var values = {};
11481                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11482                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11483                 var f = fields.items[j];
11484                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11485                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11486                 v = f.convert(v);
11487                 values[f.name] = v;
11488             }
11489                 var record = new recordType(values, id);
11490                 record.json = n;
11491                 records[records.length] = record;
11492             }
11493             return {
11494                 records : records,
11495                 totalRecords : records.length
11496             };
11497     }
11498 });/*
11499  * - LGPL
11500  * * 
11501  */
11502
11503 /**
11504  * @class Roo.bootstrap.ComboBox
11505  * @extends Roo.bootstrap.TriggerField
11506  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11507  * @cfg {Boolean} append (true|false) default false
11508  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11509  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11510  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11511  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11512  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11513  * @cfg {Boolean} animate default true
11514  * @cfg {Boolean} emptyResultText only for touch device
11515  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11516  * @constructor
11517  * Create a new ComboBox.
11518  * @param {Object} config Configuration options
11519  */
11520 Roo.bootstrap.ComboBox = function(config){
11521     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11522     this.addEvents({
11523         /**
11524          * @event expand
11525          * Fires when the dropdown list is expanded
11526              * @param {Roo.bootstrap.ComboBox} combo This combo box
11527              */
11528         'expand' : true,
11529         /**
11530          * @event collapse
11531          * Fires when the dropdown list is collapsed
11532              * @param {Roo.bootstrap.ComboBox} combo This combo box
11533              */
11534         'collapse' : true,
11535         /**
11536          * @event beforeselect
11537          * Fires before a list item is selected. Return false to cancel the selection.
11538              * @param {Roo.bootstrap.ComboBox} combo This combo box
11539              * @param {Roo.data.Record} record The data record returned from the underlying store
11540              * @param {Number} index The index of the selected item in the dropdown list
11541              */
11542         'beforeselect' : true,
11543         /**
11544          * @event select
11545          * Fires when a list item is selected
11546              * @param {Roo.bootstrap.ComboBox} combo This combo box
11547              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11548              * @param {Number} index The index of the selected item in the dropdown list
11549              */
11550         'select' : true,
11551         /**
11552          * @event beforequery
11553          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11554          * The event object passed has these properties:
11555              * @param {Roo.bootstrap.ComboBox} combo This combo box
11556              * @param {String} query The query
11557              * @param {Boolean} forceAll true to force "all" query
11558              * @param {Boolean} cancel true to cancel the query
11559              * @param {Object} e The query event object
11560              */
11561         'beforequery': true,
11562          /**
11563          * @event add
11564          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11565              * @param {Roo.bootstrap.ComboBox} combo This combo box
11566              */
11567         'add' : true,
11568         /**
11569          * @event edit
11570          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11571              * @param {Roo.bootstrap.ComboBox} combo This combo box
11572              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11573              */
11574         'edit' : true,
11575         /**
11576          * @event remove
11577          * Fires when the remove value from the combobox array
11578              * @param {Roo.bootstrap.ComboBox} combo This combo box
11579              */
11580         'remove' : true,
11581         /**
11582          * @event specialfilter
11583          * Fires when specialfilter
11584             * @param {Roo.bootstrap.ComboBox} combo This combo box
11585             */
11586         'specialfilter' : true,
11587         /**
11588          * @event tick
11589          * Fires when tick the element
11590             * @param {Roo.bootstrap.ComboBox} combo This combo box
11591             */
11592         'tick' : true,
11593         /**
11594          * @event touchviewdisplay
11595          * Fires when touch view require special display (default is using displayField)
11596             * @param {Roo.bootstrap.ComboBox} combo This combo box
11597             * @param {Object} cfg set html .
11598             */
11599         'touchviewdisplay' : true
11600         
11601     });
11602     
11603     this.item = [];
11604     this.tickItems = [];
11605     
11606     this.selectedIndex = -1;
11607     if(this.mode == 'local'){
11608         if(config.queryDelay === undefined){
11609             this.queryDelay = 10;
11610         }
11611         if(config.minChars === undefined){
11612             this.minChars = 0;
11613         }
11614     }
11615 };
11616
11617 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11618      
11619     /**
11620      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11621      * rendering into an Roo.Editor, defaults to false)
11622      */
11623     /**
11624      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11625      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11626      */
11627     /**
11628      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11629      */
11630     /**
11631      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11632      * the dropdown list (defaults to undefined, with no header element)
11633      */
11634
11635      /**
11636      * @cfg {String/Roo.Template} tpl The template to use to render the output
11637      */
11638      
11639      /**
11640      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11641      */
11642     listWidth: undefined,
11643     /**
11644      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11645      * mode = 'remote' or 'text' if mode = 'local')
11646      */
11647     displayField: undefined,
11648     
11649     /**
11650      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11651      * mode = 'remote' or 'value' if mode = 'local'). 
11652      * Note: use of a valueField requires the user make a selection
11653      * in order for a value to be mapped.
11654      */
11655     valueField: undefined,
11656     
11657     
11658     /**
11659      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11660      * field's data value (defaults to the underlying DOM element's name)
11661      */
11662     hiddenName: undefined,
11663     /**
11664      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11665      */
11666     listClass: '',
11667     /**
11668      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11669      */
11670     selectedClass: 'active',
11671     
11672     /**
11673      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11674      */
11675     shadow:'sides',
11676     /**
11677      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11678      * anchor positions (defaults to 'tl-bl')
11679      */
11680     listAlign: 'tl-bl?',
11681     /**
11682      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11683      */
11684     maxHeight: 300,
11685     /**
11686      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11687      * query specified by the allQuery config option (defaults to 'query')
11688      */
11689     triggerAction: 'query',
11690     /**
11691      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11692      * (defaults to 4, does not apply if editable = false)
11693      */
11694     minChars : 4,
11695     /**
11696      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11697      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11698      */
11699     typeAhead: false,
11700     /**
11701      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11702      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11703      */
11704     queryDelay: 500,
11705     /**
11706      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11707      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11708      */
11709     pageSize: 0,
11710     /**
11711      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11712      * when editable = true (defaults to false)
11713      */
11714     selectOnFocus:false,
11715     /**
11716      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11717      */
11718     queryParam: 'query',
11719     /**
11720      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11721      * when mode = 'remote' (defaults to 'Loading...')
11722      */
11723     loadingText: 'Loading...',
11724     /**
11725      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11726      */
11727     resizable: false,
11728     /**
11729      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11730      */
11731     handleHeight : 8,
11732     /**
11733      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11734      * traditional select (defaults to true)
11735      */
11736     editable: true,
11737     /**
11738      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11739      */
11740     allQuery: '',
11741     /**
11742      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11743      */
11744     mode: 'remote',
11745     /**
11746      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11747      * listWidth has a higher value)
11748      */
11749     minListWidth : 70,
11750     /**
11751      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11752      * allow the user to set arbitrary text into the field (defaults to false)
11753      */
11754     forceSelection:false,
11755     /**
11756      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11757      * if typeAhead = true (defaults to 250)
11758      */
11759     typeAheadDelay : 250,
11760     /**
11761      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11762      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11763      */
11764     valueNotFoundText : undefined,
11765     /**
11766      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11767      */
11768     blockFocus : false,
11769     
11770     /**
11771      * @cfg {Boolean} disableClear Disable showing of clear button.
11772      */
11773     disableClear : false,
11774     /**
11775      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11776      */
11777     alwaysQuery : false,
11778     
11779     /**
11780      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11781      */
11782     multiple : false,
11783     
11784     /**
11785      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11786      */
11787     invalidClass : "has-warning",
11788     
11789     /**
11790      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11791      */
11792     validClass : "has-success",
11793     
11794     /**
11795      * @cfg {Boolean} specialFilter (true|false) special filter default false
11796      */
11797     specialFilter : false,
11798     
11799     /**
11800      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11801      */
11802     mobileTouchView : true,
11803     
11804     //private
11805     addicon : false,
11806     editicon: false,
11807     
11808     page: 0,
11809     hasQuery: false,
11810     append: false,
11811     loadNext: false,
11812     autoFocus : true,
11813     tickable : false,
11814     btnPosition : 'right',
11815     triggerList : true,
11816     showToggleBtn : true,
11817     animate : true,
11818     emptyResultText: 'Empty',
11819     triggerText : 'Select',
11820     
11821     // element that contains real text value.. (when hidden is used..)
11822     
11823     getAutoCreate : function()
11824     {
11825         var cfg = false;
11826         
11827         /*
11828          * Touch Devices
11829          */
11830         
11831         if(Roo.isTouch && this.mobileTouchView){
11832             cfg = this.getAutoCreateTouchView();
11833             return cfg;;
11834         }
11835         
11836         /*
11837          *  Normal ComboBox
11838          */
11839         if(!this.tickable){
11840             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11841             return cfg;
11842         }
11843         
11844         /*
11845          *  ComboBox with tickable selections
11846          */
11847              
11848         var align = this.labelAlign || this.parentLabelAlign();
11849         
11850         cfg = {
11851             cls : 'form-group roo-combobox-tickable' //input-group
11852         };
11853         
11854         var buttons = {
11855             tag : 'div',
11856             cls : 'tickable-buttons',
11857             cn : [
11858                 {
11859                     tag : 'button',
11860                     type : 'button',
11861                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11862                     html : this.triggerText
11863                 },
11864                 {
11865                     tag : 'button',
11866                     type : 'button',
11867                     name : 'ok',
11868                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11869                     html : 'Done'
11870                 },
11871                 {
11872                     tag : 'button',
11873                     type : 'button',
11874                     name : 'cancel',
11875                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11876                     html : 'Cancel'
11877                 }
11878             ]
11879         };
11880         
11881         if(this.editable){
11882             buttons.cn.unshift({
11883                 tag: 'input',
11884                 cls: 'select2-search-field-input'
11885             });
11886         }
11887         
11888         var _this = this;
11889         
11890         Roo.each(buttons.cn, function(c){
11891             if (_this.size) {
11892                 c.cls += ' btn-' + _this.size;
11893             }
11894
11895             if (_this.disabled) {
11896                 c.disabled = true;
11897             }
11898         });
11899         
11900         var box = {
11901             tag: 'div',
11902             cn: [
11903                 {
11904                     tag: 'input',
11905                     type : 'hidden',
11906                     cls: 'form-hidden-field'
11907                 },
11908                 {
11909                     tag: 'ul',
11910                     cls: 'select2-choices',
11911                     cn:[
11912                         {
11913                             tag: 'li',
11914                             cls: 'select2-search-field',
11915                             cn: [
11916
11917                                 buttons
11918                             ]
11919                         }
11920                     ]
11921                 }
11922             ]
11923         };
11924         
11925         var combobox = {
11926             cls: 'select2-container input-group select2-container-multi',
11927             cn: [
11928                 box
11929 //                {
11930 //                    tag: 'ul',
11931 //                    cls: 'typeahead typeahead-long dropdown-menu',
11932 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11933 //                }
11934             ]
11935         };
11936         
11937         if(this.hasFeedback && !this.allowBlank){
11938             
11939             var feedback = {
11940                 tag: 'span',
11941                 cls: 'glyphicon form-control-feedback'
11942             };
11943
11944             combobox.cn.push(feedback);
11945         }
11946         
11947         if (align ==='left' && this.fieldLabel.length) {
11948             
11949 //                Roo.log("left and has label");
11950                 cfg.cn = [
11951                     
11952                     {
11953                         tag: 'label',
11954                         'for' :  id,
11955                         cls : 'control-label col-sm-' + this.labelWidth,
11956                         html : this.fieldLabel
11957                         
11958                     },
11959                     {
11960                         cls : "col-sm-" + (12 - this.labelWidth), 
11961                         cn: [
11962                             combobox
11963                         ]
11964                     }
11965                     
11966                 ];
11967         } else if ( this.fieldLabel.length) {
11968 //                Roo.log(" label");
11969                  cfg.cn = [
11970                    
11971                     {
11972                         tag: 'label',
11973                         //cls : 'input-group-addon',
11974                         html : this.fieldLabel
11975                         
11976                     },
11977                     
11978                     combobox
11979                     
11980                 ];
11981
11982         } else {
11983             
11984 //                Roo.log(" no label && no align");
11985                 cfg = combobox
11986                      
11987                 
11988         }
11989          
11990         var settings=this;
11991         ['xs','sm','md','lg'].map(function(size){
11992             if (settings[size]) {
11993                 cfg.cls += ' col-' + size + '-' + settings[size];
11994             }
11995         });
11996         
11997         return cfg;
11998         
11999     },
12000     
12001     _initEventsCalled : false,
12002     
12003     // private
12004     initEvents: function()
12005     {
12006         
12007         if (this._initEventsCalled) { // as we call render... prevent looping...
12008             return;
12009         }
12010         this._initEventsCalled = true;
12011         
12012         if (!this.store) {
12013             throw "can not find store for combo";
12014         }
12015         
12016         this.store = Roo.factory(this.store, Roo.data);
12017         
12018         // if we are building from html. then this element is so complex, that we can not really
12019         // use the rendered HTML.
12020         // so we have to trash and replace the previous code.
12021         if (Roo.XComponent.build_from_html) {
12022             
12023             // remove this element....
12024             var e = this.el.dom, k=0;
12025             while (e ) { e = e.previousSibling;  ++k;}
12026
12027             this.el.remove();
12028             
12029             this.el=false;
12030             this.rendered = false;
12031             
12032             this.render(this.parent().getChildContainer(true), k);
12033             
12034             
12035             
12036         }
12037         
12038         
12039         /*
12040          * Touch Devices
12041          */
12042         
12043         if(Roo.isTouch && this.mobileTouchView){
12044             this.initTouchView();
12045             return;
12046         }
12047         
12048         if(this.tickable){
12049             this.initTickableEvents();
12050             return;
12051         }
12052         
12053         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12054         
12055         if(this.hiddenName){
12056             
12057             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12058             
12059             this.hiddenField.dom.value =
12060                 this.hiddenValue !== undefined ? this.hiddenValue :
12061                 this.value !== undefined ? this.value : '';
12062
12063             // prevent input submission
12064             this.el.dom.removeAttribute('name');
12065             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12066              
12067              
12068         }
12069         //if(Roo.isGecko){
12070         //    this.el.dom.setAttribute('autocomplete', 'off');
12071         //}
12072         
12073         var cls = 'x-combo-list';
12074         
12075         //this.list = new Roo.Layer({
12076         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12077         //});
12078         
12079         var _this = this;
12080         
12081         (function(){
12082             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12083             _this.list.setWidth(lw);
12084         }).defer(100);
12085         
12086         this.list.on('mouseover', this.onViewOver, this);
12087         this.list.on('mousemove', this.onViewMove, this);
12088         
12089         this.list.on('scroll', this.onViewScroll, this);
12090         
12091         /*
12092         this.list.swallowEvent('mousewheel');
12093         this.assetHeight = 0;
12094
12095         if(this.title){
12096             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12097             this.assetHeight += this.header.getHeight();
12098         }
12099
12100         this.innerList = this.list.createChild({cls:cls+'-inner'});
12101         this.innerList.on('mouseover', this.onViewOver, this);
12102         this.innerList.on('mousemove', this.onViewMove, this);
12103         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12104         
12105         if(this.allowBlank && !this.pageSize && !this.disableClear){
12106             this.footer = this.list.createChild({cls:cls+'-ft'});
12107             this.pageTb = new Roo.Toolbar(this.footer);
12108            
12109         }
12110         if(this.pageSize){
12111             this.footer = this.list.createChild({cls:cls+'-ft'});
12112             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12113                     {pageSize: this.pageSize});
12114             
12115         }
12116         
12117         if (this.pageTb && this.allowBlank && !this.disableClear) {
12118             var _this = this;
12119             this.pageTb.add(new Roo.Toolbar.Fill(), {
12120                 cls: 'x-btn-icon x-btn-clear',
12121                 text: '&#160;',
12122                 handler: function()
12123                 {
12124                     _this.collapse();
12125                     _this.clearValue();
12126                     _this.onSelect(false, -1);
12127                 }
12128             });
12129         }
12130         if (this.footer) {
12131             this.assetHeight += this.footer.getHeight();
12132         }
12133         */
12134             
12135         if(!this.tpl){
12136             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12137         }
12138
12139         this.view = new Roo.View(this.list, this.tpl, {
12140             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12141         });
12142         //this.view.wrapEl.setDisplayed(false);
12143         this.view.on('click', this.onViewClick, this);
12144         
12145         
12146         
12147         this.store.on('beforeload', this.onBeforeLoad, this);
12148         this.store.on('load', this.onLoad, this);
12149         this.store.on('loadexception', this.onLoadException, this);
12150         /*
12151         if(this.resizable){
12152             this.resizer = new Roo.Resizable(this.list,  {
12153                pinned:true, handles:'se'
12154             });
12155             this.resizer.on('resize', function(r, w, h){
12156                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12157                 this.listWidth = w;
12158                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12159                 this.restrictHeight();
12160             }, this);
12161             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12162         }
12163         */
12164         if(!this.editable){
12165             this.editable = true;
12166             this.setEditable(false);
12167         }
12168         
12169         /*
12170         
12171         if (typeof(this.events.add.listeners) != 'undefined') {
12172             
12173             this.addicon = this.wrap.createChild(
12174                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12175        
12176             this.addicon.on('click', function(e) {
12177                 this.fireEvent('add', this);
12178             }, this);
12179         }
12180         if (typeof(this.events.edit.listeners) != 'undefined') {
12181             
12182             this.editicon = this.wrap.createChild(
12183                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12184             if (this.addicon) {
12185                 this.editicon.setStyle('margin-left', '40px');
12186             }
12187             this.editicon.on('click', function(e) {
12188                 
12189                 // we fire even  if inothing is selected..
12190                 this.fireEvent('edit', this, this.lastData );
12191                 
12192             }, this);
12193         }
12194         */
12195         
12196         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12197             "up" : function(e){
12198                 this.inKeyMode = true;
12199                 this.selectPrev();
12200             },
12201
12202             "down" : function(e){
12203                 if(!this.isExpanded()){
12204                     this.onTriggerClick();
12205                 }else{
12206                     this.inKeyMode = true;
12207                     this.selectNext();
12208                 }
12209             },
12210
12211             "enter" : function(e){
12212 //                this.onViewClick();
12213                 //return true;
12214                 this.collapse();
12215                 
12216                 if(this.fireEvent("specialkey", this, e)){
12217                     this.onViewClick(false);
12218                 }
12219                 
12220                 return true;
12221             },
12222
12223             "esc" : function(e){
12224                 this.collapse();
12225             },
12226
12227             "tab" : function(e){
12228                 this.collapse();
12229                 
12230                 if(this.fireEvent("specialkey", this, e)){
12231                     this.onViewClick(false);
12232                 }
12233                 
12234                 return true;
12235             },
12236
12237             scope : this,
12238
12239             doRelay : function(foo, bar, hname){
12240                 if(hname == 'down' || this.scope.isExpanded()){
12241                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12242                 }
12243                 return true;
12244             },
12245
12246             forceKeyDown: true
12247         });
12248         
12249         
12250         this.queryDelay = Math.max(this.queryDelay || 10,
12251                 this.mode == 'local' ? 10 : 250);
12252         
12253         
12254         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12255         
12256         if(this.typeAhead){
12257             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12258         }
12259         if(this.editable !== false){
12260             this.inputEl().on("keyup", this.onKeyUp, this);
12261         }
12262         if(this.forceSelection){
12263             this.inputEl().on('blur', this.doForce, this);
12264         }
12265         
12266         if(this.multiple){
12267             this.choices = this.el.select('ul.select2-choices', true).first();
12268             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12269         }
12270     },
12271     
12272     initTickableEvents: function()
12273     {   
12274         this.createList();
12275         
12276         if(this.hiddenName){
12277             
12278             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12279             
12280             this.hiddenField.dom.value =
12281                 this.hiddenValue !== undefined ? this.hiddenValue :
12282                 this.value !== undefined ? this.value : '';
12283
12284             // prevent input submission
12285             this.el.dom.removeAttribute('name');
12286             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12287              
12288              
12289         }
12290         
12291 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12292         
12293         this.choices = this.el.select('ul.select2-choices', true).first();
12294         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12295         if(this.triggerList){
12296             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12297         }
12298          
12299         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12300         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12301         
12302         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12303         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12304         
12305         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12306         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12307         
12308         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12309         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12310         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12311         
12312         this.okBtn.hide();
12313         this.cancelBtn.hide();
12314         
12315         var _this = this;
12316         
12317         (function(){
12318             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12319             _this.list.setWidth(lw);
12320         }).defer(100);
12321         
12322         this.list.on('mouseover', this.onViewOver, this);
12323         this.list.on('mousemove', this.onViewMove, this);
12324         
12325         this.list.on('scroll', this.onViewScroll, this);
12326         
12327         if(!this.tpl){
12328             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12329         }
12330
12331         this.view = new Roo.View(this.list, this.tpl, {
12332             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12333         });
12334         
12335         //this.view.wrapEl.setDisplayed(false);
12336         this.view.on('click', this.onViewClick, this);
12337         
12338         
12339         
12340         this.store.on('beforeload', this.onBeforeLoad, this);
12341         this.store.on('load', this.onLoad, this);
12342         this.store.on('loadexception', this.onLoadException, this);
12343         
12344         if(this.editable){
12345             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12346                 "up" : function(e){
12347                     this.inKeyMode = true;
12348                     this.selectPrev();
12349                 },
12350
12351                 "down" : function(e){
12352                     this.inKeyMode = true;
12353                     this.selectNext();
12354                 },
12355
12356                 "enter" : function(e){
12357                     if(this.fireEvent("specialkey", this, e)){
12358                         this.onViewClick(false);
12359                     }
12360                     
12361                     return true;
12362                 },
12363
12364                 "esc" : function(e){
12365                     this.onTickableFooterButtonClick(e, false, false);
12366                 },
12367
12368                 "tab" : function(e){
12369                     this.fireEvent("specialkey", this, e);
12370                     
12371                     this.onTickableFooterButtonClick(e, false, false);
12372                     
12373                     return true;
12374                 },
12375
12376                 scope : this,
12377
12378                 doRelay : function(e, fn, key){
12379                     if(this.scope.isExpanded()){
12380                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12381                     }
12382                     return true;
12383                 },
12384
12385                 forceKeyDown: true
12386             });
12387         }
12388         
12389         this.queryDelay = Math.max(this.queryDelay || 10,
12390                 this.mode == 'local' ? 10 : 250);
12391         
12392         
12393         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12394         
12395         if(this.typeAhead){
12396             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12397         }
12398         
12399         if(this.editable !== false){
12400             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12401         }
12402         
12403     },
12404
12405     onDestroy : function(){
12406         if(this.view){
12407             this.view.setStore(null);
12408             this.view.el.removeAllListeners();
12409             this.view.el.remove();
12410             this.view.purgeListeners();
12411         }
12412         if(this.list){
12413             this.list.dom.innerHTML  = '';
12414         }
12415         
12416         if(this.store){
12417             this.store.un('beforeload', this.onBeforeLoad, this);
12418             this.store.un('load', this.onLoad, this);
12419             this.store.un('loadexception', this.onLoadException, this);
12420         }
12421         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12422     },
12423
12424     // private
12425     fireKey : function(e){
12426         if(e.isNavKeyPress() && !this.list.isVisible()){
12427             this.fireEvent("specialkey", this, e);
12428         }
12429     },
12430
12431     // private
12432     onResize: function(w, h){
12433 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12434 //        
12435 //        if(typeof w != 'number'){
12436 //            // we do not handle it!?!?
12437 //            return;
12438 //        }
12439 //        var tw = this.trigger.getWidth();
12440 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12441 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12442 //        var x = w - tw;
12443 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12444 //            
12445 //        //this.trigger.setStyle('left', x+'px');
12446 //        
12447 //        if(this.list && this.listWidth === undefined){
12448 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12449 //            this.list.setWidth(lw);
12450 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12451 //        }
12452         
12453     
12454         
12455     },
12456
12457     /**
12458      * Allow or prevent the user from directly editing the field text.  If false is passed,
12459      * the user will only be able to select from the items defined in the dropdown list.  This method
12460      * is the runtime equivalent of setting the 'editable' config option at config time.
12461      * @param {Boolean} value True to allow the user to directly edit the field text
12462      */
12463     setEditable : function(value){
12464         if(value == this.editable){
12465             return;
12466         }
12467         this.editable = value;
12468         if(!value){
12469             this.inputEl().dom.setAttribute('readOnly', true);
12470             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12471             this.inputEl().addClass('x-combo-noedit');
12472         }else{
12473             this.inputEl().dom.setAttribute('readOnly', false);
12474             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12475             this.inputEl().removeClass('x-combo-noedit');
12476         }
12477     },
12478
12479     // private
12480     
12481     onBeforeLoad : function(combo,opts){
12482         if(!this.hasFocus){
12483             return;
12484         }
12485          if (!opts.add) {
12486             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12487          }
12488         this.restrictHeight();
12489         this.selectedIndex = -1;
12490     },
12491
12492     // private
12493     onLoad : function(){
12494         
12495         this.hasQuery = false;
12496         
12497         if(!this.hasFocus){
12498             return;
12499         }
12500         
12501         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12502             this.loading.hide();
12503         }
12504              
12505         if(this.store.getCount() > 0){
12506             this.expand();
12507             this.restrictHeight();
12508             if(this.lastQuery == this.allQuery){
12509                 if(this.editable && !this.tickable){
12510                     this.inputEl().dom.select();
12511                 }
12512                 
12513                 if(
12514                     !this.selectByValue(this.value, true) &&
12515                     this.autoFocus && 
12516                     (
12517                         !this.store.lastOptions ||
12518                         typeof(this.store.lastOptions.add) == 'undefined' || 
12519                         this.store.lastOptions.add != true
12520                     )
12521                 ){
12522                     this.select(0, true);
12523                 }
12524             }else{
12525                 if(this.autoFocus){
12526                     this.selectNext();
12527                 }
12528                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12529                     this.taTask.delay(this.typeAheadDelay);
12530                 }
12531             }
12532         }else{
12533             this.onEmptyResults();
12534         }
12535         
12536         //this.el.focus();
12537     },
12538     // private
12539     onLoadException : function()
12540     {
12541         this.hasQuery = false;
12542         
12543         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12544             this.loading.hide();
12545         }
12546         
12547         if(this.tickable && this.editable){
12548             return;
12549         }
12550         
12551         this.collapse();
12552         // only causes errors at present
12553         //Roo.log(this.store.reader.jsonData);
12554         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12555             // fixme
12556             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12557         //}
12558         
12559         
12560     },
12561     // private
12562     onTypeAhead : function(){
12563         if(this.store.getCount() > 0){
12564             var r = this.store.getAt(0);
12565             var newValue = r.data[this.displayField];
12566             var len = newValue.length;
12567             var selStart = this.getRawValue().length;
12568             
12569             if(selStart != len){
12570                 this.setRawValue(newValue);
12571                 this.selectText(selStart, newValue.length);
12572             }
12573         }
12574     },
12575
12576     // private
12577     onSelect : function(record, index){
12578         
12579         if(this.fireEvent('beforeselect', this, record, index) !== false){
12580         
12581             this.setFromData(index > -1 ? record.data : false);
12582             
12583             this.collapse();
12584             this.fireEvent('select', this, record, index);
12585         }
12586     },
12587
12588     /**
12589      * Returns the currently selected field value or empty string if no value is set.
12590      * @return {String} value The selected value
12591      */
12592     getValue : function(){
12593         
12594         if(this.multiple){
12595             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12596         }
12597         
12598         if(this.valueField){
12599             return typeof this.value != 'undefined' ? this.value : '';
12600         }else{
12601             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12602         }
12603     },
12604
12605     /**
12606      * Clears any text/value currently set in the field
12607      */
12608     clearValue : function(){
12609         if(this.hiddenField){
12610             this.hiddenField.dom.value = '';
12611         }
12612         this.value = '';
12613         this.setRawValue('');
12614         this.lastSelectionText = '';
12615         this.lastData = false;
12616         
12617         var close = this.closeTriggerEl();
12618         
12619         if(close){
12620             close.hide();
12621         }
12622         
12623     },
12624
12625     /**
12626      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12627      * will be displayed in the field.  If the value does not match the data value of an existing item,
12628      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12629      * Otherwise the field will be blank (although the value will still be set).
12630      * @param {String} value The value to match
12631      */
12632     setValue : function(v){
12633         if(this.multiple){
12634             this.syncValue();
12635             return;
12636         }
12637         
12638         var text = v;
12639         if(this.valueField){
12640             var r = this.findRecord(this.valueField, v);
12641             if(r){
12642                 text = r.data[this.displayField];
12643             }else if(this.valueNotFoundText !== undefined){
12644                 text = this.valueNotFoundText;
12645             }
12646         }
12647         this.lastSelectionText = text;
12648         if(this.hiddenField){
12649             this.hiddenField.dom.value = v;
12650         }
12651         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12652         this.value = v;
12653         
12654         var close = this.closeTriggerEl();
12655         
12656         if(close){
12657             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12658         }
12659     },
12660     /**
12661      * @property {Object} the last set data for the element
12662      */
12663     
12664     lastData : false,
12665     /**
12666      * Sets the value of the field based on a object which is related to the record format for the store.
12667      * @param {Object} value the value to set as. or false on reset?
12668      */
12669     setFromData : function(o){
12670         
12671         if(this.multiple){
12672             this.addItem(o);
12673             return;
12674         }
12675             
12676         var dv = ''; // display value
12677         var vv = ''; // value value..
12678         this.lastData = o;
12679         if (this.displayField) {
12680             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12681         } else {
12682             // this is an error condition!!!
12683             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12684         }
12685         
12686         if(this.valueField){
12687             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12688         }
12689         
12690         var close = this.closeTriggerEl();
12691         
12692         if(close){
12693             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12694         }
12695         
12696         if(this.hiddenField){
12697             this.hiddenField.dom.value = vv;
12698             
12699             this.lastSelectionText = dv;
12700             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12701             this.value = vv;
12702             return;
12703         }
12704         // no hidden field.. - we store the value in 'value', but still display
12705         // display field!!!!
12706         this.lastSelectionText = dv;
12707         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12708         this.value = vv;
12709         
12710         
12711         
12712     },
12713     // private
12714     reset : function(){
12715         // overridden so that last data is reset..
12716         
12717         if(this.multiple){
12718             this.clearItem();
12719             return;
12720         }
12721         
12722         this.setValue(this.originalValue);
12723         this.clearInvalid();
12724         this.lastData = false;
12725         if (this.view) {
12726             this.view.clearSelections();
12727         }
12728     },
12729     // private
12730     findRecord : function(prop, value){
12731         var record;
12732         if(this.store.getCount() > 0){
12733             this.store.each(function(r){
12734                 if(r.data[prop] == value){
12735                     record = r;
12736                     return false;
12737                 }
12738                 return true;
12739             });
12740         }
12741         return record;
12742     },
12743     
12744     getName: function()
12745     {
12746         // returns hidden if it's set..
12747         if (!this.rendered) {return ''};
12748         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12749         
12750     },
12751     // private
12752     onViewMove : function(e, t){
12753         this.inKeyMode = false;
12754     },
12755
12756     // private
12757     onViewOver : function(e, t){
12758         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12759             return;
12760         }
12761         var item = this.view.findItemFromChild(t);
12762         
12763         if(item){
12764             var index = this.view.indexOf(item);
12765             this.select(index, false);
12766         }
12767     },
12768
12769     // private
12770     onViewClick : function(view, doFocus, el, e)
12771     {
12772         var index = this.view.getSelectedIndexes()[0];
12773         
12774         var r = this.store.getAt(index);
12775         
12776         if(this.tickable){
12777             
12778             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12779                 return;
12780             }
12781             
12782             var rm = false;
12783             var _this = this;
12784             
12785             Roo.each(this.tickItems, function(v,k){
12786                 
12787                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12788                     Roo.log(v);
12789                     _this.tickItems.splice(k, 1);
12790                     
12791                     if(typeof(e) == 'undefined' && view == false){
12792                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12793                     }
12794                     
12795                     rm = true;
12796                     return;
12797                 }
12798             });
12799             
12800             if(rm){
12801                 return;
12802             }
12803             
12804             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12805                 this.tickItems.push(r.data);
12806             }
12807             
12808             if(typeof(e) == 'undefined' && view == false){
12809                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12810             }
12811                     
12812             return;
12813         }
12814         
12815         if(r){
12816             this.onSelect(r, index);
12817         }
12818         if(doFocus !== false && !this.blockFocus){
12819             this.inputEl().focus();
12820         }
12821     },
12822
12823     // private
12824     restrictHeight : function(){
12825         //this.innerList.dom.style.height = '';
12826         //var inner = this.innerList.dom;
12827         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12828         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12829         //this.list.beginUpdate();
12830         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12831         this.list.alignTo(this.inputEl(), this.listAlign);
12832         this.list.alignTo(this.inputEl(), this.listAlign);
12833         //this.list.endUpdate();
12834     },
12835
12836     // private
12837     onEmptyResults : function(){
12838         
12839         if(this.tickable && this.editable){
12840             this.restrictHeight();
12841             return;
12842         }
12843         
12844         this.collapse();
12845     },
12846
12847     /**
12848      * Returns true if the dropdown list is expanded, else false.
12849      */
12850     isExpanded : function(){
12851         return this.list.isVisible();
12852     },
12853
12854     /**
12855      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12856      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12857      * @param {String} value The data value of the item to select
12858      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12859      * selected item if it is not currently in view (defaults to true)
12860      * @return {Boolean} True if the value matched an item in the list, else false
12861      */
12862     selectByValue : function(v, scrollIntoView){
12863         if(v !== undefined && v !== null){
12864             var r = this.findRecord(this.valueField || this.displayField, v);
12865             if(r){
12866                 this.select(this.store.indexOf(r), scrollIntoView);
12867                 return true;
12868             }
12869         }
12870         return false;
12871     },
12872
12873     /**
12874      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12875      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12876      * @param {Number} index The zero-based index of the list item to select
12877      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12878      * selected item if it is not currently in view (defaults to true)
12879      */
12880     select : function(index, scrollIntoView){
12881         this.selectedIndex = index;
12882         this.view.select(index);
12883         if(scrollIntoView !== false){
12884             var el = this.view.getNode(index);
12885             /*
12886              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12887              */
12888             if(el){
12889                 this.list.scrollChildIntoView(el, false);
12890             }
12891         }
12892     },
12893
12894     // private
12895     selectNext : function(){
12896         var ct = this.store.getCount();
12897         if(ct > 0){
12898             if(this.selectedIndex == -1){
12899                 this.select(0);
12900             }else if(this.selectedIndex < ct-1){
12901                 this.select(this.selectedIndex+1);
12902             }
12903         }
12904     },
12905
12906     // private
12907     selectPrev : function(){
12908         var ct = this.store.getCount();
12909         if(ct > 0){
12910             if(this.selectedIndex == -1){
12911                 this.select(0);
12912             }else if(this.selectedIndex != 0){
12913                 this.select(this.selectedIndex-1);
12914             }
12915         }
12916     },
12917
12918     // private
12919     onKeyUp : function(e){
12920         if(this.editable !== false && !e.isSpecialKey()){
12921             this.lastKey = e.getKey();
12922             this.dqTask.delay(this.queryDelay);
12923         }
12924     },
12925
12926     // private
12927     validateBlur : function(){
12928         return !this.list || !this.list.isVisible();   
12929     },
12930
12931     // private
12932     initQuery : function(){
12933         
12934         var v = this.getRawValue();
12935         
12936         if(this.tickable && this.editable){
12937             v = this.tickableInputEl().getValue();
12938         }
12939         
12940         this.doQuery(v);
12941     },
12942
12943     // private
12944     doForce : function(){
12945         if(this.inputEl().dom.value.length > 0){
12946             this.inputEl().dom.value =
12947                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12948              
12949         }
12950     },
12951
12952     /**
12953      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12954      * query allowing the query action to be canceled if needed.
12955      * @param {String} query The SQL query to execute
12956      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12957      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12958      * saved in the current store (defaults to false)
12959      */
12960     doQuery : function(q, forceAll){
12961         
12962         if(q === undefined || q === null){
12963             q = '';
12964         }
12965         var qe = {
12966             query: q,
12967             forceAll: forceAll,
12968             combo: this,
12969             cancel:false
12970         };
12971         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12972             return false;
12973         }
12974         q = qe.query;
12975         
12976         forceAll = qe.forceAll;
12977         if(forceAll === true || (q.length >= this.minChars)){
12978             
12979             this.hasQuery = true;
12980             
12981             if(this.lastQuery != q || this.alwaysQuery){
12982                 this.lastQuery = q;
12983                 if(this.mode == 'local'){
12984                     this.selectedIndex = -1;
12985                     if(forceAll){
12986                         this.store.clearFilter();
12987                     }else{
12988                         
12989                         if(this.specialFilter){
12990                             this.fireEvent('specialfilter', this);
12991                             this.onLoad();
12992                             return;
12993                         }
12994                         
12995                         this.store.filter(this.displayField, q);
12996                     }
12997                     
12998                     this.store.fireEvent("datachanged", this.store);
12999                     
13000                     this.onLoad();
13001                     
13002                     
13003                 }else{
13004                     
13005                     this.store.baseParams[this.queryParam] = q;
13006                     
13007                     var options = {params : this.getParams(q)};
13008                     
13009                     if(this.loadNext){
13010                         options.add = true;
13011                         options.params.start = this.page * this.pageSize;
13012                     }
13013                     
13014                     this.store.load(options);
13015                     
13016                     /*
13017                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13018                      *  we should expand the list on onLoad
13019                      *  so command out it
13020                      */
13021 //                    this.expand();
13022                 }
13023             }else{
13024                 this.selectedIndex = -1;
13025                 this.onLoad();   
13026             }
13027         }
13028         
13029         this.loadNext = false;
13030     },
13031     
13032     // private
13033     getParams : function(q){
13034         var p = {};
13035         //p[this.queryParam] = q;
13036         
13037         if(this.pageSize){
13038             p.start = 0;
13039             p.limit = this.pageSize;
13040         }
13041         return p;
13042     },
13043
13044     /**
13045      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13046      */
13047     collapse : function(){
13048         if(!this.isExpanded()){
13049             return;
13050         }
13051         
13052         this.list.hide();
13053         
13054         if(this.tickable){
13055             this.hasFocus = false;
13056             this.okBtn.hide();
13057             this.cancelBtn.hide();
13058             this.trigger.show();
13059             
13060             if(this.editable){
13061                 this.tickableInputEl().dom.value = '';
13062                 this.tickableInputEl().blur();
13063             }
13064             
13065         }
13066         
13067         Roo.get(document).un('mousedown', this.collapseIf, this);
13068         Roo.get(document).un('mousewheel', this.collapseIf, this);
13069         if (!this.editable) {
13070             Roo.get(document).un('keydown', this.listKeyPress, this);
13071         }
13072         this.fireEvent('collapse', this);
13073     },
13074
13075     // private
13076     collapseIf : function(e){
13077         var in_combo  = e.within(this.el);
13078         var in_list =  e.within(this.list);
13079         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13080         
13081         if (in_combo || in_list || is_list) {
13082             //e.stopPropagation();
13083             return;
13084         }
13085         
13086         if(this.tickable){
13087             this.onTickableFooterButtonClick(e, false, false);
13088         }
13089
13090         this.collapse();
13091         
13092     },
13093
13094     /**
13095      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13096      */
13097     expand : function(){
13098        
13099         if(this.isExpanded() || !this.hasFocus){
13100             return;
13101         }
13102         
13103         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13104         this.list.setWidth(lw);
13105         
13106         
13107          Roo.log('expand');
13108         
13109         this.list.show();
13110         
13111         this.restrictHeight();
13112         
13113         if(this.tickable){
13114             
13115             this.tickItems = Roo.apply([], this.item);
13116             
13117             this.okBtn.show();
13118             this.cancelBtn.show();
13119             this.trigger.hide();
13120             
13121             if(this.editable){
13122                 this.tickableInputEl().focus();
13123             }
13124             
13125         }
13126         
13127         Roo.get(document).on('mousedown', this.collapseIf, this);
13128         Roo.get(document).on('mousewheel', this.collapseIf, this);
13129         if (!this.editable) {
13130             Roo.get(document).on('keydown', this.listKeyPress, this);
13131         }
13132         
13133         this.fireEvent('expand', this);
13134     },
13135
13136     // private
13137     // Implements the default empty TriggerField.onTriggerClick function
13138     onTriggerClick : function(e)
13139     {
13140         Roo.log('trigger click');
13141         
13142         if(this.disabled || !this.triggerList){
13143             return;
13144         }
13145         
13146         this.page = 0;
13147         this.loadNext = false;
13148         
13149         if(this.isExpanded()){
13150             this.collapse();
13151             if (!this.blockFocus) {
13152                 this.inputEl().focus();
13153             }
13154             
13155         }else {
13156             this.hasFocus = true;
13157             if(this.triggerAction == 'all') {
13158                 this.doQuery(this.allQuery, true);
13159             } else {
13160                 this.doQuery(this.getRawValue());
13161             }
13162             if (!this.blockFocus) {
13163                 this.inputEl().focus();
13164             }
13165         }
13166     },
13167     
13168     onTickableTriggerClick : function(e)
13169     {
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174         this.page = 0;
13175         this.loadNext = false;
13176         this.hasFocus = true;
13177         
13178         if(this.triggerAction == 'all') {
13179             this.doQuery(this.allQuery, true);
13180         } else {
13181             this.doQuery(this.getRawValue());
13182         }
13183     },
13184     
13185     onSearchFieldClick : function(e)
13186     {
13187         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13188             this.onTickableFooterButtonClick(e, false, false);
13189             return;
13190         }
13191         
13192         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13193             return;
13194         }
13195         
13196         this.page = 0;
13197         this.loadNext = false;
13198         this.hasFocus = true;
13199         
13200         if(this.triggerAction == 'all') {
13201             this.doQuery(this.allQuery, true);
13202         } else {
13203             this.doQuery(this.getRawValue());
13204         }
13205     },
13206     
13207     listKeyPress : function(e)
13208     {
13209         //Roo.log('listkeypress');
13210         // scroll to first matching element based on key pres..
13211         if (e.isSpecialKey()) {
13212             return false;
13213         }
13214         var k = String.fromCharCode(e.getKey()).toUpperCase();
13215         //Roo.log(k);
13216         var match  = false;
13217         var csel = this.view.getSelectedNodes();
13218         var cselitem = false;
13219         if (csel.length) {
13220             var ix = this.view.indexOf(csel[0]);
13221             cselitem  = this.store.getAt(ix);
13222             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13223                 cselitem = false;
13224             }
13225             
13226         }
13227         
13228         this.store.each(function(v) { 
13229             if (cselitem) {
13230                 // start at existing selection.
13231                 if (cselitem.id == v.id) {
13232                     cselitem = false;
13233                 }
13234                 return true;
13235             }
13236                 
13237             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13238                 match = this.store.indexOf(v);
13239                 return false;
13240             }
13241             return true;
13242         }, this);
13243         
13244         if (match === false) {
13245             return true; // no more action?
13246         }
13247         // scroll to?
13248         this.view.select(match);
13249         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13250         sn.scrollIntoView(sn.dom.parentNode, false);
13251     },
13252     
13253     onViewScroll : function(e, t){
13254         
13255         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13256             return;
13257         }
13258         
13259         this.hasQuery = true;
13260         
13261         this.loading = this.list.select('.loading', true).first();
13262         
13263         if(this.loading === null){
13264             this.list.createChild({
13265                 tag: 'div',
13266                 cls: 'loading select2-more-results select2-active',
13267                 html: 'Loading more results...'
13268             });
13269             
13270             this.loading = this.list.select('.loading', true).first();
13271             
13272             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13273             
13274             this.loading.hide();
13275         }
13276         
13277         this.loading.show();
13278         
13279         var _combo = this;
13280         
13281         this.page++;
13282         this.loadNext = true;
13283         
13284         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13285         
13286         return;
13287     },
13288     
13289     addItem : function(o)
13290     {   
13291         var dv = ''; // display value
13292         
13293         if (this.displayField) {
13294             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13295         } else {
13296             // this is an error condition!!!
13297             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13298         }
13299         
13300         if(!dv.length){
13301             return;
13302         }
13303         
13304         var choice = this.choices.createChild({
13305             tag: 'li',
13306             cls: 'select2-search-choice',
13307             cn: [
13308                 {
13309                     tag: 'div',
13310                     html: dv
13311                 },
13312                 {
13313                     tag: 'a',
13314                     href: '#',
13315                     cls: 'select2-search-choice-close',
13316                     tabindex: '-1'
13317                 }
13318             ]
13319             
13320         }, this.searchField);
13321         
13322         var close = choice.select('a.select2-search-choice-close', true).first();
13323         
13324         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13325         
13326         this.item.push(o);
13327         
13328         this.lastData = o;
13329         
13330         this.syncValue();
13331         
13332         this.inputEl().dom.value = '';
13333         
13334         this.validate();
13335     },
13336     
13337     onRemoveItem : function(e, _self, o)
13338     {
13339         e.preventDefault();
13340         
13341         this.lastItem = Roo.apply([], this.item);
13342         
13343         var index = this.item.indexOf(o.data) * 1;
13344         
13345         if( index < 0){
13346             Roo.log('not this item?!');
13347             return;
13348         }
13349         
13350         this.item.splice(index, 1);
13351         o.item.remove();
13352         
13353         this.syncValue();
13354         
13355         this.fireEvent('remove', this, e);
13356         
13357         this.validate();
13358         
13359     },
13360     
13361     syncValue : function()
13362     {
13363         if(!this.item.length){
13364             this.clearValue();
13365             return;
13366         }
13367             
13368         var value = [];
13369         var _this = this;
13370         Roo.each(this.item, function(i){
13371             if(_this.valueField){
13372                 value.push(i[_this.valueField]);
13373                 return;
13374             }
13375
13376             value.push(i);
13377         });
13378
13379         this.value = value.join(',');
13380
13381         if(this.hiddenField){
13382             this.hiddenField.dom.value = this.value;
13383         }
13384         
13385         this.store.fireEvent("datachanged", this.store);
13386     },
13387     
13388     clearItem : function()
13389     {
13390         if(!this.multiple){
13391             return;
13392         }
13393         
13394         this.item = [];
13395         
13396         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13397            c.remove();
13398         });
13399         
13400         this.syncValue();
13401         
13402         this.validate();
13403         
13404         if(this.tickable && !Roo.isTouch){
13405             this.view.refresh();
13406         }
13407     },
13408     
13409     inputEl: function ()
13410     {
13411         if(Roo.isTouch && this.mobileTouchView){
13412             return this.el.select('input.form-control',true).first();
13413         }
13414         
13415         if(this.tickable){
13416             return this.searchField;
13417         }
13418         
13419         return this.el.select('input.form-control',true).first();
13420     },
13421     
13422     
13423     onTickableFooterButtonClick : function(e, btn, el)
13424     {
13425         e.preventDefault();
13426         
13427         this.lastItem = Roo.apply([], this.item);
13428         
13429         if(btn && btn.name == 'cancel'){
13430             this.tickItems = Roo.apply([], this.item);
13431             this.collapse();
13432             return;
13433         }
13434         
13435         this.clearItem();
13436         
13437         var _this = this;
13438         
13439         Roo.each(this.tickItems, function(o){
13440             _this.addItem(o);
13441         });
13442         
13443         this.collapse();
13444         
13445     },
13446     
13447     validate : function()
13448     {
13449         var v = this.getRawValue();
13450         
13451         if(this.multiple){
13452             v = this.getValue();
13453         }
13454         
13455         if(this.disabled || this.allowBlank || v.length){
13456             this.markValid();
13457             return true;
13458         }
13459         
13460         this.markInvalid();
13461         return false;
13462     },
13463     
13464     tickableInputEl : function()
13465     {
13466         if(!this.tickable || !this.editable){
13467             return this.inputEl();
13468         }
13469         
13470         return this.inputEl().select('.select2-search-field-input', true).first();
13471     },
13472     
13473     
13474     getAutoCreateTouchView : function()
13475     {
13476         var id = Roo.id();
13477         
13478         var cfg = {
13479             cls: 'form-group' //input-group
13480         };
13481         
13482         var input =  {
13483             tag: 'input',
13484             id : id,
13485             type : this.inputType,
13486             cls : 'form-control x-combo-noedit',
13487             autocomplete: 'new-password',
13488             placeholder : this.placeholder || '',
13489             readonly : true
13490         };
13491         
13492         if (this.name) {
13493             input.name = this.name;
13494         }
13495         
13496         if (this.size) {
13497             input.cls += ' input-' + this.size;
13498         }
13499         
13500         if (this.disabled) {
13501             input.disabled = true;
13502         }
13503         
13504         var inputblock = {
13505             cls : '',
13506             cn : [
13507                 input
13508             ]
13509         };
13510         
13511         if(this.before){
13512             inputblock.cls += ' input-group';
13513             
13514             inputblock.cn.unshift({
13515                 tag :'span',
13516                 cls : 'input-group-addon',
13517                 html : this.before
13518             });
13519         }
13520         
13521         if(this.removable && !this.multiple){
13522             inputblock.cls += ' roo-removable';
13523             
13524             inputblock.cn.push({
13525                 tag: 'button',
13526                 html : 'x',
13527                 cls : 'roo-combo-removable-btn close'
13528             });
13529         }
13530
13531         if(this.hasFeedback && !this.allowBlank){
13532             
13533             inputblock.cls += ' has-feedback';
13534             
13535             inputblock.cn.push({
13536                 tag: 'span',
13537                 cls: 'glyphicon form-control-feedback'
13538             });
13539             
13540         }
13541         
13542         if (this.after) {
13543             
13544             inputblock.cls += (this.before) ? '' : ' input-group';
13545             
13546             inputblock.cn.push({
13547                 tag :'span',
13548                 cls : 'input-group-addon',
13549                 html : this.after
13550             });
13551         }
13552
13553         var box = {
13554             tag: 'div',
13555             cn: [
13556                 {
13557                     tag: 'input',
13558                     type : 'hidden',
13559                     cls: 'form-hidden-field'
13560                 },
13561                 inputblock
13562             ]
13563             
13564         };
13565         
13566         if(this.multiple){
13567             box = {
13568                 tag: 'div',
13569                 cn: [
13570                     {
13571                         tag: 'input',
13572                         type : 'hidden',
13573                         cls: 'form-hidden-field'
13574                     },
13575                     {
13576                         tag: 'ul',
13577                         cls: 'select2-choices',
13578                         cn:[
13579                             {
13580                                 tag: 'li',
13581                                 cls: 'select2-search-field',
13582                                 cn: [
13583
13584                                     inputblock
13585                                 ]
13586                             }
13587                         ]
13588                     }
13589                 ]
13590             }
13591         };
13592         
13593         var combobox = {
13594             cls: 'select2-container input-group',
13595             cn: [
13596                 box
13597             ]
13598         };
13599         
13600         if(this.multiple){
13601             combobox.cls += ' select2-container-multi';
13602         }
13603         
13604         var align = this.labelAlign || this.parentLabelAlign();
13605         
13606         cfg.cn = combobox;
13607         
13608         if(this.fieldLabel.length){
13609             
13610             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13611             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13612             
13613             cfg.cn = [
13614                 {
13615                     tag: 'label',
13616                     cls : 'control-label ' + lw,
13617                     html : this.fieldLabel
13618
13619                 },
13620                 {
13621                     cls : cw, 
13622                     cn: [
13623                         combobox
13624                     ]
13625                 }
13626             ];
13627         }
13628         
13629         var settings = this;
13630         
13631         ['xs','sm','md','lg'].map(function(size){
13632             if (settings[size]) {
13633                 cfg.cls += ' col-' + size + '-' + settings[size];
13634             }
13635         });
13636         
13637         return cfg;
13638     },
13639     
13640     initTouchView : function()
13641     {
13642         this.renderTouchView();
13643         
13644         this.touchViewEl.on('scroll', function(){
13645             this.el.dom.scrollTop = 0;
13646         }, this);
13647         
13648         this.originalValue = this.getValue();
13649         
13650         this.inputEl().on("click", this.showTouchView, this);
13651         
13652         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13653         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13654         
13655         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13656         
13657         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13658         this.store.on('load', this.onTouchViewLoad, this);
13659         this.store.on('loadexception', this.onTouchViewLoadException, this);
13660         
13661         if(this.hiddenName){
13662             
13663             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13664             
13665             this.hiddenField.dom.value =
13666                 this.hiddenValue !== undefined ? this.hiddenValue :
13667                 this.value !== undefined ? this.value : '';
13668         
13669             this.el.dom.removeAttribute('name');
13670             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13671         }
13672         
13673         if(this.multiple){
13674             this.choices = this.el.select('ul.select2-choices', true).first();
13675             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13676         }
13677         
13678         if(this.removable && !this.multiple){
13679             var close = this.closeTriggerEl();
13680             if(close){
13681                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13682                 close.on('click', this.removeBtnClick, this, close);
13683             }
13684         }
13685         /*
13686          * fix the bug in Safari iOS8
13687          */
13688         this.inputEl().on("focus", function(e){
13689             document.activeElement.blur();
13690         }, this);
13691         
13692         return;
13693         
13694         
13695     },
13696     
13697     renderTouchView : function()
13698     {
13699         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13700         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13701         
13702         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13703         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13704         
13705         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13706         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13707         this.touchViewBodyEl.setStyle('overflow', 'auto');
13708         
13709         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13710         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13711         
13712         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13713         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13714         
13715     },
13716     
13717     showTouchView : function()
13718     {
13719         if(this.disabled){
13720             return;
13721         }
13722         
13723         this.touchViewHeaderEl.hide();
13724
13725         if(this.fieldLabel.length){
13726             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13727             this.touchViewHeaderEl.show();
13728         }
13729
13730         this.touchViewEl.show();
13731
13732         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13733         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13734
13735         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13736
13737         if(this.fieldLabel.length){
13738             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13739         }
13740         
13741         this.touchViewBodyEl.setHeight(bodyHeight);
13742
13743         if(this.animate){
13744             var _this = this;
13745             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13746         }else{
13747             this.touchViewEl.addClass('in');
13748         }
13749
13750         this.doTouchViewQuery();
13751         
13752     },
13753     
13754     hideTouchView : function()
13755     {
13756         this.touchViewEl.removeClass('in');
13757
13758         if(this.animate){
13759             var _this = this;
13760             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13761         }else{
13762             this.touchViewEl.setStyle('display', 'none');
13763         }
13764         
13765     },
13766     
13767     setTouchViewValue : function()
13768     {
13769         if(this.multiple){
13770             this.clearItem();
13771         
13772             var _this = this;
13773
13774             Roo.each(this.tickItems, function(o){
13775                 this.addItem(o);
13776             }, this);
13777         }
13778         
13779         this.hideTouchView();
13780     },
13781     
13782     doTouchViewQuery : function()
13783     {
13784         var qe = {
13785             query: '',
13786             forceAll: true,
13787             combo: this,
13788             cancel:false
13789         };
13790         
13791         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13792             return false;
13793         }
13794         
13795         if(!this.alwaysQuery || this.mode == 'local'){
13796             this.onTouchViewLoad();
13797             return;
13798         }
13799         
13800         this.store.load();
13801     },
13802     
13803     onTouchViewBeforeLoad : function(combo,opts)
13804     {
13805         return;
13806     },
13807
13808     // private
13809     onTouchViewLoad : function()
13810     {
13811         if(this.store.getCount() < 1){
13812             this.onTouchViewEmptyResults();
13813             return;
13814         }
13815         
13816         this.clearTouchView();
13817         
13818         var rawValue = this.getRawValue();
13819         
13820         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13821         
13822         this.tickItems = [];
13823         
13824         this.store.data.each(function(d, rowIndex){
13825             var row = this.touchViewListGroup.createChild(template);
13826             
13827             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13828                 var cfg = {
13829                     data : d.data,
13830                     html : d.data[this.displayField]
13831                 };
13832                 
13833                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13834                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13835                 }
13836             }
13837             
13838             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13839                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13840             }
13841             
13842             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13843                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13844                 this.tickItems.push(d.data);
13845             }
13846             
13847             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13848             
13849         }, this);
13850         
13851         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13852         
13853         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13854
13855         if(this.fieldLabel.length){
13856             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13857         }
13858
13859         var listHeight = this.touchViewListGroup.getHeight();
13860         
13861         var _this = this;
13862         
13863         if(firstChecked && listHeight > bodyHeight){
13864             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13865         }
13866         
13867     },
13868     
13869     onTouchViewLoadException : function()
13870     {
13871         this.hideTouchView();
13872     },
13873     
13874     onTouchViewEmptyResults : function()
13875     {
13876         this.clearTouchView();
13877         
13878         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13879         
13880         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13881         
13882     },
13883     
13884     clearTouchView : function()
13885     {
13886         this.touchViewListGroup.dom.innerHTML = '';
13887     },
13888     
13889     onTouchViewClick : function(e, el, o)
13890     {
13891         e.preventDefault();
13892         
13893         var row = o.row;
13894         var rowIndex = o.rowIndex;
13895         
13896         var r = this.store.getAt(rowIndex);
13897         
13898         if(!this.multiple){
13899             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13900                 c.dom.removeAttribute('checked');
13901             }, this);
13902             
13903             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13904         
13905             this.setFromData(r.data);
13906             
13907             var close = this.closeTriggerEl();
13908         
13909             if(close){
13910                 close.show();
13911             }
13912
13913             this.hideTouchView();
13914             
13915             this.fireEvent('select', this, r, rowIndex);
13916             
13917             return;
13918         }
13919         
13920         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13921             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13922             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13923             return;
13924         }
13925         
13926         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13927         this.addItem(r.data);
13928         this.tickItems.push(r.data);
13929         
13930     }
13931     
13932
13933     /** 
13934     * @cfg {Boolean} grow 
13935     * @hide 
13936     */
13937     /** 
13938     * @cfg {Number} growMin 
13939     * @hide 
13940     */
13941     /** 
13942     * @cfg {Number} growMax 
13943     * @hide 
13944     */
13945     /**
13946      * @hide
13947      * @method autoSize
13948      */
13949 });
13950
13951 Roo.apply(Roo.bootstrap.ComboBox,  {
13952     
13953     header : {
13954         tag: 'div',
13955         cls: 'modal-header',
13956         cn: [
13957             {
13958                 tag: 'h4',
13959                 cls: 'modal-title'
13960             }
13961         ]
13962     },
13963     
13964     body : {
13965         tag: 'div',
13966         cls: 'modal-body',
13967         cn: [
13968             {
13969                 tag: 'ul',
13970                 cls: 'list-group'
13971             }
13972         ]
13973     },
13974     
13975     listItemRadio : {
13976         tag: 'li',
13977         cls: 'list-group-item',
13978         cn: [
13979             {
13980                 tag: 'span',
13981                 cls: 'roo-combobox-list-group-item-value'
13982             },
13983             {
13984                 tag: 'div',
13985                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13986                 cn: [
13987                     {
13988                         tag: 'input',
13989                         type: 'radio'
13990                     },
13991                     {
13992                         tag: 'label'
13993                     }
13994                 ]
13995             }
13996         ]
13997     },
13998     
13999     listItemCheckbox : {
14000         tag: 'li',
14001         cls: 'list-group-item',
14002         cn: [
14003             {
14004                 tag: 'span',
14005                 cls: 'roo-combobox-list-group-item-value'
14006             },
14007             {
14008                 tag: 'div',
14009                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14010                 cn: [
14011                     {
14012                         tag: 'input',
14013                         type: 'checkbox'
14014                     },
14015                     {
14016                         tag: 'label'
14017                     }
14018                 ]
14019             }
14020         ]
14021     },
14022     
14023     emptyResult : {
14024         tag: 'div',
14025         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14026     },
14027     
14028     footer : {
14029         tag: 'div',
14030         cls: 'modal-footer',
14031         cn: [
14032             {
14033                 tag: 'div',
14034                 cls: 'row',
14035                 cn: [
14036                     {
14037                         tag: 'div',
14038                         cls: 'col-xs-6 text-left',
14039                         cn: {
14040                             tag: 'button',
14041                             cls: 'btn btn-danger roo-touch-view-cancel',
14042                             html: 'Cancel'
14043                         }
14044                     },
14045                     {
14046                         tag: 'div',
14047                         cls: 'col-xs-6 text-right',
14048                         cn: {
14049                             tag: 'button',
14050                             cls: 'btn btn-success roo-touch-view-ok',
14051                             html: 'OK'
14052                         }
14053                     }
14054                 ]
14055             }
14056         ]
14057         
14058     }
14059 });
14060
14061 Roo.apply(Roo.bootstrap.ComboBox,  {
14062     
14063     touchViewTemplate : {
14064         tag: 'div',
14065         cls: 'modal fade roo-combobox-touch-view',
14066         cn: [
14067             {
14068                 tag: 'div',
14069                 cls: 'modal-dialog',
14070                 style : 'position:fixed', // we have to fix position....
14071                 cn: [
14072                     {
14073                         tag: 'div',
14074                         cls: 'modal-content',
14075                         cn: [
14076                             Roo.bootstrap.ComboBox.header,
14077                             Roo.bootstrap.ComboBox.body,
14078                             Roo.bootstrap.ComboBox.footer
14079                         ]
14080                     }
14081                 ]
14082             }
14083         ]
14084     }
14085 });/*
14086  * Based on:
14087  * Ext JS Library 1.1.1
14088  * Copyright(c) 2006-2007, Ext JS, LLC.
14089  *
14090  * Originally Released Under LGPL - original licence link has changed is not relivant.
14091  *
14092  * Fork - LGPL
14093  * <script type="text/javascript">
14094  */
14095
14096 /**
14097  * @class Roo.View
14098  * @extends Roo.util.Observable
14099  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14100  * This class also supports single and multi selection modes. <br>
14101  * Create a data model bound view:
14102  <pre><code>
14103  var store = new Roo.data.Store(...);
14104
14105  var view = new Roo.View({
14106     el : "my-element",
14107     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14108  
14109     singleSelect: true,
14110     selectedClass: "ydataview-selected",
14111     store: store
14112  });
14113
14114  // listen for node click?
14115  view.on("click", function(vw, index, node, e){
14116  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14117  });
14118
14119  // load XML data
14120  dataModel.load("foobar.xml");
14121  </code></pre>
14122  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14123  * <br><br>
14124  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14125  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14126  * 
14127  * Note: old style constructor is still suported (container, template, config)
14128  * 
14129  * @constructor
14130  * Create a new View
14131  * @param {Object} config The config object
14132  * 
14133  */
14134 Roo.View = function(config, depreciated_tpl, depreciated_config){
14135     
14136     this.parent = false;
14137     
14138     if (typeof(depreciated_tpl) == 'undefined') {
14139         // new way.. - universal constructor.
14140         Roo.apply(this, config);
14141         this.el  = Roo.get(this.el);
14142     } else {
14143         // old format..
14144         this.el  = Roo.get(config);
14145         this.tpl = depreciated_tpl;
14146         Roo.apply(this, depreciated_config);
14147     }
14148     this.wrapEl  = this.el.wrap().wrap();
14149     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14150     
14151     
14152     if(typeof(this.tpl) == "string"){
14153         this.tpl = new Roo.Template(this.tpl);
14154     } else {
14155         // support xtype ctors..
14156         this.tpl = new Roo.factory(this.tpl, Roo);
14157     }
14158     
14159     
14160     this.tpl.compile();
14161     
14162     /** @private */
14163     this.addEvents({
14164         /**
14165          * @event beforeclick
14166          * Fires before a click is processed. Returns false to cancel the default action.
14167          * @param {Roo.View} this
14168          * @param {Number} index The index of the target node
14169          * @param {HTMLElement} node The target node
14170          * @param {Roo.EventObject} e The raw event object
14171          */
14172             "beforeclick" : true,
14173         /**
14174          * @event click
14175          * Fires when a template node is clicked.
14176          * @param {Roo.View} this
14177          * @param {Number} index The index of the target node
14178          * @param {HTMLElement} node The target node
14179          * @param {Roo.EventObject} e The raw event object
14180          */
14181             "click" : true,
14182         /**
14183          * @event dblclick
14184          * Fires when a template node is double clicked.
14185          * @param {Roo.View} this
14186          * @param {Number} index The index of the target node
14187          * @param {HTMLElement} node The target node
14188          * @param {Roo.EventObject} e The raw event object
14189          */
14190             "dblclick" : true,
14191         /**
14192          * @event contextmenu
14193          * Fires when a template node is right clicked.
14194          * @param {Roo.View} this
14195          * @param {Number} index The index of the target node
14196          * @param {HTMLElement} node The target node
14197          * @param {Roo.EventObject} e The raw event object
14198          */
14199             "contextmenu" : true,
14200         /**
14201          * @event selectionchange
14202          * Fires when the selected nodes change.
14203          * @param {Roo.View} this
14204          * @param {Array} selections Array of the selected nodes
14205          */
14206             "selectionchange" : true,
14207     
14208         /**
14209          * @event beforeselect
14210          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14211          * @param {Roo.View} this
14212          * @param {HTMLElement} node The node to be selected
14213          * @param {Array} selections Array of currently selected nodes
14214          */
14215             "beforeselect" : true,
14216         /**
14217          * @event preparedata
14218          * Fires on every row to render, to allow you to change the data.
14219          * @param {Roo.View} this
14220          * @param {Object} data to be rendered (change this)
14221          */
14222           "preparedata" : true
14223           
14224           
14225         });
14226
14227
14228
14229     this.el.on({
14230         "click": this.onClick,
14231         "dblclick": this.onDblClick,
14232         "contextmenu": this.onContextMenu,
14233         scope:this
14234     });
14235
14236     this.selections = [];
14237     this.nodes = [];
14238     this.cmp = new Roo.CompositeElementLite([]);
14239     if(this.store){
14240         this.store = Roo.factory(this.store, Roo.data);
14241         this.setStore(this.store, true);
14242     }
14243     
14244     if ( this.footer && this.footer.xtype) {
14245            
14246          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14247         
14248         this.footer.dataSource = this.store;
14249         this.footer.container = fctr;
14250         this.footer = Roo.factory(this.footer, Roo);
14251         fctr.insertFirst(this.el);
14252         
14253         // this is a bit insane - as the paging toolbar seems to detach the el..
14254 //        dom.parentNode.parentNode.parentNode
14255          // they get detached?
14256     }
14257     
14258     
14259     Roo.View.superclass.constructor.call(this);
14260     
14261     
14262 };
14263
14264 Roo.extend(Roo.View, Roo.util.Observable, {
14265     
14266      /**
14267      * @cfg {Roo.data.Store} store Data store to load data from.
14268      */
14269     store : false,
14270     
14271     /**
14272      * @cfg {String|Roo.Element} el The container element.
14273      */
14274     el : '',
14275     
14276     /**
14277      * @cfg {String|Roo.Template} tpl The template used by this View 
14278      */
14279     tpl : false,
14280     /**
14281      * @cfg {String} dataName the named area of the template to use as the data area
14282      *                          Works with domtemplates roo-name="name"
14283      */
14284     dataName: false,
14285     /**
14286      * @cfg {String} selectedClass The css class to add to selected nodes
14287      */
14288     selectedClass : "x-view-selected",
14289      /**
14290      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14291      */
14292     emptyText : "",
14293     
14294     /**
14295      * @cfg {String} text to display on mask (default Loading)
14296      */
14297     mask : false,
14298     /**
14299      * @cfg {Boolean} multiSelect Allow multiple selection
14300      */
14301     multiSelect : false,
14302     /**
14303      * @cfg {Boolean} singleSelect Allow single selection
14304      */
14305     singleSelect:  false,
14306     
14307     /**
14308      * @cfg {Boolean} toggleSelect - selecting 
14309      */
14310     toggleSelect : false,
14311     
14312     /**
14313      * @cfg {Boolean} tickable - selecting 
14314      */
14315     tickable : false,
14316     
14317     /**
14318      * Returns the element this view is bound to.
14319      * @return {Roo.Element}
14320      */
14321     getEl : function(){
14322         return this.wrapEl;
14323     },
14324     
14325     
14326
14327     /**
14328      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14329      */
14330     refresh : function(){
14331         //Roo.log('refresh');
14332         var t = this.tpl;
14333         
14334         // if we are using something like 'domtemplate', then
14335         // the what gets used is:
14336         // t.applySubtemplate(NAME, data, wrapping data..)
14337         // the outer template then get' applied with
14338         //     the store 'extra data'
14339         // and the body get's added to the
14340         //      roo-name="data" node?
14341         //      <span class='roo-tpl-{name}'></span> ?????
14342         
14343         
14344         
14345         this.clearSelections();
14346         this.el.update("");
14347         var html = [];
14348         var records = this.store.getRange();
14349         if(records.length < 1) {
14350             
14351             // is this valid??  = should it render a template??
14352             
14353             this.el.update(this.emptyText);
14354             return;
14355         }
14356         var el = this.el;
14357         if (this.dataName) {
14358             this.el.update(t.apply(this.store.meta)); //????
14359             el = this.el.child('.roo-tpl-' + this.dataName);
14360         }
14361         
14362         for(var i = 0, len = records.length; i < len; i++){
14363             var data = this.prepareData(records[i].data, i, records[i]);
14364             this.fireEvent("preparedata", this, data, i, records[i]);
14365             
14366             var d = Roo.apply({}, data);
14367             
14368             if(this.tickable){
14369                 Roo.apply(d, {'roo-id' : Roo.id()});
14370                 
14371                 var _this = this;
14372             
14373                 Roo.each(this.parent.item, function(item){
14374                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14375                         return;
14376                     }
14377                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14378                 });
14379             }
14380             
14381             html[html.length] = Roo.util.Format.trim(
14382                 this.dataName ?
14383                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14384                     t.apply(d)
14385             );
14386         }
14387         
14388         
14389         
14390         el.update(html.join(""));
14391         this.nodes = el.dom.childNodes;
14392         this.updateIndexes(0);
14393     },
14394     
14395
14396     /**
14397      * Function to override to reformat the data that is sent to
14398      * the template for each node.
14399      * DEPRICATED - use the preparedata event handler.
14400      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14401      * a JSON object for an UpdateManager bound view).
14402      */
14403     prepareData : function(data, index, record)
14404     {
14405         this.fireEvent("preparedata", this, data, index, record);
14406         return data;
14407     },
14408
14409     onUpdate : function(ds, record){
14410         // Roo.log('on update');   
14411         this.clearSelections();
14412         var index = this.store.indexOf(record);
14413         var n = this.nodes[index];
14414         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14415         n.parentNode.removeChild(n);
14416         this.updateIndexes(index, index);
14417     },
14418
14419     
14420     
14421 // --------- FIXME     
14422     onAdd : function(ds, records, index)
14423     {
14424         //Roo.log(['on Add', ds, records, index] );        
14425         this.clearSelections();
14426         if(this.nodes.length == 0){
14427             this.refresh();
14428             return;
14429         }
14430         var n = this.nodes[index];
14431         for(var i = 0, len = records.length; i < len; i++){
14432             var d = this.prepareData(records[i].data, i, records[i]);
14433             if(n){
14434                 this.tpl.insertBefore(n, d);
14435             }else{
14436                 
14437                 this.tpl.append(this.el, d);
14438             }
14439         }
14440         this.updateIndexes(index);
14441     },
14442
14443     onRemove : function(ds, record, index){
14444        // Roo.log('onRemove');
14445         this.clearSelections();
14446         var el = this.dataName  ?
14447             this.el.child('.roo-tpl-' + this.dataName) :
14448             this.el; 
14449         
14450         el.dom.removeChild(this.nodes[index]);
14451         this.updateIndexes(index);
14452     },
14453
14454     /**
14455      * Refresh an individual node.
14456      * @param {Number} index
14457      */
14458     refreshNode : function(index){
14459         this.onUpdate(this.store, this.store.getAt(index));
14460     },
14461
14462     updateIndexes : function(startIndex, endIndex){
14463         var ns = this.nodes;
14464         startIndex = startIndex || 0;
14465         endIndex = endIndex || ns.length - 1;
14466         for(var i = startIndex; i <= endIndex; i++){
14467             ns[i].nodeIndex = i;
14468         }
14469     },
14470
14471     /**
14472      * Changes the data store this view uses and refresh the view.
14473      * @param {Store} store
14474      */
14475     setStore : function(store, initial){
14476         if(!initial && this.store){
14477             this.store.un("datachanged", this.refresh);
14478             this.store.un("add", this.onAdd);
14479             this.store.un("remove", this.onRemove);
14480             this.store.un("update", this.onUpdate);
14481             this.store.un("clear", this.refresh);
14482             this.store.un("beforeload", this.onBeforeLoad);
14483             this.store.un("load", this.onLoad);
14484             this.store.un("loadexception", this.onLoad);
14485         }
14486         if(store){
14487           
14488             store.on("datachanged", this.refresh, this);
14489             store.on("add", this.onAdd, this);
14490             store.on("remove", this.onRemove, this);
14491             store.on("update", this.onUpdate, this);
14492             store.on("clear", this.refresh, this);
14493             store.on("beforeload", this.onBeforeLoad, this);
14494             store.on("load", this.onLoad, this);
14495             store.on("loadexception", this.onLoad, this);
14496         }
14497         
14498         if(store){
14499             this.refresh();
14500         }
14501     },
14502     /**
14503      * onbeforeLoad - masks the loading area.
14504      *
14505      */
14506     onBeforeLoad : function(store,opts)
14507     {
14508          //Roo.log('onBeforeLoad');   
14509         if (!opts.add) {
14510             this.el.update("");
14511         }
14512         this.el.mask(this.mask ? this.mask : "Loading" ); 
14513     },
14514     onLoad : function ()
14515     {
14516         this.el.unmask();
14517     },
14518     
14519
14520     /**
14521      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14522      * @param {HTMLElement} node
14523      * @return {HTMLElement} The template node
14524      */
14525     findItemFromChild : function(node){
14526         var el = this.dataName  ?
14527             this.el.child('.roo-tpl-' + this.dataName,true) :
14528             this.el.dom; 
14529         
14530         if(!node || node.parentNode == el){
14531                     return node;
14532             }
14533             var p = node.parentNode;
14534             while(p && p != el){
14535             if(p.parentNode == el){
14536                 return p;
14537             }
14538             p = p.parentNode;
14539         }
14540             return null;
14541     },
14542
14543     /** @ignore */
14544     onClick : function(e){
14545         var item = this.findItemFromChild(e.getTarget());
14546         if(item){
14547             var index = this.indexOf(item);
14548             if(this.onItemClick(item, index, e) !== false){
14549                 this.fireEvent("click", this, index, item, e);
14550             }
14551         }else{
14552             this.clearSelections();
14553         }
14554     },
14555
14556     /** @ignore */
14557     onContextMenu : function(e){
14558         var item = this.findItemFromChild(e.getTarget());
14559         if(item){
14560             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14561         }
14562     },
14563
14564     /** @ignore */
14565     onDblClick : function(e){
14566         var item = this.findItemFromChild(e.getTarget());
14567         if(item){
14568             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14569         }
14570     },
14571
14572     onItemClick : function(item, index, e)
14573     {
14574         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14575             return false;
14576         }
14577         if (this.toggleSelect) {
14578             var m = this.isSelected(item) ? 'unselect' : 'select';
14579             //Roo.log(m);
14580             var _t = this;
14581             _t[m](item, true, false);
14582             return true;
14583         }
14584         if(this.multiSelect || this.singleSelect){
14585             if(this.multiSelect && e.shiftKey && this.lastSelection){
14586                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14587             }else{
14588                 this.select(item, this.multiSelect && e.ctrlKey);
14589                 this.lastSelection = item;
14590             }
14591             
14592             if(!this.tickable){
14593                 e.preventDefault();
14594             }
14595             
14596         }
14597         return true;
14598     },
14599
14600     /**
14601      * Get the number of selected nodes.
14602      * @return {Number}
14603      */
14604     getSelectionCount : function(){
14605         return this.selections.length;
14606     },
14607
14608     /**
14609      * Get the currently selected nodes.
14610      * @return {Array} An array of HTMLElements
14611      */
14612     getSelectedNodes : function(){
14613         return this.selections;
14614     },
14615
14616     /**
14617      * Get the indexes of the selected nodes.
14618      * @return {Array}
14619      */
14620     getSelectedIndexes : function(){
14621         var indexes = [], s = this.selections;
14622         for(var i = 0, len = s.length; i < len; i++){
14623             indexes.push(s[i].nodeIndex);
14624         }
14625         return indexes;
14626     },
14627
14628     /**
14629      * Clear all selections
14630      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14631      */
14632     clearSelections : function(suppressEvent){
14633         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14634             this.cmp.elements = this.selections;
14635             this.cmp.removeClass(this.selectedClass);
14636             this.selections = [];
14637             if(!suppressEvent){
14638                 this.fireEvent("selectionchange", this, this.selections);
14639             }
14640         }
14641     },
14642
14643     /**
14644      * Returns true if the passed node is selected
14645      * @param {HTMLElement/Number} node The node or node index
14646      * @return {Boolean}
14647      */
14648     isSelected : function(node){
14649         var s = this.selections;
14650         if(s.length < 1){
14651             return false;
14652         }
14653         node = this.getNode(node);
14654         return s.indexOf(node) !== -1;
14655     },
14656
14657     /**
14658      * Selects nodes.
14659      * @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
14660      * @param {Boolean} keepExisting (optional) true to keep existing selections
14661      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14662      */
14663     select : function(nodeInfo, keepExisting, suppressEvent){
14664         if(nodeInfo instanceof Array){
14665             if(!keepExisting){
14666                 this.clearSelections(true);
14667             }
14668             for(var i = 0, len = nodeInfo.length; i < len; i++){
14669                 this.select(nodeInfo[i], true, true);
14670             }
14671             return;
14672         } 
14673         var node = this.getNode(nodeInfo);
14674         if(!node || this.isSelected(node)){
14675             return; // already selected.
14676         }
14677         if(!keepExisting){
14678             this.clearSelections(true);
14679         }
14680         
14681         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14682             Roo.fly(node).addClass(this.selectedClass);
14683             this.selections.push(node);
14684             if(!suppressEvent){
14685                 this.fireEvent("selectionchange", this, this.selections);
14686             }
14687         }
14688         
14689         
14690     },
14691       /**
14692      * Unselects nodes.
14693      * @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
14694      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14695      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14696      */
14697     unselect : function(nodeInfo, keepExisting, suppressEvent)
14698     {
14699         if(nodeInfo instanceof Array){
14700             Roo.each(this.selections, function(s) {
14701                 this.unselect(s, nodeInfo);
14702             }, this);
14703             return;
14704         }
14705         var node = this.getNode(nodeInfo);
14706         if(!node || !this.isSelected(node)){
14707             //Roo.log("not selected");
14708             return; // not selected.
14709         }
14710         // fireevent???
14711         var ns = [];
14712         Roo.each(this.selections, function(s) {
14713             if (s == node ) {
14714                 Roo.fly(node).removeClass(this.selectedClass);
14715
14716                 return;
14717             }
14718             ns.push(s);
14719         },this);
14720         
14721         this.selections= ns;
14722         this.fireEvent("selectionchange", this, this.selections);
14723     },
14724
14725     /**
14726      * Gets a template node.
14727      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14728      * @return {HTMLElement} The node or null if it wasn't found
14729      */
14730     getNode : function(nodeInfo){
14731         if(typeof nodeInfo == "string"){
14732             return document.getElementById(nodeInfo);
14733         }else if(typeof nodeInfo == "number"){
14734             return this.nodes[nodeInfo];
14735         }
14736         return nodeInfo;
14737     },
14738
14739     /**
14740      * Gets a range template nodes.
14741      * @param {Number} startIndex
14742      * @param {Number} endIndex
14743      * @return {Array} An array of nodes
14744      */
14745     getNodes : function(start, end){
14746         var ns = this.nodes;
14747         start = start || 0;
14748         end = typeof end == "undefined" ? ns.length - 1 : end;
14749         var nodes = [];
14750         if(start <= end){
14751             for(var i = start; i <= end; i++){
14752                 nodes.push(ns[i]);
14753             }
14754         } else{
14755             for(var i = start; i >= end; i--){
14756                 nodes.push(ns[i]);
14757             }
14758         }
14759         return nodes;
14760     },
14761
14762     /**
14763      * Finds the index of the passed node
14764      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14765      * @return {Number} The index of the node or -1
14766      */
14767     indexOf : function(node){
14768         node = this.getNode(node);
14769         if(typeof node.nodeIndex == "number"){
14770             return node.nodeIndex;
14771         }
14772         var ns = this.nodes;
14773         for(var i = 0, len = ns.length; i < len; i++){
14774             if(ns[i] == node){
14775                 return i;
14776             }
14777         }
14778         return -1;
14779     }
14780 });
14781 /*
14782  * - LGPL
14783  *
14784  * based on jquery fullcalendar
14785  * 
14786  */
14787
14788 Roo.bootstrap = Roo.bootstrap || {};
14789 /**
14790  * @class Roo.bootstrap.Calendar
14791  * @extends Roo.bootstrap.Component
14792  * Bootstrap Calendar class
14793  * @cfg {Boolean} loadMask (true|false) default false
14794  * @cfg {Object} header generate the user specific header of the calendar, default false
14795
14796  * @constructor
14797  * Create a new Container
14798  * @param {Object} config The config object
14799  */
14800
14801
14802
14803 Roo.bootstrap.Calendar = function(config){
14804     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14805      this.addEvents({
14806         /**
14807              * @event select
14808              * Fires when a date is selected
14809              * @param {DatePicker} this
14810              * @param {Date} date The selected date
14811              */
14812         'select': true,
14813         /**
14814              * @event monthchange
14815              * Fires when the displayed month changes 
14816              * @param {DatePicker} this
14817              * @param {Date} date The selected month
14818              */
14819         'monthchange': true,
14820         /**
14821              * @event evententer
14822              * Fires when mouse over an event
14823              * @param {Calendar} this
14824              * @param {event} Event
14825              */
14826         'evententer': true,
14827         /**
14828              * @event eventleave
14829              * Fires when the mouse leaves an
14830              * @param {Calendar} this
14831              * @param {event}
14832              */
14833         'eventleave': true,
14834         /**
14835              * @event eventclick
14836              * Fires when the mouse click an
14837              * @param {Calendar} this
14838              * @param {event}
14839              */
14840         'eventclick': true
14841         
14842     });
14843
14844 };
14845
14846 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14847     
14848      /**
14849      * @cfg {Number} startDay
14850      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14851      */
14852     startDay : 0,
14853     
14854     loadMask : false,
14855     
14856     header : false,
14857       
14858     getAutoCreate : function(){
14859         
14860         
14861         var fc_button = function(name, corner, style, content ) {
14862             return Roo.apply({},{
14863                 tag : 'span',
14864                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14865                          (corner.length ?
14866                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14867                             ''
14868                         ),
14869                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14870                 unselectable: 'on'
14871             });
14872         };
14873         
14874         var header = {};
14875         
14876         if(!this.header){
14877             header = {
14878                 tag : 'table',
14879                 cls : 'fc-header',
14880                 style : 'width:100%',
14881                 cn : [
14882                     {
14883                         tag: 'tr',
14884                         cn : [
14885                             {
14886                                 tag : 'td',
14887                                 cls : 'fc-header-left',
14888                                 cn : [
14889                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14890                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14891                                     { tag: 'span', cls: 'fc-header-space' },
14892                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14893
14894
14895                                 ]
14896                             },
14897
14898                             {
14899                                 tag : 'td',
14900                                 cls : 'fc-header-center',
14901                                 cn : [
14902                                     {
14903                                         tag: 'span',
14904                                         cls: 'fc-header-title',
14905                                         cn : {
14906                                             tag: 'H2',
14907                                             html : 'month / year'
14908                                         }
14909                                     }
14910
14911                                 ]
14912                             },
14913                             {
14914                                 tag : 'td',
14915                                 cls : 'fc-header-right',
14916                                 cn : [
14917                               /*      fc_button('month', 'left', '', 'month' ),
14918                                     fc_button('week', '', '', 'week' ),
14919                                     fc_button('day', 'right', '', 'day' )
14920                                 */    
14921
14922                                 ]
14923                             }
14924
14925                         ]
14926                     }
14927                 ]
14928             };
14929         }
14930         
14931         header = this.header;
14932         
14933        
14934         var cal_heads = function() {
14935             var ret = [];
14936             // fixme - handle this.
14937             
14938             for (var i =0; i < Date.dayNames.length; i++) {
14939                 var d = Date.dayNames[i];
14940                 ret.push({
14941                     tag: 'th',
14942                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14943                     html : d.substring(0,3)
14944                 });
14945                 
14946             }
14947             ret[0].cls += ' fc-first';
14948             ret[6].cls += ' fc-last';
14949             return ret;
14950         };
14951         var cal_cell = function(n) {
14952             return  {
14953                 tag: 'td',
14954                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14955                 cn : [
14956                     {
14957                         cn : [
14958                             {
14959                                 cls: 'fc-day-number',
14960                                 html: 'D'
14961                             },
14962                             {
14963                                 cls: 'fc-day-content',
14964                              
14965                                 cn : [
14966                                      {
14967                                         style: 'position: relative;' // height: 17px;
14968                                     }
14969                                 ]
14970                             }
14971                             
14972                             
14973                         ]
14974                     }
14975                 ]
14976                 
14977             }
14978         };
14979         var cal_rows = function() {
14980             
14981             var ret = [];
14982             for (var r = 0; r < 6; r++) {
14983                 var row= {
14984                     tag : 'tr',
14985                     cls : 'fc-week',
14986                     cn : []
14987                 };
14988                 
14989                 for (var i =0; i < Date.dayNames.length; i++) {
14990                     var d = Date.dayNames[i];
14991                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14992
14993                 }
14994                 row.cn[0].cls+=' fc-first';
14995                 row.cn[0].cn[0].style = 'min-height:90px';
14996                 row.cn[6].cls+=' fc-last';
14997                 ret.push(row);
14998                 
14999             }
15000             ret[0].cls += ' fc-first';
15001             ret[4].cls += ' fc-prev-last';
15002             ret[5].cls += ' fc-last';
15003             return ret;
15004             
15005         };
15006         
15007         var cal_table = {
15008             tag: 'table',
15009             cls: 'fc-border-separate',
15010             style : 'width:100%',
15011             cellspacing  : 0,
15012             cn : [
15013                 { 
15014                     tag: 'thead',
15015                     cn : [
15016                         { 
15017                             tag: 'tr',
15018                             cls : 'fc-first fc-last',
15019                             cn : cal_heads()
15020                         }
15021                     ]
15022                 },
15023                 { 
15024                     tag: 'tbody',
15025                     cn : cal_rows()
15026                 }
15027                   
15028             ]
15029         };
15030          
15031          var cfg = {
15032             cls : 'fc fc-ltr',
15033             cn : [
15034                 header,
15035                 {
15036                     cls : 'fc-content',
15037                     style : "position: relative;",
15038                     cn : [
15039                         {
15040                             cls : 'fc-view fc-view-month fc-grid',
15041                             style : 'position: relative',
15042                             unselectable : 'on',
15043                             cn : [
15044                                 {
15045                                     cls : 'fc-event-container',
15046                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15047                                 },
15048                                 cal_table
15049                             ]
15050                         }
15051                     ]
15052     
15053                 }
15054            ] 
15055             
15056         };
15057         
15058          
15059         
15060         return cfg;
15061     },
15062     
15063     
15064     initEvents : function()
15065     {
15066         if(!this.store){
15067             throw "can not find store for calendar";
15068         }
15069         
15070         var mark = {
15071             tag: "div",
15072             cls:"x-dlg-mask",
15073             style: "text-align:center",
15074             cn: [
15075                 {
15076                     tag: "div",
15077                     style: "background-color:white;width:50%;margin:250 auto",
15078                     cn: [
15079                         {
15080                             tag: "img",
15081                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15082                         },
15083                         {
15084                             tag: "span",
15085                             html: "Loading"
15086                         }
15087                         
15088                     ]
15089                 }
15090             ]
15091         };
15092         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15093         
15094         var size = this.el.select('.fc-content', true).first().getSize();
15095         this.maskEl.setSize(size.width, size.height);
15096         this.maskEl.enableDisplayMode("block");
15097         if(!this.loadMask){
15098             this.maskEl.hide();
15099         }
15100         
15101         this.store = Roo.factory(this.store, Roo.data);
15102         this.store.on('load', this.onLoad, this);
15103         this.store.on('beforeload', this.onBeforeLoad, this);
15104         
15105         this.resize();
15106         
15107         this.cells = this.el.select('.fc-day',true);
15108         //Roo.log(this.cells);
15109         this.textNodes = this.el.query('.fc-day-number');
15110         this.cells.addClassOnOver('fc-state-hover');
15111         
15112         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15113         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15114         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15115         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15116         
15117         this.on('monthchange', this.onMonthChange, this);
15118         
15119         this.update(new Date().clearTime());
15120     },
15121     
15122     resize : function() {
15123         var sz  = this.el.getSize();
15124         
15125         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15126         this.el.select('.fc-day-content div',true).setHeight(34);
15127     },
15128     
15129     
15130     // private
15131     showPrevMonth : function(e){
15132         this.update(this.activeDate.add("mo", -1));
15133     },
15134     showToday : function(e){
15135         this.update(new Date().clearTime());
15136     },
15137     // private
15138     showNextMonth : function(e){
15139         this.update(this.activeDate.add("mo", 1));
15140     },
15141
15142     // private
15143     showPrevYear : function(){
15144         this.update(this.activeDate.add("y", -1));
15145     },
15146
15147     // private
15148     showNextYear : function(){
15149         this.update(this.activeDate.add("y", 1));
15150     },
15151
15152     
15153    // private
15154     update : function(date)
15155     {
15156         var vd = this.activeDate;
15157         this.activeDate = date;
15158 //        if(vd && this.el){
15159 //            var t = date.getTime();
15160 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15161 //                Roo.log('using add remove');
15162 //                
15163 //                this.fireEvent('monthchange', this, date);
15164 //                
15165 //                this.cells.removeClass("fc-state-highlight");
15166 //                this.cells.each(function(c){
15167 //                   if(c.dateValue == t){
15168 //                       c.addClass("fc-state-highlight");
15169 //                       setTimeout(function(){
15170 //                            try{c.dom.firstChild.focus();}catch(e){}
15171 //                       }, 50);
15172 //                       return false;
15173 //                   }
15174 //                   return true;
15175 //                });
15176 //                return;
15177 //            }
15178 //        }
15179         
15180         var days = date.getDaysInMonth();
15181         
15182         var firstOfMonth = date.getFirstDateOfMonth();
15183         var startingPos = firstOfMonth.getDay()-this.startDay;
15184         
15185         if(startingPos < this.startDay){
15186             startingPos += 7;
15187         }
15188         
15189         var pm = date.add(Date.MONTH, -1);
15190         var prevStart = pm.getDaysInMonth()-startingPos;
15191 //        
15192         this.cells = this.el.select('.fc-day',true);
15193         this.textNodes = this.el.query('.fc-day-number');
15194         this.cells.addClassOnOver('fc-state-hover');
15195         
15196         var cells = this.cells.elements;
15197         var textEls = this.textNodes;
15198         
15199         Roo.each(cells, function(cell){
15200             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15201         });
15202         
15203         days += startingPos;
15204
15205         // convert everything to numbers so it's fast
15206         var day = 86400000;
15207         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15208         //Roo.log(d);
15209         //Roo.log(pm);
15210         //Roo.log(prevStart);
15211         
15212         var today = new Date().clearTime().getTime();
15213         var sel = date.clearTime().getTime();
15214         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15215         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15216         var ddMatch = this.disabledDatesRE;
15217         var ddText = this.disabledDatesText;
15218         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15219         var ddaysText = this.disabledDaysText;
15220         var format = this.format;
15221         
15222         var setCellClass = function(cal, cell){
15223             cell.row = 0;
15224             cell.events = [];
15225             cell.more = [];
15226             //Roo.log('set Cell Class');
15227             cell.title = "";
15228             var t = d.getTime();
15229             
15230             //Roo.log(d);
15231             
15232             cell.dateValue = t;
15233             if(t == today){
15234                 cell.className += " fc-today";
15235                 cell.className += " fc-state-highlight";
15236                 cell.title = cal.todayText;
15237             }
15238             if(t == sel){
15239                 // disable highlight in other month..
15240                 //cell.className += " fc-state-highlight";
15241                 
15242             }
15243             // disabling
15244             if(t < min) {
15245                 cell.className = " fc-state-disabled";
15246                 cell.title = cal.minText;
15247                 return;
15248             }
15249             if(t > max) {
15250                 cell.className = " fc-state-disabled";
15251                 cell.title = cal.maxText;
15252                 return;
15253             }
15254             if(ddays){
15255                 if(ddays.indexOf(d.getDay()) != -1){
15256                     cell.title = ddaysText;
15257                     cell.className = " fc-state-disabled";
15258                 }
15259             }
15260             if(ddMatch && format){
15261                 var fvalue = d.dateFormat(format);
15262                 if(ddMatch.test(fvalue)){
15263                     cell.title = ddText.replace("%0", fvalue);
15264                     cell.className = " fc-state-disabled";
15265                 }
15266             }
15267             
15268             if (!cell.initialClassName) {
15269                 cell.initialClassName = cell.dom.className;
15270             }
15271             
15272             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15273         };
15274
15275         var i = 0;
15276         
15277         for(; i < startingPos; i++) {
15278             textEls[i].innerHTML = (++prevStart);
15279             d.setDate(d.getDate()+1);
15280             
15281             cells[i].className = "fc-past fc-other-month";
15282             setCellClass(this, cells[i]);
15283         }
15284         
15285         var intDay = 0;
15286         
15287         for(; i < days; i++){
15288             intDay = i - startingPos + 1;
15289             textEls[i].innerHTML = (intDay);
15290             d.setDate(d.getDate()+1);
15291             
15292             cells[i].className = ''; // "x-date-active";
15293             setCellClass(this, cells[i]);
15294         }
15295         var extraDays = 0;
15296         
15297         for(; i < 42; i++) {
15298             textEls[i].innerHTML = (++extraDays);
15299             d.setDate(d.getDate()+1);
15300             
15301             cells[i].className = "fc-future fc-other-month";
15302             setCellClass(this, cells[i]);
15303         }
15304         
15305         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15306         
15307         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15308         
15309         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15310         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15311         
15312         if(totalRows != 6){
15313             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15314             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15315         }
15316         
15317         this.fireEvent('monthchange', this, date);
15318         
15319         
15320         /*
15321         if(!this.internalRender){
15322             var main = this.el.dom.firstChild;
15323             var w = main.offsetWidth;
15324             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15325             Roo.fly(main).setWidth(w);
15326             this.internalRender = true;
15327             // opera does not respect the auto grow header center column
15328             // then, after it gets a width opera refuses to recalculate
15329             // without a second pass
15330             if(Roo.isOpera && !this.secondPass){
15331                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15332                 this.secondPass = true;
15333                 this.update.defer(10, this, [date]);
15334             }
15335         }
15336         */
15337         
15338     },
15339     
15340     findCell : function(dt) {
15341         dt = dt.clearTime().getTime();
15342         var ret = false;
15343         this.cells.each(function(c){
15344             //Roo.log("check " +c.dateValue + '?=' + dt);
15345             if(c.dateValue == dt){
15346                 ret = c;
15347                 return false;
15348             }
15349             return true;
15350         });
15351         
15352         return ret;
15353     },
15354     
15355     findCells : function(ev) {
15356         var s = ev.start.clone().clearTime().getTime();
15357        // Roo.log(s);
15358         var e= ev.end.clone().clearTime().getTime();
15359        // Roo.log(e);
15360         var ret = [];
15361         this.cells.each(function(c){
15362              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15363             
15364             if(c.dateValue > e){
15365                 return ;
15366             }
15367             if(c.dateValue < s){
15368                 return ;
15369             }
15370             ret.push(c);
15371         });
15372         
15373         return ret;    
15374     },
15375     
15376 //    findBestRow: function(cells)
15377 //    {
15378 //        var ret = 0;
15379 //        
15380 //        for (var i =0 ; i < cells.length;i++) {
15381 //            ret  = Math.max(cells[i].rows || 0,ret);
15382 //        }
15383 //        return ret;
15384 //        
15385 //    },
15386     
15387     
15388     addItem : function(ev)
15389     {
15390         // look for vertical location slot in
15391         var cells = this.findCells(ev);
15392         
15393 //        ev.row = this.findBestRow(cells);
15394         
15395         // work out the location.
15396         
15397         var crow = false;
15398         var rows = [];
15399         for(var i =0; i < cells.length; i++) {
15400             
15401             cells[i].row = cells[0].row;
15402             
15403             if(i == 0){
15404                 cells[i].row = cells[i].row + 1;
15405             }
15406             
15407             if (!crow) {
15408                 crow = {
15409                     start : cells[i],
15410                     end :  cells[i]
15411                 };
15412                 continue;
15413             }
15414             if (crow.start.getY() == cells[i].getY()) {
15415                 // on same row.
15416                 crow.end = cells[i];
15417                 continue;
15418             }
15419             // different row.
15420             rows.push(crow);
15421             crow = {
15422                 start: cells[i],
15423                 end : cells[i]
15424             };
15425             
15426         }
15427         
15428         rows.push(crow);
15429         ev.els = [];
15430         ev.rows = rows;
15431         ev.cells = cells;
15432         
15433         cells[0].events.push(ev);
15434         
15435         this.calevents.push(ev);
15436     },
15437     
15438     clearEvents: function() {
15439         
15440         if(!this.calevents){
15441             return;
15442         }
15443         
15444         Roo.each(this.cells.elements, function(c){
15445             c.row = 0;
15446             c.events = [];
15447             c.more = [];
15448         });
15449         
15450         Roo.each(this.calevents, function(e) {
15451             Roo.each(e.els, function(el) {
15452                 el.un('mouseenter' ,this.onEventEnter, this);
15453                 el.un('mouseleave' ,this.onEventLeave, this);
15454                 el.remove();
15455             },this);
15456         },this);
15457         
15458         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15459             e.remove();
15460         });
15461         
15462     },
15463     
15464     renderEvents: function()
15465     {   
15466         var _this = this;
15467         
15468         this.cells.each(function(c) {
15469             
15470             if(c.row < 5){
15471                 return;
15472             }
15473             
15474             var ev = c.events;
15475             
15476             var r = 4;
15477             if(c.row != c.events.length){
15478                 r = 4 - (4 - (c.row - c.events.length));
15479             }
15480             
15481             c.events = ev.slice(0, r);
15482             c.more = ev.slice(r);
15483             
15484             if(c.more.length && c.more.length == 1){
15485                 c.events.push(c.more.pop());
15486             }
15487             
15488             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15489             
15490         });
15491             
15492         this.cells.each(function(c) {
15493             
15494             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15495             
15496             
15497             for (var e = 0; e < c.events.length; e++){
15498                 var ev = c.events[e];
15499                 var rows = ev.rows;
15500                 
15501                 for(var i = 0; i < rows.length; i++) {
15502                 
15503                     // how many rows should it span..
15504
15505                     var  cfg = {
15506                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15507                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15508
15509                         unselectable : "on",
15510                         cn : [
15511                             {
15512                                 cls: 'fc-event-inner',
15513                                 cn : [
15514     //                                {
15515     //                                  tag:'span',
15516     //                                  cls: 'fc-event-time',
15517     //                                  html : cells.length > 1 ? '' : ev.time
15518     //                                },
15519                                     {
15520                                       tag:'span',
15521                                       cls: 'fc-event-title',
15522                                       html : String.format('{0}', ev.title)
15523                                     }
15524
15525
15526                                 ]
15527                             },
15528                             {
15529                                 cls: 'ui-resizable-handle ui-resizable-e',
15530                                 html : '&nbsp;&nbsp;&nbsp'
15531                             }
15532
15533                         ]
15534                     };
15535
15536                     if (i == 0) {
15537                         cfg.cls += ' fc-event-start';
15538                     }
15539                     if ((i+1) == rows.length) {
15540                         cfg.cls += ' fc-event-end';
15541                     }
15542
15543                     var ctr = _this.el.select('.fc-event-container',true).first();
15544                     var cg = ctr.createChild(cfg);
15545
15546                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15547                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15548
15549                     var r = (c.more.length) ? 1 : 0;
15550                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15551                     cg.setWidth(ebox.right - sbox.x -2);
15552
15553                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15554                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15555                     cg.on('click', _this.onEventClick, _this, ev);
15556
15557                     ev.els.push(cg);
15558                     
15559                 }
15560                 
15561             }
15562             
15563             
15564             if(c.more.length){
15565                 var  cfg = {
15566                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15567                     style : 'position: absolute',
15568                     unselectable : "on",
15569                     cn : [
15570                         {
15571                             cls: 'fc-event-inner',
15572                             cn : [
15573                                 {
15574                                   tag:'span',
15575                                   cls: 'fc-event-title',
15576                                   html : 'More'
15577                                 }
15578
15579
15580                             ]
15581                         },
15582                         {
15583                             cls: 'ui-resizable-handle ui-resizable-e',
15584                             html : '&nbsp;&nbsp;&nbsp'
15585                         }
15586
15587                     ]
15588                 };
15589
15590                 var ctr = _this.el.select('.fc-event-container',true).first();
15591                 var cg = ctr.createChild(cfg);
15592
15593                 var sbox = c.select('.fc-day-content',true).first().getBox();
15594                 var ebox = c.select('.fc-day-content',true).first().getBox();
15595                 //Roo.log(cg);
15596                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15597                 cg.setWidth(ebox.right - sbox.x -2);
15598
15599                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15600                 
15601             }
15602             
15603         });
15604         
15605         
15606         
15607     },
15608     
15609     onEventEnter: function (e, el,event,d) {
15610         this.fireEvent('evententer', this, el, event);
15611     },
15612     
15613     onEventLeave: function (e, el,event,d) {
15614         this.fireEvent('eventleave', this, el, event);
15615     },
15616     
15617     onEventClick: function (e, el,event,d) {
15618         this.fireEvent('eventclick', this, el, event);
15619     },
15620     
15621     onMonthChange: function () {
15622         this.store.load();
15623     },
15624     
15625     onMoreEventClick: function(e, el, more)
15626     {
15627         var _this = this;
15628         
15629         this.calpopover.placement = 'right';
15630         this.calpopover.setTitle('More');
15631         
15632         this.calpopover.setContent('');
15633         
15634         var ctr = this.calpopover.el.select('.popover-content', true).first();
15635         
15636         Roo.each(more, function(m){
15637             var cfg = {
15638                 cls : 'fc-event-hori fc-event-draggable',
15639                 html : m.title
15640             };
15641             var cg = ctr.createChild(cfg);
15642             
15643             cg.on('click', _this.onEventClick, _this, m);
15644         });
15645         
15646         this.calpopover.show(el);
15647         
15648         
15649     },
15650     
15651     onLoad: function () 
15652     {   
15653         this.calevents = [];
15654         var cal = this;
15655         
15656         if(this.store.getCount() > 0){
15657             this.store.data.each(function(d){
15658                cal.addItem({
15659                     id : d.data.id,
15660                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15661                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15662                     time : d.data.start_time,
15663                     title : d.data.title,
15664                     description : d.data.description,
15665                     venue : d.data.venue
15666                 });
15667             });
15668         }
15669         
15670         this.renderEvents();
15671         
15672         if(this.calevents.length && this.loadMask){
15673             this.maskEl.hide();
15674         }
15675     },
15676     
15677     onBeforeLoad: function()
15678     {
15679         this.clearEvents();
15680         if(this.loadMask){
15681             this.maskEl.show();
15682         }
15683     }
15684 });
15685
15686  
15687  /*
15688  * - LGPL
15689  *
15690  * element
15691  * 
15692  */
15693
15694 /**
15695  * @class Roo.bootstrap.Popover
15696  * @extends Roo.bootstrap.Component
15697  * Bootstrap Popover class
15698  * @cfg {String} html contents of the popover   (or false to use children..)
15699  * @cfg {String} title of popover (or false to hide)
15700  * @cfg {String} placement how it is placed
15701  * @cfg {String} trigger click || hover (or false to trigger manually)
15702  * @cfg {String} over what (parent or false to trigger manually.)
15703  * @cfg {Number} delay - delay before showing
15704  
15705  * @constructor
15706  * Create a new Popover
15707  * @param {Object} config The config object
15708  */
15709
15710 Roo.bootstrap.Popover = function(config){
15711     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15712     
15713     this.addEvents({
15714         // raw events
15715          /**
15716          * @event show
15717          * After the popover show
15718          * 
15719          * @param {Roo.bootstrap.Popover} this
15720          */
15721         "show" : true,
15722         /**
15723          * @event hide
15724          * After the popover hide
15725          * 
15726          * @param {Roo.bootstrap.Popover} this
15727          */
15728         "hide" : true
15729     });
15730 };
15731
15732 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15733     
15734     title: 'Fill in a title',
15735     html: false,
15736     
15737     placement : 'right',
15738     trigger : 'hover', // hover
15739     
15740     delay : 0,
15741     
15742     over: 'parent',
15743     
15744     can_build_overlaid : false,
15745     
15746     getChildContainer : function()
15747     {
15748         return this.el.select('.popover-content',true).first();
15749     },
15750     
15751     getAutoCreate : function(){
15752          
15753         var cfg = {
15754            cls : 'popover roo-dynamic',
15755            style: 'display:block',
15756            cn : [
15757                 {
15758                     cls : 'arrow'
15759                 },
15760                 {
15761                     cls : 'popover-inner',
15762                     cn : [
15763                         {
15764                             tag: 'h3',
15765                             cls: 'popover-title',
15766                             html : this.title
15767                         },
15768                         {
15769                             cls : 'popover-content',
15770                             html : this.html
15771                         }
15772                     ]
15773                     
15774                 }
15775            ]
15776         };
15777         
15778         return cfg;
15779     },
15780     setTitle: function(str)
15781     {
15782         this.title = str;
15783         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15784     },
15785     setContent: function(str)
15786     {
15787         this.html = str;
15788         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15789     },
15790     // as it get's added to the bottom of the page.
15791     onRender : function(ct, position)
15792     {
15793         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15794         if(!this.el){
15795             var cfg = Roo.apply({},  this.getAutoCreate());
15796             cfg.id = Roo.id();
15797             
15798             if (this.cls) {
15799                 cfg.cls += ' ' + this.cls;
15800             }
15801             if (this.style) {
15802                 cfg.style = this.style;
15803             }
15804             //Roo.log("adding to ");
15805             this.el = Roo.get(document.body).createChild(cfg, position);
15806 //            Roo.log(this.el);
15807         }
15808         this.initEvents();
15809     },
15810     
15811     initEvents : function()
15812     {
15813         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15814         this.el.enableDisplayMode('block');
15815         this.el.hide();
15816         if (this.over === false) {
15817             return; 
15818         }
15819         if (this.triggers === false) {
15820             return;
15821         }
15822         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15823         var triggers = this.trigger ? this.trigger.split(' ') : [];
15824         Roo.each(triggers, function(trigger) {
15825         
15826             if (trigger == 'click') {
15827                 on_el.on('click', this.toggle, this);
15828             } else if (trigger != 'manual') {
15829                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15830                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15831       
15832                 on_el.on(eventIn  ,this.enter, this);
15833                 on_el.on(eventOut, this.leave, this);
15834             }
15835         }, this);
15836         
15837     },
15838     
15839     
15840     // private
15841     timeout : null,
15842     hoverState : null,
15843     
15844     toggle : function () {
15845         this.hoverState == 'in' ? this.leave() : this.enter();
15846     },
15847     
15848     enter : function () {
15849        
15850     
15851         clearTimeout(this.timeout);
15852     
15853         this.hoverState = 'in';
15854     
15855         if (!this.delay || !this.delay.show) {
15856             this.show();
15857             return;
15858         }
15859         var _t = this;
15860         this.timeout = setTimeout(function () {
15861             if (_t.hoverState == 'in') {
15862                 _t.show();
15863             }
15864         }, this.delay.show)
15865     },
15866     leave : function() {
15867         clearTimeout(this.timeout);
15868     
15869         this.hoverState = 'out';
15870     
15871         if (!this.delay || !this.delay.hide) {
15872             this.hide();
15873             return;
15874         }
15875         var _t = this;
15876         this.timeout = setTimeout(function () {
15877             if (_t.hoverState == 'out') {
15878                 _t.hide();
15879             }
15880         }, this.delay.hide)
15881     },
15882     
15883     show : function (on_el)
15884     {
15885         if (!on_el) {
15886             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15887         }
15888         // set content.
15889         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15890         if (this.html !== false) {
15891             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15892         }
15893         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15894         if (!this.title.length) {
15895             this.el.select('.popover-title',true).hide();
15896         }
15897         
15898         var placement = typeof this.placement == 'function' ?
15899             this.placement.call(this, this.el, on_el) :
15900             this.placement;
15901             
15902         var autoToken = /\s?auto?\s?/i;
15903         var autoPlace = autoToken.test(placement);
15904         if (autoPlace) {
15905             placement = placement.replace(autoToken, '') || 'top';
15906         }
15907         
15908         //this.el.detach()
15909         //this.el.setXY([0,0]);
15910         this.el.show();
15911         this.el.dom.style.display='block';
15912         this.el.addClass(placement);
15913         
15914         //this.el.appendTo(on_el);
15915         
15916         var p = this.getPosition();
15917         var box = this.el.getBox();
15918         
15919         if (autoPlace) {
15920             // fixme..
15921         }
15922         var align = Roo.bootstrap.Popover.alignment[placement];
15923         this.el.alignTo(on_el, align[0],align[1]);
15924         //var arrow = this.el.select('.arrow',true).first();
15925         //arrow.set(align[2], 
15926         
15927         this.el.addClass('in');
15928         
15929         
15930         if (this.el.hasClass('fade')) {
15931             // fade it?
15932         }
15933         
15934         this.fireEvent('show', this);
15935         
15936     },
15937     hide : function()
15938     {
15939         this.el.setXY([0,0]);
15940         this.el.removeClass('in');
15941         this.el.hide();
15942         this.hoverState = null;
15943         
15944         this.fireEvent('hide', this);
15945     }
15946     
15947 });
15948
15949 Roo.bootstrap.Popover.alignment = {
15950     'left' : ['r-l', [-10,0], 'right'],
15951     'right' : ['l-r', [10,0], 'left'],
15952     'bottom' : ['t-b', [0,10], 'top'],
15953     'top' : [ 'b-t', [0,-10], 'bottom']
15954 };
15955
15956  /*
15957  * - LGPL
15958  *
15959  * Progress
15960  * 
15961  */
15962
15963 /**
15964  * @class Roo.bootstrap.Progress
15965  * @extends Roo.bootstrap.Component
15966  * Bootstrap Progress class
15967  * @cfg {Boolean} striped striped of the progress bar
15968  * @cfg {Boolean} active animated of the progress bar
15969  * 
15970  * 
15971  * @constructor
15972  * Create a new Progress
15973  * @param {Object} config The config object
15974  */
15975
15976 Roo.bootstrap.Progress = function(config){
15977     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15978 };
15979
15980 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15981     
15982     striped : false,
15983     active: false,
15984     
15985     getAutoCreate : function(){
15986         var cfg = {
15987             tag: 'div',
15988             cls: 'progress'
15989         };
15990         
15991         
15992         if(this.striped){
15993             cfg.cls += ' progress-striped';
15994         }
15995       
15996         if(this.active){
15997             cfg.cls += ' active';
15998         }
15999         
16000         
16001         return cfg;
16002     }
16003    
16004 });
16005
16006  
16007
16008  /*
16009  * - LGPL
16010  *
16011  * ProgressBar
16012  * 
16013  */
16014
16015 /**
16016  * @class Roo.bootstrap.ProgressBar
16017  * @extends Roo.bootstrap.Component
16018  * Bootstrap ProgressBar class
16019  * @cfg {Number} aria_valuenow aria-value now
16020  * @cfg {Number} aria_valuemin aria-value min
16021  * @cfg {Number} aria_valuemax aria-value max
16022  * @cfg {String} label label for the progress bar
16023  * @cfg {String} panel (success | info | warning | danger )
16024  * @cfg {String} role role of the progress bar
16025  * @cfg {String} sr_only text
16026  * 
16027  * 
16028  * @constructor
16029  * Create a new ProgressBar
16030  * @param {Object} config The config object
16031  */
16032
16033 Roo.bootstrap.ProgressBar = function(config){
16034     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16035 };
16036
16037 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16038     
16039     aria_valuenow : 0,
16040     aria_valuemin : 0,
16041     aria_valuemax : 100,
16042     label : false,
16043     panel : false,
16044     role : false,
16045     sr_only: false,
16046     
16047     getAutoCreate : function()
16048     {
16049         
16050         var cfg = {
16051             tag: 'div',
16052             cls: 'progress-bar',
16053             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16054         };
16055         
16056         if(this.sr_only){
16057             cfg.cn = {
16058                 tag: 'span',
16059                 cls: 'sr-only',
16060                 html: this.sr_only
16061             }
16062         }
16063         
16064         if(this.role){
16065             cfg.role = this.role;
16066         }
16067         
16068         if(this.aria_valuenow){
16069             cfg['aria-valuenow'] = this.aria_valuenow;
16070         }
16071         
16072         if(this.aria_valuemin){
16073             cfg['aria-valuemin'] = this.aria_valuemin;
16074         }
16075         
16076         if(this.aria_valuemax){
16077             cfg['aria-valuemax'] = this.aria_valuemax;
16078         }
16079         
16080         if(this.label && !this.sr_only){
16081             cfg.html = this.label;
16082         }
16083         
16084         if(this.panel){
16085             cfg.cls += ' progress-bar-' + this.panel;
16086         }
16087         
16088         return cfg;
16089     },
16090     
16091     update : function(aria_valuenow)
16092     {
16093         this.aria_valuenow = aria_valuenow;
16094         
16095         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16096     }
16097    
16098 });
16099
16100  
16101
16102  /*
16103  * - LGPL
16104  *
16105  * column
16106  * 
16107  */
16108
16109 /**
16110  * @class Roo.bootstrap.TabGroup
16111  * @extends Roo.bootstrap.Column
16112  * Bootstrap Column class
16113  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16114  * @cfg {Boolean} carousel true to make the group behave like a carousel
16115  * @cfg {Boolean} bullets show bullets for the panels
16116  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16117  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16118  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16119  * 
16120  * @constructor
16121  * Create a new TabGroup
16122  * @param {Object} config The config object
16123  */
16124
16125 Roo.bootstrap.TabGroup = function(config){
16126     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16127     if (!this.navId) {
16128         this.navId = Roo.id();
16129     }
16130     this.tabs = [];
16131     Roo.bootstrap.TabGroup.register(this);
16132     
16133 };
16134
16135 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16136     
16137     carousel : false,
16138     transition : false,
16139     bullets : 0,
16140     timer : 0,
16141     autoslide : false,
16142     slideFn : false,
16143     slideOnTouch : false,
16144     
16145     getAutoCreate : function()
16146     {
16147         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16148         
16149         cfg.cls += ' tab-content';
16150         
16151         if (this.carousel) {
16152             cfg.cls += ' carousel slide';
16153             
16154             cfg.cn = [{
16155                cls : 'carousel-inner'
16156             }];
16157         
16158             if(this.bullets  && !Roo.isTouch){
16159                 
16160                 var bullets = {
16161                     cls : 'carousel-bullets',
16162                     cn : []
16163                 };
16164                
16165                 if(this.bullets_cls){
16166                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16167                 }
16168                  /*
16169                 for (var i = 0; i < this.bullets; i++){
16170                     bullets.cn.push({
16171                         cls : 'bullet bullet-' + i
16172                     });
16173                 }
16174                 */
16175                 bullets.cn.push({
16176                     cls : 'clear'
16177                 });
16178                 
16179                 cfg.cn[0].cn = bullets;
16180             }
16181         }
16182         
16183         return cfg;
16184     },
16185     
16186     initEvents:  function()
16187     {
16188         if(Roo.isTouch && this.slideOnTouch){
16189             this.el.on("touchstart", this.onTouchStart, this);
16190         }
16191         
16192         if(this.autoslide){
16193             var _this = this;
16194             
16195             this.slideFn = window.setInterval(function() {
16196                 _this.showPanelNext();
16197             }, this.timer);
16198         }
16199         
16200     },
16201     
16202     onTouchStart : function(e, el, o)
16203     {
16204         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16205             return;
16206         }
16207         
16208         this.showPanelNext();
16209     },
16210     
16211     getChildContainer : function()
16212     {
16213         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16214     },
16215     
16216     /**
16217     * register a Navigation item
16218     * @param {Roo.bootstrap.NavItem} the navitem to add
16219     */
16220     register : function(item)
16221     {
16222         this.tabs.push( item);
16223         item.navId = this.navId; // not really needed..
16224         this.addBullet();
16225     
16226     },
16227     
16228     getActivePanel : function()
16229     {
16230         var r = false;
16231         Roo.each(this.tabs, function(t) {
16232             if (t.active) {
16233                 r = t;
16234                 return false;
16235             }
16236             return null;
16237         });
16238         return r;
16239         
16240     },
16241     getPanelByName : function(n)
16242     {
16243         var r = false;
16244         Roo.each(this.tabs, function(t) {
16245             if (t.tabId == n) {
16246                 r = t;
16247                 return false;
16248             }
16249             return null;
16250         });
16251         return r;
16252     },
16253     indexOfPanel : function(p)
16254     {
16255         var r = false;
16256         Roo.each(this.tabs, function(t,i) {
16257             if (t.tabId == p.tabId) {
16258                 r = i;
16259                 return false;
16260             }
16261             return null;
16262         });
16263         return r;
16264     },
16265     /**
16266      * show a specific panel
16267      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16268      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16269      */
16270     showPanel : function (pan)
16271     {
16272         if(this.transition || typeof(pan) == 'undefined'){
16273             Roo.log("waiting for the transitionend");
16274             return;
16275         }
16276         
16277         if (typeof(pan) == 'number') {
16278             pan = this.tabs[pan];
16279         }
16280         
16281         if (typeof(pan) == 'string') {
16282             pan = this.getPanelByName(pan);
16283         }
16284         
16285         var cur = this.getActivePanel();
16286         
16287         if(!pan || !cur){
16288             Roo.log('pan or acitve pan is undefined');
16289             return false;
16290         }
16291         
16292         if (pan.tabId == this.getActivePanel().tabId) {
16293             return true;
16294         }
16295         
16296         if (false === cur.fireEvent('beforedeactivate')) {
16297             return false;
16298         }
16299         
16300         if(this.bullets > 0 && !Roo.isTouch){
16301             this.setActiveBullet(this.indexOfPanel(pan));
16302         }
16303         
16304         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16305             
16306             this.transition = true;
16307             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16308             var lr = dir == 'next' ? 'left' : 'right';
16309             pan.el.addClass(dir); // or prev
16310             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16311             cur.el.addClass(lr); // or right
16312             pan.el.addClass(lr);
16313             
16314             var _this = this;
16315             cur.el.on('transitionend', function() {
16316                 Roo.log("trans end?");
16317                 
16318                 pan.el.removeClass([lr,dir]);
16319                 pan.setActive(true);
16320                 
16321                 cur.el.removeClass([lr]);
16322                 cur.setActive(false);
16323                 
16324                 _this.transition = false;
16325                 
16326             }, this, { single:  true } );
16327             
16328             return true;
16329         }
16330         
16331         cur.setActive(false);
16332         pan.setActive(true);
16333         
16334         return true;
16335         
16336     },
16337     showPanelNext : function()
16338     {
16339         var i = this.indexOfPanel(this.getActivePanel());
16340         
16341         if (i >= this.tabs.length - 1 && !this.autoslide) {
16342             return;
16343         }
16344         
16345         if (i >= this.tabs.length - 1 && this.autoslide) {
16346             i = -1;
16347         }
16348         
16349         this.showPanel(this.tabs[i+1]);
16350     },
16351     
16352     showPanelPrev : function()
16353     {
16354         var i = this.indexOfPanel(this.getActivePanel());
16355         
16356         if (i  < 1 && !this.autoslide) {
16357             return;
16358         }
16359         
16360         if (i < 1 && this.autoslide) {
16361             i = this.tabs.length;
16362         }
16363         
16364         this.showPanel(this.tabs[i-1]);
16365     },
16366     
16367     
16368     addBullet: function()
16369     {
16370         if(!this.bullets || Roo.isTouch){
16371             return;
16372         }
16373         var ctr = this.el.select('.carousel-bullets',true).first();
16374         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16375         var bullet = ctr.createChild({
16376             cls : 'bullet bullet-' + i
16377         },ctr.dom.lastChild);
16378         
16379         
16380         var _this = this;
16381         
16382         bullet.on('click', (function(e, el, o, ii, t){
16383
16384             e.preventDefault();
16385
16386             this.showPanel(ii);
16387
16388             if(this.autoslide && this.slideFn){
16389                 clearInterval(this.slideFn);
16390                 this.slideFn = window.setInterval(function() {
16391                     _this.showPanelNext();
16392                 }, this.timer);
16393             }
16394
16395         }).createDelegate(this, [i, bullet], true));
16396                 
16397         
16398     },
16399      
16400     setActiveBullet : function(i)
16401     {
16402         if(Roo.isTouch){
16403             return;
16404         }
16405         
16406         Roo.each(this.el.select('.bullet', true).elements, function(el){
16407             el.removeClass('selected');
16408         });
16409
16410         var bullet = this.el.select('.bullet-' + i, true).first();
16411         
16412         if(!bullet){
16413             return;
16414         }
16415         
16416         bullet.addClass('selected');
16417     }
16418     
16419     
16420   
16421 });
16422
16423  
16424
16425  
16426  
16427 Roo.apply(Roo.bootstrap.TabGroup, {
16428     
16429     groups: {},
16430      /**
16431     * register a Navigation Group
16432     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16433     */
16434     register : function(navgrp)
16435     {
16436         this.groups[navgrp.navId] = navgrp;
16437         
16438     },
16439     /**
16440     * fetch a Navigation Group based on the navigation ID
16441     * if one does not exist , it will get created.
16442     * @param {string} the navgroup to add
16443     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16444     */
16445     get: function(navId) {
16446         if (typeof(this.groups[navId]) == 'undefined') {
16447             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16448         }
16449         return this.groups[navId] ;
16450     }
16451     
16452     
16453     
16454 });
16455
16456  /*
16457  * - LGPL
16458  *
16459  * TabPanel
16460  * 
16461  */
16462
16463 /**
16464  * @class Roo.bootstrap.TabPanel
16465  * @extends Roo.bootstrap.Component
16466  * Bootstrap TabPanel class
16467  * @cfg {Boolean} active panel active
16468  * @cfg {String} html panel content
16469  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16470  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16471  * 
16472  * 
16473  * @constructor
16474  * Create a new TabPanel
16475  * @param {Object} config The config object
16476  */
16477
16478 Roo.bootstrap.TabPanel = function(config){
16479     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16480     this.addEvents({
16481         /**
16482              * @event changed
16483              * Fires when the active status changes
16484              * @param {Roo.bootstrap.TabPanel} this
16485              * @param {Boolean} state the new state
16486             
16487          */
16488         'changed': true,
16489         /**
16490              * @event beforedeactivate
16491              * Fires before a tab is de-activated - can be used to do validation on a form.
16492              * @param {Roo.bootstrap.TabPanel} this
16493              * @return {Boolean} false if there is an error
16494             
16495          */
16496         'beforedeactivate': true
16497      });
16498     
16499     this.tabId = this.tabId || Roo.id();
16500   
16501 };
16502
16503 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16504     
16505     active: false,
16506     html: false,
16507     tabId: false,
16508     navId : false,
16509     
16510     getAutoCreate : function(){
16511         var cfg = {
16512             tag: 'div',
16513             // item is needed for carousel - not sure if it has any effect otherwise
16514             cls: 'tab-pane item',
16515             html: this.html || ''
16516         };
16517         
16518         if(this.active){
16519             cfg.cls += ' active';
16520         }
16521         
16522         if(this.tabId){
16523             cfg.tabId = this.tabId;
16524         }
16525         
16526         
16527         return cfg;
16528     },
16529     
16530     initEvents:  function()
16531     {
16532         var p = this.parent();
16533         this.navId = this.navId || p.navId;
16534         
16535         if (typeof(this.navId) != 'undefined') {
16536             // not really needed.. but just in case.. parent should be a NavGroup.
16537             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16538             
16539             tg.register(this);
16540             
16541             var i = tg.tabs.length - 1;
16542             
16543             if(this.active && tg.bullets > 0 && i < tg.bullets){
16544                 tg.setActiveBullet(i);
16545             }
16546         }
16547         
16548     },
16549     
16550     
16551     onRender : function(ct, position)
16552     {
16553        // Roo.log("Call onRender: " + this.xtype);
16554         
16555         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16556         
16557         
16558         
16559         
16560         
16561     },
16562     
16563     setActive: function(state)
16564     {
16565         Roo.log("panel - set active " + this.tabId + "=" + state);
16566         
16567         this.active = state;
16568         if (!state) {
16569             this.el.removeClass('active');
16570             
16571         } else  if (!this.el.hasClass('active')) {
16572             this.el.addClass('active');
16573         }
16574         
16575         this.fireEvent('changed', this, state);
16576     }
16577     
16578     
16579 });
16580  
16581
16582  
16583
16584  /*
16585  * - LGPL
16586  *
16587  * DateField
16588  * 
16589  */
16590
16591 /**
16592  * @class Roo.bootstrap.DateField
16593  * @extends Roo.bootstrap.Input
16594  * Bootstrap DateField class
16595  * @cfg {Number} weekStart default 0
16596  * @cfg {String} viewMode default empty, (months|years)
16597  * @cfg {String} minViewMode default empty, (months|years)
16598  * @cfg {Number} startDate default -Infinity
16599  * @cfg {Number} endDate default Infinity
16600  * @cfg {Boolean} todayHighlight default false
16601  * @cfg {Boolean} todayBtn default false
16602  * @cfg {Boolean} calendarWeeks default false
16603  * @cfg {Object} daysOfWeekDisabled default empty
16604  * @cfg {Boolean} singleMode default false (true | false)
16605  * 
16606  * @cfg {Boolean} keyboardNavigation default true
16607  * @cfg {String} language default en
16608  * 
16609  * @constructor
16610  * Create a new DateField
16611  * @param {Object} config The config object
16612  */
16613
16614 Roo.bootstrap.DateField = function(config){
16615     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16616      this.addEvents({
16617             /**
16618              * @event show
16619              * Fires when this field show.
16620              * @param {Roo.bootstrap.DateField} this
16621              * @param {Mixed} date The date value
16622              */
16623             show : true,
16624             /**
16625              * @event show
16626              * Fires when this field hide.
16627              * @param {Roo.bootstrap.DateField} this
16628              * @param {Mixed} date The date value
16629              */
16630             hide : true,
16631             /**
16632              * @event select
16633              * Fires when select a date.
16634              * @param {Roo.bootstrap.DateField} this
16635              * @param {Mixed} date The date value
16636              */
16637             select : true,
16638             /**
16639              * @event beforeselect
16640              * Fires when before select a date.
16641              * @param {Roo.bootstrap.DateField} this
16642              * @param {Mixed} date The date value
16643              */
16644             beforeselect : true
16645         });
16646 };
16647
16648 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16649     
16650     /**
16651      * @cfg {String} format
16652      * The default date format string which can be overriden for localization support.  The format must be
16653      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16654      */
16655     format : "m/d/y",
16656     /**
16657      * @cfg {String} altFormats
16658      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16659      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16660      */
16661     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16662     
16663     weekStart : 0,
16664     
16665     viewMode : '',
16666     
16667     minViewMode : '',
16668     
16669     todayHighlight : false,
16670     
16671     todayBtn: false,
16672     
16673     language: 'en',
16674     
16675     keyboardNavigation: true,
16676     
16677     calendarWeeks: false,
16678     
16679     startDate: -Infinity,
16680     
16681     endDate: Infinity,
16682     
16683     daysOfWeekDisabled: [],
16684     
16685     _events: [],
16686     
16687     singleMode : false,
16688     
16689     UTCDate: function()
16690     {
16691         return new Date(Date.UTC.apply(Date, arguments));
16692     },
16693     
16694     UTCToday: function()
16695     {
16696         var today = new Date();
16697         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16698     },
16699     
16700     getDate: function() {
16701             var d = this.getUTCDate();
16702             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16703     },
16704     
16705     getUTCDate: function() {
16706             return this.date;
16707     },
16708     
16709     setDate: function(d) {
16710             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16711     },
16712     
16713     setUTCDate: function(d) {
16714             this.date = d;
16715             this.setValue(this.formatDate(this.date));
16716     },
16717         
16718     onRender: function(ct, position)
16719     {
16720         
16721         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16722         
16723         this.language = this.language || 'en';
16724         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16725         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16726         
16727         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16728         this.format = this.format || 'm/d/y';
16729         this.isInline = false;
16730         this.isInput = true;
16731         this.component = this.el.select('.add-on', true).first() || false;
16732         this.component = (this.component && this.component.length === 0) ? false : this.component;
16733         this.hasInput = this.component && this.inputEL().length;
16734         
16735         if (typeof(this.minViewMode === 'string')) {
16736             switch (this.minViewMode) {
16737                 case 'months':
16738                     this.minViewMode = 1;
16739                     break;
16740                 case 'years':
16741                     this.minViewMode = 2;
16742                     break;
16743                 default:
16744                     this.minViewMode = 0;
16745                     break;
16746             }
16747         }
16748         
16749         if (typeof(this.viewMode === 'string')) {
16750             switch (this.viewMode) {
16751                 case 'months':
16752                     this.viewMode = 1;
16753                     break;
16754                 case 'years':
16755                     this.viewMode = 2;
16756                     break;
16757                 default:
16758                     this.viewMode = 0;
16759                     break;
16760             }
16761         }
16762                 
16763         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16764         
16765 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16766         
16767         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16768         
16769         this.picker().on('mousedown', this.onMousedown, this);
16770         this.picker().on('click', this.onClick, this);
16771         
16772         this.picker().addClass('datepicker-dropdown');
16773         
16774         this.startViewMode = this.viewMode;
16775         
16776         if(this.singleMode){
16777             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16778                 v.setVisibilityMode(Roo.Element.DISPLAY);
16779                 v.hide();
16780             });
16781             
16782             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16783                 v.setStyle('width', '189px');
16784             });
16785         }
16786         
16787         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16788             if(!this.calendarWeeks){
16789                 v.remove();
16790                 return;
16791             }
16792             
16793             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16794             v.attr('colspan', function(i, val){
16795                 return parseInt(val) + 1;
16796             });
16797         });
16798                         
16799         
16800         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16801         
16802         this.setStartDate(this.startDate);
16803         this.setEndDate(this.endDate);
16804         
16805         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16806         
16807         this.fillDow();
16808         this.fillMonths();
16809         this.update();
16810         this.showMode();
16811         
16812         if(this.isInline) {
16813             this.show();
16814         }
16815     },
16816     
16817     picker : function()
16818     {
16819         return this.pickerEl;
16820 //        return this.el.select('.datepicker', true).first();
16821     },
16822     
16823     fillDow: function()
16824     {
16825         var dowCnt = this.weekStart;
16826         
16827         var dow = {
16828             tag: 'tr',
16829             cn: [
16830                 
16831             ]
16832         };
16833         
16834         if(this.calendarWeeks){
16835             dow.cn.push({
16836                 tag: 'th',
16837                 cls: 'cw',
16838                 html: '&nbsp;'
16839             })
16840         }
16841         
16842         while (dowCnt < this.weekStart + 7) {
16843             dow.cn.push({
16844                 tag: 'th',
16845                 cls: 'dow',
16846                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16847             });
16848         }
16849         
16850         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16851     },
16852     
16853     fillMonths: function()
16854     {    
16855         var i = 0;
16856         var months = this.picker().select('>.datepicker-months td', true).first();
16857         
16858         months.dom.innerHTML = '';
16859         
16860         while (i < 12) {
16861             var month = {
16862                 tag: 'span',
16863                 cls: 'month',
16864                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16865             };
16866             
16867             months.createChild(month);
16868         }
16869         
16870     },
16871     
16872     update: function()
16873     {
16874         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16875         
16876         if (this.date < this.startDate) {
16877             this.viewDate = new Date(this.startDate);
16878         } else if (this.date > this.endDate) {
16879             this.viewDate = new Date(this.endDate);
16880         } else {
16881             this.viewDate = new Date(this.date);
16882         }
16883         
16884         this.fill();
16885     },
16886     
16887     fill: function() 
16888     {
16889         var d = new Date(this.viewDate),
16890                 year = d.getUTCFullYear(),
16891                 month = d.getUTCMonth(),
16892                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16893                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16894                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16895                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16896                 currentDate = this.date && this.date.valueOf(),
16897                 today = this.UTCToday();
16898         
16899         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16900         
16901 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16902         
16903 //        this.picker.select('>tfoot th.today').
16904 //                                              .text(dates[this.language].today)
16905 //                                              .toggle(this.todayBtn !== false);
16906     
16907         this.updateNavArrows();
16908         this.fillMonths();
16909                                                 
16910         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16911         
16912         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16913          
16914         prevMonth.setUTCDate(day);
16915         
16916         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16917         
16918         var nextMonth = new Date(prevMonth);
16919         
16920         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16921         
16922         nextMonth = nextMonth.valueOf();
16923         
16924         var fillMonths = false;
16925         
16926         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16927         
16928         while(prevMonth.valueOf() < nextMonth) {
16929             var clsName = '';
16930             
16931             if (prevMonth.getUTCDay() === this.weekStart) {
16932                 if(fillMonths){
16933                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16934                 }
16935                     
16936                 fillMonths = {
16937                     tag: 'tr',
16938                     cn: []
16939                 };
16940                 
16941                 if(this.calendarWeeks){
16942                     // ISO 8601: First week contains first thursday.
16943                     // ISO also states week starts on Monday, but we can be more abstract here.
16944                     var
16945                     // Start of current week: based on weekstart/current date
16946                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16947                     // Thursday of this week
16948                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16949                     // First Thursday of year, year from thursday
16950                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16951                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16952                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16953                     
16954                     fillMonths.cn.push({
16955                         tag: 'td',
16956                         cls: 'cw',
16957                         html: calWeek
16958                     });
16959                 }
16960             }
16961             
16962             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16963                 clsName += ' old';
16964             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16965                 clsName += ' new';
16966             }
16967             if (this.todayHighlight &&
16968                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16969                 prevMonth.getUTCMonth() == today.getMonth() &&
16970                 prevMonth.getUTCDate() == today.getDate()) {
16971                 clsName += ' today';
16972             }
16973             
16974             if (currentDate && prevMonth.valueOf() === currentDate) {
16975                 clsName += ' active';
16976             }
16977             
16978             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16979                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16980                     clsName += ' disabled';
16981             }
16982             
16983             fillMonths.cn.push({
16984                 tag: 'td',
16985                 cls: 'day ' + clsName,
16986                 html: prevMonth.getDate()
16987             });
16988             
16989             prevMonth.setDate(prevMonth.getDate()+1);
16990         }
16991           
16992         var currentYear = this.date && this.date.getUTCFullYear();
16993         var currentMonth = this.date && this.date.getUTCMonth();
16994         
16995         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16996         
16997         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16998             v.removeClass('active');
16999             
17000             if(currentYear === year && k === currentMonth){
17001                 v.addClass('active');
17002             }
17003             
17004             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17005                 v.addClass('disabled');
17006             }
17007             
17008         });
17009         
17010         
17011         year = parseInt(year/10, 10) * 10;
17012         
17013         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17014         
17015         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17016         
17017         year -= 1;
17018         for (var i = -1; i < 11; i++) {
17019             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17020                 tag: 'span',
17021                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17022                 html: year
17023             });
17024             
17025             year += 1;
17026         }
17027     },
17028     
17029     showMode: function(dir) 
17030     {
17031         if (dir) {
17032             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17033         }
17034         
17035         Roo.each(this.picker().select('>div',true).elements, function(v){
17036             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17037             v.hide();
17038         });
17039         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17040     },
17041     
17042     place: function()
17043     {
17044         if(this.isInline) {
17045             return;
17046         }
17047         
17048         this.picker().removeClass(['bottom', 'top']);
17049         
17050         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17051             /*
17052              * place to the top of element!
17053              *
17054              */
17055             
17056             this.picker().addClass('top');
17057             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17058             
17059             return;
17060         }
17061         
17062         this.picker().addClass('bottom');
17063         
17064         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17065     },
17066     
17067     parseDate : function(value)
17068     {
17069         if(!value || value instanceof Date){
17070             return value;
17071         }
17072         var v = Date.parseDate(value, this.format);
17073         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17074             v = Date.parseDate(value, 'Y-m-d');
17075         }
17076         if(!v && this.altFormats){
17077             if(!this.altFormatsArray){
17078                 this.altFormatsArray = this.altFormats.split("|");
17079             }
17080             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17081                 v = Date.parseDate(value, this.altFormatsArray[i]);
17082             }
17083         }
17084         return v;
17085     },
17086     
17087     formatDate : function(date, fmt)
17088     {   
17089         return (!date || !(date instanceof Date)) ?
17090         date : date.dateFormat(fmt || this.format);
17091     },
17092     
17093     onFocus : function()
17094     {
17095         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17096         this.show();
17097     },
17098     
17099     onBlur : function()
17100     {
17101         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17102         
17103         var d = this.inputEl().getValue();
17104         
17105         this.setValue(d);
17106                 
17107         this.hide();
17108     },
17109     
17110     show : function()
17111     {
17112         this.picker().show();
17113         this.update();
17114         this.place();
17115         
17116         this.fireEvent('show', this, this.date);
17117     },
17118     
17119     hide : function()
17120     {
17121         if(this.isInline) {
17122             return;
17123         }
17124         this.picker().hide();
17125         this.viewMode = this.startViewMode;
17126         this.showMode();
17127         
17128         this.fireEvent('hide', this, this.date);
17129         
17130     },
17131     
17132     onMousedown: function(e)
17133     {
17134         e.stopPropagation();
17135         e.preventDefault();
17136     },
17137     
17138     keyup: function(e)
17139     {
17140         Roo.bootstrap.DateField.superclass.keyup.call(this);
17141         this.update();
17142     },
17143
17144     setValue: function(v)
17145     {
17146         if(this.fireEvent('beforeselect', this, v) !== false){
17147             var d = new Date(this.parseDate(v) ).clearTime();
17148         
17149             if(isNaN(d.getTime())){
17150                 this.date = this.viewDate = '';
17151                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17152                 return;
17153             }
17154
17155             v = this.formatDate(d);
17156
17157             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17158
17159             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17160
17161             this.update();
17162
17163             this.fireEvent('select', this, this.date);
17164         }
17165     },
17166     
17167     getValue: function()
17168     {
17169         return this.formatDate(this.date);
17170     },
17171     
17172     fireKey: function(e)
17173     {
17174         if (!this.picker().isVisible()){
17175             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17176                 this.show();
17177             }
17178             return;
17179         }
17180         
17181         var dateChanged = false,
17182         dir, day, month,
17183         newDate, newViewDate;
17184         
17185         switch(e.keyCode){
17186             case 27: // escape
17187                 this.hide();
17188                 e.preventDefault();
17189                 break;
17190             case 37: // left
17191             case 39: // right
17192                 if (!this.keyboardNavigation) {
17193                     break;
17194                 }
17195                 dir = e.keyCode == 37 ? -1 : 1;
17196                 
17197                 if (e.ctrlKey){
17198                     newDate = this.moveYear(this.date, dir);
17199                     newViewDate = this.moveYear(this.viewDate, dir);
17200                 } else if (e.shiftKey){
17201                     newDate = this.moveMonth(this.date, dir);
17202                     newViewDate = this.moveMonth(this.viewDate, dir);
17203                 } else {
17204                     newDate = new Date(this.date);
17205                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17206                     newViewDate = new Date(this.viewDate);
17207                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17208                 }
17209                 if (this.dateWithinRange(newDate)){
17210                     this.date = newDate;
17211                     this.viewDate = newViewDate;
17212                     this.setValue(this.formatDate(this.date));
17213 //                    this.update();
17214                     e.preventDefault();
17215                     dateChanged = true;
17216                 }
17217                 break;
17218             case 38: // up
17219             case 40: // down
17220                 if (!this.keyboardNavigation) {
17221                     break;
17222                 }
17223                 dir = e.keyCode == 38 ? -1 : 1;
17224                 if (e.ctrlKey){
17225                     newDate = this.moveYear(this.date, dir);
17226                     newViewDate = this.moveYear(this.viewDate, dir);
17227                 } else if (e.shiftKey){
17228                     newDate = this.moveMonth(this.date, dir);
17229                     newViewDate = this.moveMonth(this.viewDate, dir);
17230                 } else {
17231                     newDate = new Date(this.date);
17232                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17233                     newViewDate = new Date(this.viewDate);
17234                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17235                 }
17236                 if (this.dateWithinRange(newDate)){
17237                     this.date = newDate;
17238                     this.viewDate = newViewDate;
17239                     this.setValue(this.formatDate(this.date));
17240 //                    this.update();
17241                     e.preventDefault();
17242                     dateChanged = true;
17243                 }
17244                 break;
17245             case 13: // enter
17246                 this.setValue(this.formatDate(this.date));
17247                 this.hide();
17248                 e.preventDefault();
17249                 break;
17250             case 9: // tab
17251                 this.setValue(this.formatDate(this.date));
17252                 this.hide();
17253                 break;
17254             case 16: // shift
17255             case 17: // ctrl
17256             case 18: // alt
17257                 break;
17258             default :
17259                 this.hide();
17260                 
17261         }
17262     },
17263     
17264     
17265     onClick: function(e) 
17266     {
17267         e.stopPropagation();
17268         e.preventDefault();
17269         
17270         var target = e.getTarget();
17271         
17272         if(target.nodeName.toLowerCase() === 'i'){
17273             target = Roo.get(target).dom.parentNode;
17274         }
17275         
17276         var nodeName = target.nodeName;
17277         var className = target.className;
17278         var html = target.innerHTML;
17279         //Roo.log(nodeName);
17280         
17281         switch(nodeName.toLowerCase()) {
17282             case 'th':
17283                 switch(className) {
17284                     case 'switch':
17285                         this.showMode(1);
17286                         break;
17287                     case 'prev':
17288                     case 'next':
17289                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17290                         switch(this.viewMode){
17291                                 case 0:
17292                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17293                                         break;
17294                                 case 1:
17295                                 case 2:
17296                                         this.viewDate = this.moveYear(this.viewDate, dir);
17297                                         break;
17298                         }
17299                         this.fill();
17300                         break;
17301                     case 'today':
17302                         var date = new Date();
17303                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17304 //                        this.fill()
17305                         this.setValue(this.formatDate(this.date));
17306                         
17307                         this.hide();
17308                         break;
17309                 }
17310                 break;
17311             case 'span':
17312                 if (className.indexOf('disabled') < 0) {
17313                     this.viewDate.setUTCDate(1);
17314                     if (className.indexOf('month') > -1) {
17315                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17316                     } else {
17317                         var year = parseInt(html, 10) || 0;
17318                         this.viewDate.setUTCFullYear(year);
17319                         
17320                     }
17321                     
17322                     if(this.singleMode){
17323                         this.setValue(this.formatDate(this.viewDate));
17324                         this.hide();
17325                         return;
17326                     }
17327                     
17328                     this.showMode(-1);
17329                     this.fill();
17330                 }
17331                 break;
17332                 
17333             case 'td':
17334                 //Roo.log(className);
17335                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17336                     var day = parseInt(html, 10) || 1;
17337                     var year = this.viewDate.getUTCFullYear(),
17338                         month = this.viewDate.getUTCMonth();
17339
17340                     if (className.indexOf('old') > -1) {
17341                         if(month === 0 ){
17342                             month = 11;
17343                             year -= 1;
17344                         }else{
17345                             month -= 1;
17346                         }
17347                     } else if (className.indexOf('new') > -1) {
17348                         if (month == 11) {
17349                             month = 0;
17350                             year += 1;
17351                         } else {
17352                             month += 1;
17353                         }
17354                     }
17355                     //Roo.log([year,month,day]);
17356                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17357                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17358 //                    this.fill();
17359                     //Roo.log(this.formatDate(this.date));
17360                     this.setValue(this.formatDate(this.date));
17361                     this.hide();
17362                 }
17363                 break;
17364         }
17365     },
17366     
17367     setStartDate: function(startDate)
17368     {
17369         this.startDate = startDate || -Infinity;
17370         if (this.startDate !== -Infinity) {
17371             this.startDate = this.parseDate(this.startDate);
17372         }
17373         this.update();
17374         this.updateNavArrows();
17375     },
17376
17377     setEndDate: function(endDate)
17378     {
17379         this.endDate = endDate || Infinity;
17380         if (this.endDate !== Infinity) {
17381             this.endDate = this.parseDate(this.endDate);
17382         }
17383         this.update();
17384         this.updateNavArrows();
17385     },
17386     
17387     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17388     {
17389         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17390         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17391             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17392         }
17393         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17394             return parseInt(d, 10);
17395         });
17396         this.update();
17397         this.updateNavArrows();
17398     },
17399     
17400     updateNavArrows: function() 
17401     {
17402         if(this.singleMode){
17403             return;
17404         }
17405         
17406         var d = new Date(this.viewDate),
17407         year = d.getUTCFullYear(),
17408         month = d.getUTCMonth();
17409         
17410         Roo.each(this.picker().select('.prev', true).elements, function(v){
17411             v.show();
17412             switch (this.viewMode) {
17413                 case 0:
17414
17415                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17416                         v.hide();
17417                     }
17418                     break;
17419                 case 1:
17420                 case 2:
17421                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17422                         v.hide();
17423                     }
17424                     break;
17425             }
17426         });
17427         
17428         Roo.each(this.picker().select('.next', true).elements, function(v){
17429             v.show();
17430             switch (this.viewMode) {
17431                 case 0:
17432
17433                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17434                         v.hide();
17435                     }
17436                     break;
17437                 case 1:
17438                 case 2:
17439                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17440                         v.hide();
17441                     }
17442                     break;
17443             }
17444         })
17445     },
17446     
17447     moveMonth: function(date, dir)
17448     {
17449         if (!dir) {
17450             return date;
17451         }
17452         var new_date = new Date(date.valueOf()),
17453         day = new_date.getUTCDate(),
17454         month = new_date.getUTCMonth(),
17455         mag = Math.abs(dir),
17456         new_month, test;
17457         dir = dir > 0 ? 1 : -1;
17458         if (mag == 1){
17459             test = dir == -1
17460             // If going back one month, make sure month is not current month
17461             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17462             ? function(){
17463                 return new_date.getUTCMonth() == month;
17464             }
17465             // If going forward one month, make sure month is as expected
17466             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17467             : function(){
17468                 return new_date.getUTCMonth() != new_month;
17469             };
17470             new_month = month + dir;
17471             new_date.setUTCMonth(new_month);
17472             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17473             if (new_month < 0 || new_month > 11) {
17474                 new_month = (new_month + 12) % 12;
17475             }
17476         } else {
17477             // For magnitudes >1, move one month at a time...
17478             for (var i=0; i<mag; i++) {
17479                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17480                 new_date = this.moveMonth(new_date, dir);
17481             }
17482             // ...then reset the day, keeping it in the new month
17483             new_month = new_date.getUTCMonth();
17484             new_date.setUTCDate(day);
17485             test = function(){
17486                 return new_month != new_date.getUTCMonth();
17487             };
17488         }
17489         // Common date-resetting loop -- if date is beyond end of month, make it
17490         // end of month
17491         while (test()){
17492             new_date.setUTCDate(--day);
17493             new_date.setUTCMonth(new_month);
17494         }
17495         return new_date;
17496     },
17497
17498     moveYear: function(date, dir)
17499     {
17500         return this.moveMonth(date, dir*12);
17501     },
17502
17503     dateWithinRange: function(date)
17504     {
17505         return date >= this.startDate && date <= this.endDate;
17506     },
17507
17508     
17509     remove: function() 
17510     {
17511         this.picker().remove();
17512     }
17513    
17514 });
17515
17516 Roo.apply(Roo.bootstrap.DateField,  {
17517     
17518     head : {
17519         tag: 'thead',
17520         cn: [
17521         {
17522             tag: 'tr',
17523             cn: [
17524             {
17525                 tag: 'th',
17526                 cls: 'prev',
17527                 html: '<i class="fa fa-arrow-left"/>'
17528             },
17529             {
17530                 tag: 'th',
17531                 cls: 'switch',
17532                 colspan: '5'
17533             },
17534             {
17535                 tag: 'th',
17536                 cls: 'next',
17537                 html: '<i class="fa fa-arrow-right"/>'
17538             }
17539
17540             ]
17541         }
17542         ]
17543     },
17544     
17545     content : {
17546         tag: 'tbody',
17547         cn: [
17548         {
17549             tag: 'tr',
17550             cn: [
17551             {
17552                 tag: 'td',
17553                 colspan: '7'
17554             }
17555             ]
17556         }
17557         ]
17558     },
17559     
17560     footer : {
17561         tag: 'tfoot',
17562         cn: [
17563         {
17564             tag: 'tr',
17565             cn: [
17566             {
17567                 tag: 'th',
17568                 colspan: '7',
17569                 cls: 'today'
17570             }
17571                     
17572             ]
17573         }
17574         ]
17575     },
17576     
17577     dates:{
17578         en: {
17579             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17580             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17581             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17582             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17583             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17584             today: "Today"
17585         }
17586     },
17587     
17588     modes: [
17589     {
17590         clsName: 'days',
17591         navFnc: 'Month',
17592         navStep: 1
17593     },
17594     {
17595         clsName: 'months',
17596         navFnc: 'FullYear',
17597         navStep: 1
17598     },
17599     {
17600         clsName: 'years',
17601         navFnc: 'FullYear',
17602         navStep: 10
17603     }]
17604 });
17605
17606 Roo.apply(Roo.bootstrap.DateField,  {
17607   
17608     template : {
17609         tag: 'div',
17610         cls: 'datepicker dropdown-menu roo-dynamic',
17611         cn: [
17612         {
17613             tag: 'div',
17614             cls: 'datepicker-days',
17615             cn: [
17616             {
17617                 tag: 'table',
17618                 cls: 'table-condensed',
17619                 cn:[
17620                 Roo.bootstrap.DateField.head,
17621                 {
17622                     tag: 'tbody'
17623                 },
17624                 Roo.bootstrap.DateField.footer
17625                 ]
17626             }
17627             ]
17628         },
17629         {
17630             tag: 'div',
17631             cls: 'datepicker-months',
17632             cn: [
17633             {
17634                 tag: 'table',
17635                 cls: 'table-condensed',
17636                 cn:[
17637                 Roo.bootstrap.DateField.head,
17638                 Roo.bootstrap.DateField.content,
17639                 Roo.bootstrap.DateField.footer
17640                 ]
17641             }
17642             ]
17643         },
17644         {
17645             tag: 'div',
17646             cls: 'datepicker-years',
17647             cn: [
17648             {
17649                 tag: 'table',
17650                 cls: 'table-condensed',
17651                 cn:[
17652                 Roo.bootstrap.DateField.head,
17653                 Roo.bootstrap.DateField.content,
17654                 Roo.bootstrap.DateField.footer
17655                 ]
17656             }
17657             ]
17658         }
17659         ]
17660     }
17661 });
17662
17663  
17664
17665  /*
17666  * - LGPL
17667  *
17668  * TimeField
17669  * 
17670  */
17671
17672 /**
17673  * @class Roo.bootstrap.TimeField
17674  * @extends Roo.bootstrap.Input
17675  * Bootstrap DateField class
17676  * 
17677  * 
17678  * @constructor
17679  * Create a new TimeField
17680  * @param {Object} config The config object
17681  */
17682
17683 Roo.bootstrap.TimeField = function(config){
17684     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17685     this.addEvents({
17686             /**
17687              * @event show
17688              * Fires when this field show.
17689              * @param {Roo.bootstrap.DateField} thisthis
17690              * @param {Mixed} date The date value
17691              */
17692             show : true,
17693             /**
17694              * @event show
17695              * Fires when this field hide.
17696              * @param {Roo.bootstrap.DateField} this
17697              * @param {Mixed} date The date value
17698              */
17699             hide : true,
17700             /**
17701              * @event select
17702              * Fires when select a date.
17703              * @param {Roo.bootstrap.DateField} this
17704              * @param {Mixed} date The date value
17705              */
17706             select : true
17707         });
17708 };
17709
17710 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17711     
17712     /**
17713      * @cfg {String} format
17714      * The default time format string which can be overriden for localization support.  The format must be
17715      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17716      */
17717     format : "H:i",
17718        
17719     onRender: function(ct, position)
17720     {
17721         
17722         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17723                 
17724         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17725         
17726         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17727         
17728         this.pop = this.picker().select('>.datepicker-time',true).first();
17729         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17730         
17731         this.picker().on('mousedown', this.onMousedown, this);
17732         this.picker().on('click', this.onClick, this);
17733         
17734         this.picker().addClass('datepicker-dropdown');
17735     
17736         this.fillTime();
17737         this.update();
17738             
17739         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17740         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17741         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17742         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17743         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17744         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17745
17746     },
17747     
17748     fireKey: function(e){
17749         if (!this.picker().isVisible()){
17750             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17751                 this.show();
17752             }
17753             return;
17754         }
17755
17756         e.preventDefault();
17757         
17758         switch(e.keyCode){
17759             case 27: // escape
17760                 this.hide();
17761                 break;
17762             case 37: // left
17763             case 39: // right
17764                 this.onTogglePeriod();
17765                 break;
17766             case 38: // up
17767                 this.onIncrementMinutes();
17768                 break;
17769             case 40: // down
17770                 this.onDecrementMinutes();
17771                 break;
17772             case 13: // enter
17773             case 9: // tab
17774                 this.setTime();
17775                 break;
17776         }
17777     },
17778     
17779     onClick: function(e) {
17780         e.stopPropagation();
17781         e.preventDefault();
17782     },
17783     
17784     picker : function()
17785     {
17786         return this.el.select('.datepicker', true).first();
17787     },
17788     
17789     fillTime: function()
17790     {    
17791         var time = this.pop.select('tbody', true).first();
17792         
17793         time.dom.innerHTML = '';
17794         
17795         time.createChild({
17796             tag: 'tr',
17797             cn: [
17798                 {
17799                     tag: 'td',
17800                     cn: [
17801                         {
17802                             tag: 'a',
17803                             href: '#',
17804                             cls: 'btn',
17805                             cn: [
17806                                 {
17807                                     tag: 'span',
17808                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17809                                 }
17810                             ]
17811                         } 
17812                     ]
17813                 },
17814                 {
17815                     tag: 'td',
17816                     cls: 'separator'
17817                 },
17818                 {
17819                     tag: 'td',
17820                     cn: [
17821                         {
17822                             tag: 'a',
17823                             href: '#',
17824                             cls: 'btn',
17825                             cn: [
17826                                 {
17827                                     tag: 'span',
17828                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17829                                 }
17830                             ]
17831                         }
17832                     ]
17833                 },
17834                 {
17835                     tag: 'td',
17836                     cls: 'separator'
17837                 }
17838             ]
17839         });
17840         
17841         time.createChild({
17842             tag: 'tr',
17843             cn: [
17844                 {
17845                     tag: 'td',
17846                     cn: [
17847                         {
17848                             tag: 'span',
17849                             cls: 'timepicker-hour',
17850                             html: '00'
17851                         }  
17852                     ]
17853                 },
17854                 {
17855                     tag: 'td',
17856                     cls: 'separator',
17857                     html: ':'
17858                 },
17859                 {
17860                     tag: 'td',
17861                     cn: [
17862                         {
17863                             tag: 'span',
17864                             cls: 'timepicker-minute',
17865                             html: '00'
17866                         }  
17867                     ]
17868                 },
17869                 {
17870                     tag: 'td',
17871                     cls: 'separator'
17872                 },
17873                 {
17874                     tag: 'td',
17875                     cn: [
17876                         {
17877                             tag: 'button',
17878                             type: 'button',
17879                             cls: 'btn btn-primary period',
17880                             html: 'AM'
17881                             
17882                         }
17883                     ]
17884                 }
17885             ]
17886         });
17887         
17888         time.createChild({
17889             tag: 'tr',
17890             cn: [
17891                 {
17892                     tag: 'td',
17893                     cn: [
17894                         {
17895                             tag: 'a',
17896                             href: '#',
17897                             cls: 'btn',
17898                             cn: [
17899                                 {
17900                                     tag: 'span',
17901                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17902                                 }
17903                             ]
17904                         }
17905                     ]
17906                 },
17907                 {
17908                     tag: 'td',
17909                     cls: 'separator'
17910                 },
17911                 {
17912                     tag: 'td',
17913                     cn: [
17914                         {
17915                             tag: 'a',
17916                             href: '#',
17917                             cls: 'btn',
17918                             cn: [
17919                                 {
17920                                     tag: 'span',
17921                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17922                                 }
17923                             ]
17924                         }
17925                     ]
17926                 },
17927                 {
17928                     tag: 'td',
17929                     cls: 'separator'
17930                 }
17931             ]
17932         });
17933         
17934     },
17935     
17936     update: function()
17937     {
17938         
17939         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17940         
17941         this.fill();
17942     },
17943     
17944     fill: function() 
17945     {
17946         var hours = this.time.getHours();
17947         var minutes = this.time.getMinutes();
17948         var period = 'AM';
17949         
17950         if(hours > 11){
17951             period = 'PM';
17952         }
17953         
17954         if(hours == 0){
17955             hours = 12;
17956         }
17957         
17958         
17959         if(hours > 12){
17960             hours = hours - 12;
17961         }
17962         
17963         if(hours < 10){
17964             hours = '0' + hours;
17965         }
17966         
17967         if(minutes < 10){
17968             minutes = '0' + minutes;
17969         }
17970         
17971         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17972         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17973         this.pop.select('button', true).first().dom.innerHTML = period;
17974         
17975     },
17976     
17977     place: function()
17978     {   
17979         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17980         
17981         var cls = ['bottom'];
17982         
17983         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17984             cls.pop();
17985             cls.push('top');
17986         }
17987         
17988         cls.push('right');
17989         
17990         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17991             cls.pop();
17992             cls.push('left');
17993         }
17994         
17995         this.picker().addClass(cls.join('-'));
17996         
17997         var _this = this;
17998         
17999         Roo.each(cls, function(c){
18000             if(c == 'bottom'){
18001                 _this.picker().setTop(_this.inputEl().getHeight());
18002                 return;
18003             }
18004             if(c == 'top'){
18005                 _this.picker().setTop(0 - _this.picker().getHeight());
18006                 return;
18007             }
18008             
18009             if(c == 'left'){
18010                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18011                 return;
18012             }
18013             if(c == 'right'){
18014                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18015                 return;
18016             }
18017         });
18018         
18019     },
18020   
18021     onFocus : function()
18022     {
18023         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18024         this.show();
18025     },
18026     
18027     onBlur : function()
18028     {
18029         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18030         this.hide();
18031     },
18032     
18033     show : function()
18034     {
18035         this.picker().show();
18036         this.pop.show();
18037         this.update();
18038         this.place();
18039         
18040         this.fireEvent('show', this, this.date);
18041     },
18042     
18043     hide : function()
18044     {
18045         this.picker().hide();
18046         this.pop.hide();
18047         
18048         this.fireEvent('hide', this, this.date);
18049     },
18050     
18051     setTime : function()
18052     {
18053         this.hide();
18054         this.setValue(this.time.format(this.format));
18055         
18056         this.fireEvent('select', this, this.date);
18057         
18058         
18059     },
18060     
18061     onMousedown: function(e){
18062         e.stopPropagation();
18063         e.preventDefault();
18064     },
18065     
18066     onIncrementHours: function()
18067     {
18068         Roo.log('onIncrementHours');
18069         this.time = this.time.add(Date.HOUR, 1);
18070         this.update();
18071         
18072     },
18073     
18074     onDecrementHours: function()
18075     {
18076         Roo.log('onDecrementHours');
18077         this.time = this.time.add(Date.HOUR, -1);
18078         this.update();
18079     },
18080     
18081     onIncrementMinutes: function()
18082     {
18083         Roo.log('onIncrementMinutes');
18084         this.time = this.time.add(Date.MINUTE, 1);
18085         this.update();
18086     },
18087     
18088     onDecrementMinutes: function()
18089     {
18090         Roo.log('onDecrementMinutes');
18091         this.time = this.time.add(Date.MINUTE, -1);
18092         this.update();
18093     },
18094     
18095     onTogglePeriod: function()
18096     {
18097         Roo.log('onTogglePeriod');
18098         this.time = this.time.add(Date.HOUR, 12);
18099         this.update();
18100     }
18101     
18102    
18103 });
18104
18105 Roo.apply(Roo.bootstrap.TimeField,  {
18106     
18107     content : {
18108         tag: 'tbody',
18109         cn: [
18110             {
18111                 tag: 'tr',
18112                 cn: [
18113                 {
18114                     tag: 'td',
18115                     colspan: '7'
18116                 }
18117                 ]
18118             }
18119         ]
18120     },
18121     
18122     footer : {
18123         tag: 'tfoot',
18124         cn: [
18125             {
18126                 tag: 'tr',
18127                 cn: [
18128                 {
18129                     tag: 'th',
18130                     colspan: '7',
18131                     cls: '',
18132                     cn: [
18133                         {
18134                             tag: 'button',
18135                             cls: 'btn btn-info ok',
18136                             html: 'OK'
18137                         }
18138                     ]
18139                 }
18140
18141                 ]
18142             }
18143         ]
18144     }
18145 });
18146
18147 Roo.apply(Roo.bootstrap.TimeField,  {
18148   
18149     template : {
18150         tag: 'div',
18151         cls: 'datepicker dropdown-menu',
18152         cn: [
18153             {
18154                 tag: 'div',
18155                 cls: 'datepicker-time',
18156                 cn: [
18157                 {
18158                     tag: 'table',
18159                     cls: 'table-condensed',
18160                     cn:[
18161                     Roo.bootstrap.TimeField.content,
18162                     Roo.bootstrap.TimeField.footer
18163                     ]
18164                 }
18165                 ]
18166             }
18167         ]
18168     }
18169 });
18170
18171  
18172
18173  /*
18174  * - LGPL
18175  *
18176  * MonthField
18177  * 
18178  */
18179
18180 /**
18181  * @class Roo.bootstrap.MonthField
18182  * @extends Roo.bootstrap.Input
18183  * Bootstrap MonthField class
18184  * 
18185  * @cfg {String} language default en
18186  * 
18187  * @constructor
18188  * Create a new MonthField
18189  * @param {Object} config The config object
18190  */
18191
18192 Roo.bootstrap.MonthField = function(config){
18193     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18194     
18195     this.addEvents({
18196         /**
18197          * @event show
18198          * Fires when this field show.
18199          * @param {Roo.bootstrap.MonthField} this
18200          * @param {Mixed} date The date value
18201          */
18202         show : true,
18203         /**
18204          * @event show
18205          * Fires when this field hide.
18206          * @param {Roo.bootstrap.MonthField} this
18207          * @param {Mixed} date The date value
18208          */
18209         hide : true,
18210         /**
18211          * @event select
18212          * Fires when select a date.
18213          * @param {Roo.bootstrap.MonthField} this
18214          * @param {String} oldvalue The old value
18215          * @param {String} newvalue The new value
18216          */
18217         select : true
18218     });
18219 };
18220
18221 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18222     
18223     onRender: function(ct, position)
18224     {
18225         
18226         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18227         
18228         this.language = this.language || 'en';
18229         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18230         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18231         
18232         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18233         this.isInline = false;
18234         this.isInput = true;
18235         this.component = this.el.select('.add-on', true).first() || false;
18236         this.component = (this.component && this.component.length === 0) ? false : this.component;
18237         this.hasInput = this.component && this.inputEL().length;
18238         
18239         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18240         
18241         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18242         
18243         this.picker().on('mousedown', this.onMousedown, this);
18244         this.picker().on('click', this.onClick, this);
18245         
18246         this.picker().addClass('datepicker-dropdown');
18247         
18248         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18249             v.setStyle('width', '189px');
18250         });
18251         
18252         this.fillMonths();
18253         
18254         this.update();
18255         
18256         if(this.isInline) {
18257             this.show();
18258         }
18259         
18260     },
18261     
18262     setValue: function(v, suppressEvent)
18263     {   
18264         var o = this.getValue();
18265         
18266         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18267         
18268         this.update();
18269
18270         if(suppressEvent !== true){
18271             this.fireEvent('select', this, o, v);
18272         }
18273         
18274     },
18275     
18276     getValue: function()
18277     {
18278         return this.value;
18279     },
18280     
18281     onClick: function(e) 
18282     {
18283         e.stopPropagation();
18284         e.preventDefault();
18285         
18286         var target = e.getTarget();
18287         
18288         if(target.nodeName.toLowerCase() === 'i'){
18289             target = Roo.get(target).dom.parentNode;
18290         }
18291         
18292         var nodeName = target.nodeName;
18293         var className = target.className;
18294         var html = target.innerHTML;
18295         
18296         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18297             return;
18298         }
18299         
18300         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18301         
18302         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18303         
18304         this.hide();
18305                         
18306     },
18307     
18308     picker : function()
18309     {
18310         return this.pickerEl;
18311     },
18312     
18313     fillMonths: function()
18314     {    
18315         var i = 0;
18316         var months = this.picker().select('>.datepicker-months td', true).first();
18317         
18318         months.dom.innerHTML = '';
18319         
18320         while (i < 12) {
18321             var month = {
18322                 tag: 'span',
18323                 cls: 'month',
18324                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18325             };
18326             
18327             months.createChild(month);
18328         }
18329         
18330     },
18331     
18332     update: function()
18333     {
18334         var _this = this;
18335         
18336         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18337             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18338         }
18339         
18340         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18341             e.removeClass('active');
18342             
18343             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18344                 e.addClass('active');
18345             }
18346         })
18347     },
18348     
18349     place: function()
18350     {
18351         if(this.isInline) {
18352             return;
18353         }
18354         
18355         this.picker().removeClass(['bottom', 'top']);
18356         
18357         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18358             /*
18359              * place to the top of element!
18360              *
18361              */
18362             
18363             this.picker().addClass('top');
18364             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18365             
18366             return;
18367         }
18368         
18369         this.picker().addClass('bottom');
18370         
18371         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18372     },
18373     
18374     onFocus : function()
18375     {
18376         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18377         this.show();
18378     },
18379     
18380     onBlur : function()
18381     {
18382         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18383         
18384         var d = this.inputEl().getValue();
18385         
18386         this.setValue(d);
18387                 
18388         this.hide();
18389     },
18390     
18391     show : function()
18392     {
18393         this.picker().show();
18394         this.picker().select('>.datepicker-months', true).first().show();
18395         this.update();
18396         this.place();
18397         
18398         this.fireEvent('show', this, this.date);
18399     },
18400     
18401     hide : function()
18402     {
18403         if(this.isInline) {
18404             return;
18405         }
18406         this.picker().hide();
18407         this.fireEvent('hide', this, this.date);
18408         
18409     },
18410     
18411     onMousedown: function(e)
18412     {
18413         e.stopPropagation();
18414         e.preventDefault();
18415     },
18416     
18417     keyup: function(e)
18418     {
18419         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18420         this.update();
18421     },
18422
18423     fireKey: function(e)
18424     {
18425         if (!this.picker().isVisible()){
18426             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18427                 this.show();
18428             }
18429             return;
18430         }
18431         
18432         var dir;
18433         
18434         switch(e.keyCode){
18435             case 27: // escape
18436                 this.hide();
18437                 e.preventDefault();
18438                 break;
18439             case 37: // left
18440             case 39: // right
18441                 dir = e.keyCode == 37 ? -1 : 1;
18442                 
18443                 this.vIndex = this.vIndex + dir;
18444                 
18445                 if(this.vIndex < 0){
18446                     this.vIndex = 0;
18447                 }
18448                 
18449                 if(this.vIndex > 11){
18450                     this.vIndex = 11;
18451                 }
18452                 
18453                 if(isNaN(this.vIndex)){
18454                     this.vIndex = 0;
18455                 }
18456                 
18457                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18458                 
18459                 break;
18460             case 38: // up
18461             case 40: // down
18462                 
18463                 dir = e.keyCode == 38 ? -1 : 1;
18464                 
18465                 this.vIndex = this.vIndex + dir * 4;
18466                 
18467                 if(this.vIndex < 0){
18468                     this.vIndex = 0;
18469                 }
18470                 
18471                 if(this.vIndex > 11){
18472                     this.vIndex = 11;
18473                 }
18474                 
18475                 if(isNaN(this.vIndex)){
18476                     this.vIndex = 0;
18477                 }
18478                 
18479                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18480                 break;
18481                 
18482             case 13: // enter
18483                 
18484                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18485                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18486                 }
18487                 
18488                 this.hide();
18489                 e.preventDefault();
18490                 break;
18491             case 9: // tab
18492                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18493                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18494                 }
18495                 this.hide();
18496                 break;
18497             case 16: // shift
18498             case 17: // ctrl
18499             case 18: // alt
18500                 break;
18501             default :
18502                 this.hide();
18503                 
18504         }
18505     },
18506     
18507     remove: function() 
18508     {
18509         this.picker().remove();
18510     }
18511    
18512 });
18513
18514 Roo.apply(Roo.bootstrap.MonthField,  {
18515     
18516     content : {
18517         tag: 'tbody',
18518         cn: [
18519         {
18520             tag: 'tr',
18521             cn: [
18522             {
18523                 tag: 'td',
18524                 colspan: '7'
18525             }
18526             ]
18527         }
18528         ]
18529     },
18530     
18531     dates:{
18532         en: {
18533             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18534             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18535         }
18536     }
18537 });
18538
18539 Roo.apply(Roo.bootstrap.MonthField,  {
18540   
18541     template : {
18542         tag: 'div',
18543         cls: 'datepicker dropdown-menu roo-dynamic',
18544         cn: [
18545             {
18546                 tag: 'div',
18547                 cls: 'datepicker-months',
18548                 cn: [
18549                 {
18550                     tag: 'table',
18551                     cls: 'table-condensed',
18552                     cn:[
18553                         Roo.bootstrap.DateField.content
18554                     ]
18555                 }
18556                 ]
18557             }
18558         ]
18559     }
18560 });
18561
18562  
18563
18564  
18565  /*
18566  * - LGPL
18567  *
18568  * CheckBox
18569  * 
18570  */
18571
18572 /**
18573  * @class Roo.bootstrap.CheckBox
18574  * @extends Roo.bootstrap.Input
18575  * Bootstrap CheckBox class
18576  * 
18577  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18578  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18579  * @cfg {String} boxLabel The text that appears beside the checkbox
18580  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18581  * @cfg {Boolean} checked initnal the element
18582  * @cfg {Boolean} inline inline the element (default false)
18583  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18584  * 
18585  * @constructor
18586  * Create a new CheckBox
18587  * @param {Object} config The config object
18588  */
18589
18590 Roo.bootstrap.CheckBox = function(config){
18591     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18592    
18593     this.addEvents({
18594         /**
18595         * @event check
18596         * Fires when the element is checked or unchecked.
18597         * @param {Roo.bootstrap.CheckBox} this This input
18598         * @param {Boolean} checked The new checked value
18599         */
18600        check : true
18601     });
18602     
18603 };
18604
18605 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18606   
18607     inputType: 'checkbox',
18608     inputValue: 1,
18609     valueOff: 0,
18610     boxLabel: false,
18611     checked: false,
18612     weight : false,
18613     inline: false,
18614     
18615     getAutoCreate : function()
18616     {
18617         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18618         
18619         var id = Roo.id();
18620         
18621         var cfg = {};
18622         
18623         cfg.cls = 'form-group ' + this.inputType; //input-group
18624         
18625         if(this.inline){
18626             cfg.cls += ' ' + this.inputType + '-inline';
18627         }
18628         
18629         var input =  {
18630             tag: 'input',
18631             id : id,
18632             type : this.inputType,
18633             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18634             cls : 'roo-' + this.inputType, //'form-box',
18635             placeholder : this.placeholder || ''
18636             
18637         };
18638         
18639         if (this.weight) { // Validity check?
18640             cfg.cls += " " + this.inputType + "-" + this.weight;
18641         }
18642         
18643         if (this.disabled) {
18644             input.disabled=true;
18645         }
18646         
18647         if(this.checked){
18648             input.checked = this.checked;
18649         }
18650         
18651         if (this.name) {
18652             input.name = this.name;
18653         }
18654         
18655         if (this.size) {
18656             input.cls += ' input-' + this.size;
18657         }
18658         
18659         var settings=this;
18660         
18661         ['xs','sm','md','lg'].map(function(size){
18662             if (settings[size]) {
18663                 cfg.cls += ' col-' + size + '-' + settings[size];
18664             }
18665         });
18666         
18667         var inputblock = input;
18668          
18669         if (this.before || this.after) {
18670             
18671             inputblock = {
18672                 cls : 'input-group',
18673                 cn :  [] 
18674             };
18675             
18676             if (this.before) {
18677                 inputblock.cn.push({
18678                     tag :'span',
18679                     cls : 'input-group-addon',
18680                     html : this.before
18681                 });
18682             }
18683             
18684             inputblock.cn.push(input);
18685             
18686             if (this.after) {
18687                 inputblock.cn.push({
18688                     tag :'span',
18689                     cls : 'input-group-addon',
18690                     html : this.after
18691                 });
18692             }
18693             
18694         }
18695         
18696         if (align ==='left' && this.fieldLabel.length) {
18697 //                Roo.log("left and has label");
18698                 cfg.cn = [
18699                     
18700                     {
18701                         tag: 'label',
18702                         'for' :  id,
18703                         cls : 'control-label col-md-' + this.labelWidth,
18704                         html : this.fieldLabel
18705                         
18706                     },
18707                     {
18708                         cls : "col-md-" + (12 - this.labelWidth), 
18709                         cn: [
18710                             inputblock
18711                         ]
18712                     }
18713                     
18714                 ];
18715         } else if ( this.fieldLabel.length) {
18716 //                Roo.log(" label");
18717                 cfg.cn = [
18718                    
18719                     {
18720                         tag: this.boxLabel ? 'span' : 'label',
18721                         'for': id,
18722                         cls: 'control-label box-input-label',
18723                         //cls : 'input-group-addon',
18724                         html : this.fieldLabel
18725                         
18726                     },
18727                     
18728                     inputblock
18729                     
18730                 ];
18731
18732         } else {
18733             
18734 //                Roo.log(" no label && no align");
18735                 cfg.cn = [  inputblock ] ;
18736                 
18737                 
18738         }
18739         if(this.boxLabel){
18740              var boxLabelCfg = {
18741                 tag: 'label',
18742                 //'for': id, // box label is handled by onclick - so no for...
18743                 cls: 'box-label',
18744                 html: this.boxLabel
18745             };
18746             
18747             if(this.tooltip){
18748                 boxLabelCfg.tooltip = this.tooltip;
18749             }
18750              
18751             cfg.cn.push(boxLabelCfg);
18752         }
18753         
18754         
18755        
18756         return cfg;
18757         
18758     },
18759     
18760     /**
18761      * return the real input element.
18762      */
18763     inputEl: function ()
18764     {
18765         return this.el.select('input.roo-' + this.inputType,true).first();
18766     },
18767     
18768     labelEl: function()
18769     {
18770         return this.el.select('label.control-label',true).first();
18771     },
18772     /* depricated... */
18773     
18774     label: function()
18775     {
18776         return this.labelEl();
18777     },
18778     
18779     initEvents : function()
18780     {
18781 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18782         
18783         this.inputEl().on('click', this.onClick,  this);
18784         
18785         if (this.boxLabel) { 
18786             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18787         }
18788         
18789         this.startValue = this.getValue();
18790         
18791         if(this.groupId){
18792             Roo.bootstrap.CheckBox.register(this);
18793         }
18794     },
18795     
18796     onClick : function()
18797     {   
18798         this.setChecked(!this.checked);
18799     },
18800     
18801     setChecked : function(state,suppressEvent)
18802     {
18803         this.startValue = this.getValue();
18804         
18805         if(this.inputType == 'radio'){
18806             
18807             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18808                 e.dom.checked = false;
18809             });
18810             
18811             this.inputEl().dom.checked = true;
18812             
18813             this.inputEl().dom.value = this.inputValue;
18814             
18815             if(suppressEvent !== true){
18816                 this.fireEvent('check', this, true);
18817             }
18818             
18819             this.validate();
18820             
18821             return;
18822         }
18823         
18824         this.checked = state;
18825         
18826         this.inputEl().dom.checked = state;
18827         
18828         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18829         
18830         if(suppressEvent !== true){
18831             this.fireEvent('check', this, state);
18832         }
18833         
18834         this.validate();
18835     },
18836     
18837     getValue : function()
18838     {
18839         if(this.inputType == 'radio'){
18840             return this.getGroupValue();
18841         }
18842         
18843         return this.inputEl().getValue();
18844         
18845     },
18846     
18847     getGroupValue : function()
18848     {
18849         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18850             return '';
18851         }
18852         
18853         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18854     },
18855     
18856     setValue : function(v,suppressEvent)
18857     {
18858         if(this.inputType == 'radio'){
18859             this.setGroupValue(v, suppressEvent);
18860             return;
18861         }
18862         
18863         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18864         
18865         this.validate();
18866     },
18867     
18868     setGroupValue : function(v, suppressEvent)
18869     {
18870         this.startValue = this.getValue();
18871         
18872         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18873             e.dom.checked = false;
18874             
18875             if(e.dom.value == v){
18876                 e.dom.checked = true;
18877             }
18878         });
18879         
18880         if(suppressEvent !== true){
18881             this.fireEvent('check', this, true);
18882         }
18883
18884         this.validate();
18885         
18886         return;
18887     },
18888     
18889     validate : function()
18890     {
18891         if(
18892                 this.disabled || 
18893                 (this.inputType == 'radio' && this.validateRadio()) ||
18894                 (this.inputType == 'checkbox' && this.validateCheckbox())
18895         ){
18896             this.markValid();
18897             return true;
18898         }
18899         
18900         this.markInvalid();
18901         return false;
18902     },
18903     
18904     validateRadio : function()
18905     {
18906         var valid = false;
18907         
18908         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18909             if(!e.dom.checked){
18910                 return;
18911             }
18912             
18913             valid = true;
18914             
18915             return false;
18916         });
18917         
18918         return valid;
18919     },
18920     
18921     validateCheckbox : function()
18922     {
18923         if(!this.groupId){
18924             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18925         }
18926         
18927         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18928         
18929         if(!group){
18930             return false;
18931         }
18932         
18933         var r = false;
18934         
18935         for(var i in group){
18936             if(r){
18937                 break;
18938             }
18939             
18940             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18941         }
18942         
18943         return r;
18944     },
18945     
18946     /**
18947      * Mark this field as valid
18948      */
18949     markValid : function()
18950     {
18951         if(this.allowBlank){
18952             return;
18953         }
18954         
18955         var _this = this;
18956         
18957         this.fireEvent('valid', this);
18958         
18959         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18960         
18961         if(this.groupId){
18962             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18963         }
18964         
18965         if(label){
18966             label.markValid();
18967         }
18968         
18969         if(this.inputType == 'radio'){
18970             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18971                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18972                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18973             });
18974             
18975             return;
18976         }
18977         
18978         if(!this.groupId){
18979             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18980             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18981             return;
18982         }
18983         
18984         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18985             
18986         if(!group){
18987             return;
18988         }
18989         
18990         for(var i in group){
18991             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18992             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18993         }
18994     },
18995     
18996      /**
18997      * Mark this field as invalid
18998      * @param {String} msg The validation message
18999      */
19000     markInvalid : function(msg)
19001     {
19002         if(this.allowBlank){
19003             return;
19004         }
19005         
19006         var _this = this;
19007         
19008         this.fireEvent('invalid', this, msg);
19009         
19010         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19011         
19012         if(this.groupId){
19013             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19014         }
19015         
19016         if(label){
19017             label.markInvalid();
19018         }
19019             
19020         if(this.inputType == 'radio'){
19021             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19022                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19023                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19024             });
19025             
19026             return;
19027         }
19028         
19029         if(!this.groupId){
19030             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19031             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19032             return;
19033         }
19034         
19035         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19036         
19037         if(!group){
19038             return;
19039         }
19040         
19041         for(var i in group){
19042             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19043             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19044         }
19045         
19046     }
19047     
19048 });
19049
19050 Roo.apply(Roo.bootstrap.CheckBox, {
19051     
19052     groups: {},
19053     
19054      /**
19055     * register a CheckBox Group
19056     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19057     */
19058     register : function(checkbox)
19059     {
19060         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19061             this.groups[checkbox.groupId] = {};
19062         }
19063         
19064         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19065             return;
19066         }
19067         
19068         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19069         
19070     },
19071     /**
19072     * fetch a CheckBox Group based on the group ID
19073     * @param {string} the group ID
19074     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19075     */
19076     get: function(groupId) {
19077         if (typeof(this.groups[groupId]) == 'undefined') {
19078             return false;
19079         }
19080         
19081         return this.groups[groupId] ;
19082     }
19083     
19084     
19085 });
19086 /*
19087  * - LGPL
19088  *
19089  * Radio
19090  *
19091  *
19092  * not inline
19093  *<div class="radio">
19094   <label>
19095     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19096     Option one is this and that&mdash;be sure to include why it's great
19097   </label>
19098 </div>
19099  *
19100  *
19101  *inline
19102  *<span>
19103  *<label class="radio-inline">fieldLabel</label>
19104  *<label class="radio-inline">
19105   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19106 </label>
19107 <span>
19108  * 
19109  * 
19110  */
19111
19112 /**
19113  * @class Roo.bootstrap.Radio
19114  * @extends Roo.bootstrap.CheckBox
19115  * Bootstrap Radio class
19116
19117  * @constructor
19118  * Create a new Radio
19119  * @param {Object} config The config object
19120  */
19121
19122 Roo.bootstrap.Radio = function(config){
19123     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19124    
19125 };
19126
19127 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19128     
19129     inputType: 'radio',
19130     inputValue: '',
19131     valueOff: '',
19132     
19133     getAutoCreate : function()
19134     {
19135         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19136         align = align || 'left'; // default...
19137         
19138         
19139         
19140         var id = Roo.id();
19141         
19142         var cfg = {
19143                 tag : this.inline ? 'span' : 'div',
19144                 cls : '',
19145                 cn : []
19146         };
19147         
19148         var inline = this.inline ? ' radio-inline' : '';
19149         
19150         var lbl = {
19151                 tag: 'label' ,
19152                 // does not need for, as we wrap the input with it..
19153                 'for' : id,
19154                 cls : 'control-label box-label' + inline,
19155                 cn : []
19156         };
19157         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19158         
19159         var fieldLabel = {
19160             tag: 'label' ,
19161             //cls : 'control-label' + inline,
19162             html : this.fieldLabel,
19163             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19164         };
19165         
19166  
19167         
19168         
19169         var input =  {
19170             tag: 'input',
19171             id : id,
19172             type : this.inputType,
19173             //value : (!this.checked) ? this.valueOff : this.inputValue,
19174             value : this.inputValue,
19175             cls : 'roo-radio',
19176             placeholder : this.placeholder || '' // ?? needed????
19177             
19178         };
19179         if (this.weight) { // Validity check?
19180             input.cls += " radio-" + this.weight;
19181         }
19182         if (this.disabled) {
19183             input.disabled=true;
19184         }
19185         
19186         if(this.checked){
19187             input.checked = this.checked;
19188         }
19189         
19190         if (this.name) {
19191             input.name = this.name;
19192         }
19193         
19194         if (this.size) {
19195             input.cls += ' input-' + this.size;
19196         }
19197         
19198         //?? can span's inline have a width??
19199         
19200         var settings=this;
19201         ['xs','sm','md','lg'].map(function(size){
19202             if (settings[size]) {
19203                 cfg.cls += ' col-' + size + '-' + settings[size];
19204             }
19205         });
19206         
19207         var inputblock = input;
19208         
19209         if (this.before || this.after) {
19210             
19211             inputblock = {
19212                 cls : 'input-group',
19213                 tag : 'span',
19214                 cn :  [] 
19215             };
19216             if (this.before) {
19217                 inputblock.cn.push({
19218                     tag :'span',
19219                     cls : 'input-group-addon',
19220                     html : this.before
19221                 });
19222             }
19223             inputblock.cn.push(input);
19224             if (this.after) {
19225                 inputblock.cn.push({
19226                     tag :'span',
19227                     cls : 'input-group-addon',
19228                     html : this.after
19229                 });
19230             }
19231             
19232         };
19233         
19234         
19235         if (this.fieldLabel && this.fieldLabel.length) {
19236             cfg.cn.push(fieldLabel);
19237         }
19238        
19239         // normal bootstrap puts the input inside the label.
19240         // however with our styled version - it has to go after the input.
19241        
19242         //lbl.cn.push(inputblock);
19243         
19244         var lblwrap =  {
19245             tag: 'span',
19246             cls: 'radio' + inline,
19247             cn: [
19248                 inputblock,
19249                 lbl
19250             ]
19251         };
19252         
19253         cfg.cn.push( lblwrap);
19254         
19255         if(this.boxLabel){
19256             lbl.cn.push({
19257                 tag: 'span',
19258                 html: this.boxLabel
19259             })
19260         }
19261          
19262         
19263         return cfg;
19264         
19265     },
19266     
19267     initEvents : function()
19268     {
19269 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19270         
19271         this.inputEl().on('click', this.onClick,  this);
19272         if (this.boxLabel) {
19273             //Roo.log('find label');
19274             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19275         }
19276         
19277     },
19278     
19279     inputEl: function ()
19280     {
19281         return this.el.select('input.roo-radio',true).first();
19282     },
19283     onClick : function()
19284     {   
19285         Roo.log("click");
19286         this.setChecked(true);
19287     },
19288     
19289     setChecked : function(state,suppressEvent)
19290     {
19291         if(state){
19292             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19293                 v.dom.checked = false;
19294             });
19295         }
19296         Roo.log(this.inputEl().dom);
19297         this.checked = state;
19298         this.inputEl().dom.checked = state;
19299         
19300         if(suppressEvent !== true){
19301             this.fireEvent('check', this, state);
19302         }
19303         
19304         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19305         
19306     },
19307     
19308     getGroupValue : function()
19309     {
19310         var value = '';
19311         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19312             if(v.dom.checked == true){
19313                 value = v.dom.value;
19314             }
19315         });
19316         
19317         return value;
19318     },
19319     
19320     /**
19321      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19322      * @return {Mixed} value The field value
19323      */
19324     getValue : function(){
19325         return this.getGroupValue();
19326     }
19327     
19328 });
19329
19330  
19331 //<script type="text/javascript">
19332
19333 /*
19334  * Based  Ext JS Library 1.1.1
19335  * Copyright(c) 2006-2007, Ext JS, LLC.
19336  * LGPL
19337  *
19338  */
19339  
19340 /**
19341  * @class Roo.HtmlEditorCore
19342  * @extends Roo.Component
19343  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19344  *
19345  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19346  */
19347
19348 Roo.HtmlEditorCore = function(config){
19349     
19350     
19351     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19352     
19353     
19354     this.addEvents({
19355         /**
19356          * @event initialize
19357          * Fires when the editor is fully initialized (including the iframe)
19358          * @param {Roo.HtmlEditorCore} this
19359          */
19360         initialize: true,
19361         /**
19362          * @event activate
19363          * Fires when the editor is first receives the focus. Any insertion must wait
19364          * until after this event.
19365          * @param {Roo.HtmlEditorCore} this
19366          */
19367         activate: true,
19368          /**
19369          * @event beforesync
19370          * Fires before the textarea is updated with content from the editor iframe. Return false
19371          * to cancel the sync.
19372          * @param {Roo.HtmlEditorCore} this
19373          * @param {String} html
19374          */
19375         beforesync: true,
19376          /**
19377          * @event beforepush
19378          * Fires before the iframe editor is updated with content from the textarea. Return false
19379          * to cancel the push.
19380          * @param {Roo.HtmlEditorCore} this
19381          * @param {String} html
19382          */
19383         beforepush: true,
19384          /**
19385          * @event sync
19386          * Fires when the textarea is updated with content from the editor iframe.
19387          * @param {Roo.HtmlEditorCore} this
19388          * @param {String} html
19389          */
19390         sync: true,
19391          /**
19392          * @event push
19393          * Fires when the iframe editor is updated with content from the textarea.
19394          * @param {Roo.HtmlEditorCore} this
19395          * @param {String} html
19396          */
19397         push: true,
19398         
19399         /**
19400          * @event editorevent
19401          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19402          * @param {Roo.HtmlEditorCore} this
19403          */
19404         editorevent: true
19405         
19406     });
19407     
19408     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19409     
19410     // defaults : white / black...
19411     this.applyBlacklists();
19412     
19413     
19414     
19415 };
19416
19417
19418 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19419
19420
19421      /**
19422      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19423      */
19424     
19425     owner : false,
19426     
19427      /**
19428      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19429      *                        Roo.resizable.
19430      */
19431     resizable : false,
19432      /**
19433      * @cfg {Number} height (in pixels)
19434      */   
19435     height: 300,
19436    /**
19437      * @cfg {Number} width (in pixels)
19438      */   
19439     width: 500,
19440     
19441     /**
19442      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19443      * 
19444      */
19445     stylesheets: false,
19446     
19447     // id of frame..
19448     frameId: false,
19449     
19450     // private properties
19451     validationEvent : false,
19452     deferHeight: true,
19453     initialized : false,
19454     activated : false,
19455     sourceEditMode : false,
19456     onFocus : Roo.emptyFn,
19457     iframePad:3,
19458     hideMode:'offsets',
19459     
19460     clearUp: true,
19461     
19462     // blacklist + whitelisted elements..
19463     black: false,
19464     white: false,
19465      
19466     
19467
19468     /**
19469      * Protected method that will not generally be called directly. It
19470      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19471      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19472      */
19473     getDocMarkup : function(){
19474         // body styles..
19475         var st = '';
19476         
19477         // inherit styels from page...?? 
19478         if (this.stylesheets === false) {
19479             
19480             Roo.get(document.head).select('style').each(function(node) {
19481                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19482             });
19483             
19484             Roo.get(document.head).select('link').each(function(node) { 
19485                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19486             });
19487             
19488         } else if (!this.stylesheets.length) {
19489                 // simple..
19490                 st = '<style type="text/css">' +
19491                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19492                    '</style>';
19493         } else { 
19494             
19495         }
19496         
19497         st +=  '<style type="text/css">' +
19498             'IMG { cursor: pointer } ' +
19499         '</style>';
19500
19501         
19502         return '<html><head>' + st  +
19503             //<style type="text/css">' +
19504             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19505             //'</style>' +
19506             ' </head><body class="roo-htmleditor-body"></body></html>';
19507     },
19508
19509     // private
19510     onRender : function(ct, position)
19511     {
19512         var _t = this;
19513         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19514         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19515         
19516         
19517         this.el.dom.style.border = '0 none';
19518         this.el.dom.setAttribute('tabIndex', -1);
19519         this.el.addClass('x-hidden hide');
19520         
19521         
19522         
19523         if(Roo.isIE){ // fix IE 1px bogus margin
19524             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19525         }
19526        
19527         
19528         this.frameId = Roo.id();
19529         
19530          
19531         
19532         var iframe = this.owner.wrap.createChild({
19533             tag: 'iframe',
19534             cls: 'form-control', // bootstrap..
19535             id: this.frameId,
19536             name: this.frameId,
19537             frameBorder : 'no',
19538             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19539         }, this.el
19540         );
19541         
19542         
19543         this.iframe = iframe.dom;
19544
19545          this.assignDocWin();
19546         
19547         this.doc.designMode = 'on';
19548        
19549         this.doc.open();
19550         this.doc.write(this.getDocMarkup());
19551         this.doc.close();
19552
19553         
19554         var task = { // must defer to wait for browser to be ready
19555             run : function(){
19556                 //console.log("run task?" + this.doc.readyState);
19557                 this.assignDocWin();
19558                 if(this.doc.body || this.doc.readyState == 'complete'){
19559                     try {
19560                         this.doc.designMode="on";
19561                     } catch (e) {
19562                         return;
19563                     }
19564                     Roo.TaskMgr.stop(task);
19565                     this.initEditor.defer(10, this);
19566                 }
19567             },
19568             interval : 10,
19569             duration: 10000,
19570             scope: this
19571         };
19572         Roo.TaskMgr.start(task);
19573
19574     },
19575
19576     // private
19577     onResize : function(w, h)
19578     {
19579          Roo.log('resize: ' +w + ',' + h );
19580         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19581         if(!this.iframe){
19582             return;
19583         }
19584         if(typeof w == 'number'){
19585             
19586             this.iframe.style.width = w + 'px';
19587         }
19588         if(typeof h == 'number'){
19589             
19590             this.iframe.style.height = h + 'px';
19591             if(this.doc){
19592                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19593             }
19594         }
19595         
19596     },
19597
19598     /**
19599      * Toggles the editor between standard and source edit mode.
19600      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19601      */
19602     toggleSourceEdit : function(sourceEditMode){
19603         
19604         this.sourceEditMode = sourceEditMode === true;
19605         
19606         if(this.sourceEditMode){
19607  
19608             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19609             
19610         }else{
19611             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19612             //this.iframe.className = '';
19613             this.deferFocus();
19614         }
19615         //this.setSize(this.owner.wrap.getSize());
19616         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19617     },
19618
19619     
19620   
19621
19622     /**
19623      * Protected method that will not generally be called directly. If you need/want
19624      * custom HTML cleanup, this is the method you should override.
19625      * @param {String} html The HTML to be cleaned
19626      * return {String} The cleaned HTML
19627      */
19628     cleanHtml : function(html){
19629         html = String(html);
19630         if(html.length > 5){
19631             if(Roo.isSafari){ // strip safari nonsense
19632                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19633             }
19634         }
19635         if(html == '&nbsp;'){
19636             html = '';
19637         }
19638         return html;
19639     },
19640
19641     /**
19642      * HTML Editor -> Textarea
19643      * Protected method that will not generally be called directly. Syncs the contents
19644      * of the editor iframe with the textarea.
19645      */
19646     syncValue : function(){
19647         if(this.initialized){
19648             var bd = (this.doc.body || this.doc.documentElement);
19649             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19650             var html = bd.innerHTML;
19651             if(Roo.isSafari){
19652                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19653                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19654                 if(m && m[1]){
19655                     html = '<div style="'+m[0]+'">' + html + '</div>';
19656                 }
19657             }
19658             html = this.cleanHtml(html);
19659             // fix up the special chars.. normaly like back quotes in word...
19660             // however we do not want to do this with chinese..
19661             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19662                 var cc = b.charCodeAt();
19663                 if (
19664                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19665                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19666                     (cc >= 0xf900 && cc < 0xfb00 )
19667                 ) {
19668                         return b;
19669                 }
19670                 return "&#"+cc+";" 
19671             });
19672             if(this.owner.fireEvent('beforesync', this, html) !== false){
19673                 this.el.dom.value = html;
19674                 this.owner.fireEvent('sync', this, html);
19675             }
19676         }
19677     },
19678
19679     /**
19680      * Protected method that will not generally be called directly. Pushes the value of the textarea
19681      * into the iframe editor.
19682      */
19683     pushValue : function(){
19684         if(this.initialized){
19685             var v = this.el.dom.value.trim();
19686             
19687 //            if(v.length < 1){
19688 //                v = '&#160;';
19689 //            }
19690             
19691             if(this.owner.fireEvent('beforepush', this, v) !== false){
19692                 var d = (this.doc.body || this.doc.documentElement);
19693                 d.innerHTML = v;
19694                 this.cleanUpPaste();
19695                 this.el.dom.value = d.innerHTML;
19696                 this.owner.fireEvent('push', this, v);
19697             }
19698         }
19699     },
19700
19701     // private
19702     deferFocus : function(){
19703         this.focus.defer(10, this);
19704     },
19705
19706     // doc'ed in Field
19707     focus : function(){
19708         if(this.win && !this.sourceEditMode){
19709             this.win.focus();
19710         }else{
19711             this.el.focus();
19712         }
19713     },
19714     
19715     assignDocWin: function()
19716     {
19717         var iframe = this.iframe;
19718         
19719          if(Roo.isIE){
19720             this.doc = iframe.contentWindow.document;
19721             this.win = iframe.contentWindow;
19722         } else {
19723 //            if (!Roo.get(this.frameId)) {
19724 //                return;
19725 //            }
19726 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19727 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19728             
19729             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19730                 return;
19731             }
19732             
19733             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19734             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19735         }
19736     },
19737     
19738     // private
19739     initEditor : function(){
19740         //console.log("INIT EDITOR");
19741         this.assignDocWin();
19742         
19743         
19744         
19745         this.doc.designMode="on";
19746         this.doc.open();
19747         this.doc.write(this.getDocMarkup());
19748         this.doc.close();
19749         
19750         var dbody = (this.doc.body || this.doc.documentElement);
19751         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19752         // this copies styles from the containing element into thsi one..
19753         // not sure why we need all of this..
19754         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19755         
19756         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19757         //ss['background-attachment'] = 'fixed'; // w3c
19758         dbody.bgProperties = 'fixed'; // ie
19759         //Roo.DomHelper.applyStyles(dbody, ss);
19760         Roo.EventManager.on(this.doc, {
19761             //'mousedown': this.onEditorEvent,
19762             'mouseup': this.onEditorEvent,
19763             'dblclick': this.onEditorEvent,
19764             'click': this.onEditorEvent,
19765             'keyup': this.onEditorEvent,
19766             buffer:100,
19767             scope: this
19768         });
19769         if(Roo.isGecko){
19770             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19771         }
19772         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19773             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19774         }
19775         this.initialized = true;
19776
19777         this.owner.fireEvent('initialize', this);
19778         this.pushValue();
19779     },
19780
19781     // private
19782     onDestroy : function(){
19783         
19784         
19785         
19786         if(this.rendered){
19787             
19788             //for (var i =0; i < this.toolbars.length;i++) {
19789             //    // fixme - ask toolbars for heights?
19790             //    this.toolbars[i].onDestroy();
19791            // }
19792             
19793             //this.wrap.dom.innerHTML = '';
19794             //this.wrap.remove();
19795         }
19796     },
19797
19798     // private
19799     onFirstFocus : function(){
19800         
19801         this.assignDocWin();
19802         
19803         
19804         this.activated = true;
19805          
19806     
19807         if(Roo.isGecko){ // prevent silly gecko errors
19808             this.win.focus();
19809             var s = this.win.getSelection();
19810             if(!s.focusNode || s.focusNode.nodeType != 3){
19811                 var r = s.getRangeAt(0);
19812                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19813                 r.collapse(true);
19814                 this.deferFocus();
19815             }
19816             try{
19817                 this.execCmd('useCSS', true);
19818                 this.execCmd('styleWithCSS', false);
19819             }catch(e){}
19820         }
19821         this.owner.fireEvent('activate', this);
19822     },
19823
19824     // private
19825     adjustFont: function(btn){
19826         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19827         //if(Roo.isSafari){ // safari
19828         //    adjust *= 2;
19829        // }
19830         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19831         if(Roo.isSafari){ // safari
19832             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19833             v =  (v < 10) ? 10 : v;
19834             v =  (v > 48) ? 48 : v;
19835             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19836             
19837         }
19838         
19839         
19840         v = Math.max(1, v+adjust);
19841         
19842         this.execCmd('FontSize', v  );
19843     },
19844
19845     onEditorEvent : function(e)
19846     {
19847         this.owner.fireEvent('editorevent', this, e);
19848       //  this.updateToolbar();
19849         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19850     },
19851
19852     insertTag : function(tg)
19853     {
19854         // could be a bit smarter... -> wrap the current selected tRoo..
19855         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19856             
19857             range = this.createRange(this.getSelection());
19858             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19859             wrappingNode.appendChild(range.extractContents());
19860             range.insertNode(wrappingNode);
19861
19862             return;
19863             
19864             
19865             
19866         }
19867         this.execCmd("formatblock",   tg);
19868         
19869     },
19870     
19871     insertText : function(txt)
19872     {
19873         
19874         
19875         var range = this.createRange();
19876         range.deleteContents();
19877                //alert(Sender.getAttribute('label'));
19878                
19879         range.insertNode(this.doc.createTextNode(txt));
19880     } ,
19881     
19882      
19883
19884     /**
19885      * Executes a Midas editor command on the editor document and performs necessary focus and
19886      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19887      * @param {String} cmd The Midas command
19888      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19889      */
19890     relayCmd : function(cmd, value){
19891         this.win.focus();
19892         this.execCmd(cmd, value);
19893         this.owner.fireEvent('editorevent', this);
19894         //this.updateToolbar();
19895         this.owner.deferFocus();
19896     },
19897
19898     /**
19899      * Executes a Midas editor command directly on the editor document.
19900      * For visual commands, you should use {@link #relayCmd} instead.
19901      * <b>This should only be called after the editor is initialized.</b>
19902      * @param {String} cmd The Midas command
19903      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19904      */
19905     execCmd : function(cmd, value){
19906         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19907         this.syncValue();
19908     },
19909  
19910  
19911    
19912     /**
19913      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19914      * to insert tRoo.
19915      * @param {String} text | dom node.. 
19916      */
19917     insertAtCursor : function(text)
19918     {
19919         
19920         
19921         
19922         if(!this.activated){
19923             return;
19924         }
19925         /*
19926         if(Roo.isIE){
19927             this.win.focus();
19928             var r = this.doc.selection.createRange();
19929             if(r){
19930                 r.collapse(true);
19931                 r.pasteHTML(text);
19932                 this.syncValue();
19933                 this.deferFocus();
19934             
19935             }
19936             return;
19937         }
19938         */
19939         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19940             this.win.focus();
19941             
19942             
19943             // from jquery ui (MIT licenced)
19944             var range, node;
19945             var win = this.win;
19946             
19947             if (win.getSelection && win.getSelection().getRangeAt) {
19948                 range = win.getSelection().getRangeAt(0);
19949                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19950                 range.insertNode(node);
19951             } else if (win.document.selection && win.document.selection.createRange) {
19952                 // no firefox support
19953                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19954                 win.document.selection.createRange().pasteHTML(txt);
19955             } else {
19956                 // no firefox support
19957                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19958                 this.execCmd('InsertHTML', txt);
19959             } 
19960             
19961             this.syncValue();
19962             
19963             this.deferFocus();
19964         }
19965     },
19966  // private
19967     mozKeyPress : function(e){
19968         if(e.ctrlKey){
19969             var c = e.getCharCode(), cmd;
19970           
19971             if(c > 0){
19972                 c = String.fromCharCode(c).toLowerCase();
19973                 switch(c){
19974                     case 'b':
19975                         cmd = 'bold';
19976                         break;
19977                     case 'i':
19978                         cmd = 'italic';
19979                         break;
19980                     
19981                     case 'u':
19982                         cmd = 'underline';
19983                         break;
19984                     
19985                     case 'v':
19986                         this.cleanUpPaste.defer(100, this);
19987                         return;
19988                         
19989                 }
19990                 if(cmd){
19991                     this.win.focus();
19992                     this.execCmd(cmd);
19993                     this.deferFocus();
19994                     e.preventDefault();
19995                 }
19996                 
19997             }
19998         }
19999     },
20000
20001     // private
20002     fixKeys : function(){ // load time branching for fastest keydown performance
20003         if(Roo.isIE){
20004             return function(e){
20005                 var k = e.getKey(), r;
20006                 if(k == e.TAB){
20007                     e.stopEvent();
20008                     r = this.doc.selection.createRange();
20009                     if(r){
20010                         r.collapse(true);
20011                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20012                         this.deferFocus();
20013                     }
20014                     return;
20015                 }
20016                 
20017                 if(k == e.ENTER){
20018                     r = this.doc.selection.createRange();
20019                     if(r){
20020                         var target = r.parentElement();
20021                         if(!target || target.tagName.toLowerCase() != 'li'){
20022                             e.stopEvent();
20023                             r.pasteHTML('<br />');
20024                             r.collapse(false);
20025                             r.select();
20026                         }
20027                     }
20028                 }
20029                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20030                     this.cleanUpPaste.defer(100, this);
20031                     return;
20032                 }
20033                 
20034                 
20035             };
20036         }else if(Roo.isOpera){
20037             return function(e){
20038                 var k = e.getKey();
20039                 if(k == e.TAB){
20040                     e.stopEvent();
20041                     this.win.focus();
20042                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20043                     this.deferFocus();
20044                 }
20045                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20046                     this.cleanUpPaste.defer(100, this);
20047                     return;
20048                 }
20049                 
20050             };
20051         }else if(Roo.isSafari){
20052             return function(e){
20053                 var k = e.getKey();
20054                 
20055                 if(k == e.TAB){
20056                     e.stopEvent();
20057                     this.execCmd('InsertText','\t');
20058                     this.deferFocus();
20059                     return;
20060                 }
20061                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20062                     this.cleanUpPaste.defer(100, this);
20063                     return;
20064                 }
20065                 
20066              };
20067         }
20068     }(),
20069     
20070     getAllAncestors: function()
20071     {
20072         var p = this.getSelectedNode();
20073         var a = [];
20074         if (!p) {
20075             a.push(p); // push blank onto stack..
20076             p = this.getParentElement();
20077         }
20078         
20079         
20080         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20081             a.push(p);
20082             p = p.parentNode;
20083         }
20084         a.push(this.doc.body);
20085         return a;
20086     },
20087     lastSel : false,
20088     lastSelNode : false,
20089     
20090     
20091     getSelection : function() 
20092     {
20093         this.assignDocWin();
20094         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20095     },
20096     
20097     getSelectedNode: function() 
20098     {
20099         // this may only work on Gecko!!!
20100         
20101         // should we cache this!!!!
20102         
20103         
20104         
20105          
20106         var range = this.createRange(this.getSelection()).cloneRange();
20107         
20108         if (Roo.isIE) {
20109             var parent = range.parentElement();
20110             while (true) {
20111                 var testRange = range.duplicate();
20112                 testRange.moveToElementText(parent);
20113                 if (testRange.inRange(range)) {
20114                     break;
20115                 }
20116                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20117                     break;
20118                 }
20119                 parent = parent.parentElement;
20120             }
20121             return parent;
20122         }
20123         
20124         // is ancestor a text element.
20125         var ac =  range.commonAncestorContainer;
20126         if (ac.nodeType == 3) {
20127             ac = ac.parentNode;
20128         }
20129         
20130         var ar = ac.childNodes;
20131          
20132         var nodes = [];
20133         var other_nodes = [];
20134         var has_other_nodes = false;
20135         for (var i=0;i<ar.length;i++) {
20136             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20137                 continue;
20138             }
20139             // fullly contained node.
20140             
20141             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20142                 nodes.push(ar[i]);
20143                 continue;
20144             }
20145             
20146             // probably selected..
20147             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20148                 other_nodes.push(ar[i]);
20149                 continue;
20150             }
20151             // outer..
20152             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20153                 continue;
20154             }
20155             
20156             
20157             has_other_nodes = true;
20158         }
20159         if (!nodes.length && other_nodes.length) {
20160             nodes= other_nodes;
20161         }
20162         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20163             return false;
20164         }
20165         
20166         return nodes[0];
20167     },
20168     createRange: function(sel)
20169     {
20170         // this has strange effects when using with 
20171         // top toolbar - not sure if it's a great idea.
20172         //this.editor.contentWindow.focus();
20173         if (typeof sel != "undefined") {
20174             try {
20175                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20176             } catch(e) {
20177                 return this.doc.createRange();
20178             }
20179         } else {
20180             return this.doc.createRange();
20181         }
20182     },
20183     getParentElement: function()
20184     {
20185         
20186         this.assignDocWin();
20187         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20188         
20189         var range = this.createRange(sel);
20190          
20191         try {
20192             var p = range.commonAncestorContainer;
20193             while (p.nodeType == 3) { // text node
20194                 p = p.parentNode;
20195             }
20196             return p;
20197         } catch (e) {
20198             return null;
20199         }
20200     
20201     },
20202     /***
20203      *
20204      * Range intersection.. the hard stuff...
20205      *  '-1' = before
20206      *  '0' = hits..
20207      *  '1' = after.
20208      *         [ -- selected range --- ]
20209      *   [fail]                        [fail]
20210      *
20211      *    basically..
20212      *      if end is before start or  hits it. fail.
20213      *      if start is after end or hits it fail.
20214      *
20215      *   if either hits (but other is outside. - then it's not 
20216      *   
20217      *    
20218      **/
20219     
20220     
20221     // @see http://www.thismuchiknow.co.uk/?p=64.
20222     rangeIntersectsNode : function(range, node)
20223     {
20224         var nodeRange = node.ownerDocument.createRange();
20225         try {
20226             nodeRange.selectNode(node);
20227         } catch (e) {
20228             nodeRange.selectNodeContents(node);
20229         }
20230     
20231         var rangeStartRange = range.cloneRange();
20232         rangeStartRange.collapse(true);
20233     
20234         var rangeEndRange = range.cloneRange();
20235         rangeEndRange.collapse(false);
20236     
20237         var nodeStartRange = nodeRange.cloneRange();
20238         nodeStartRange.collapse(true);
20239     
20240         var nodeEndRange = nodeRange.cloneRange();
20241         nodeEndRange.collapse(false);
20242     
20243         return rangeStartRange.compareBoundaryPoints(
20244                  Range.START_TO_START, nodeEndRange) == -1 &&
20245                rangeEndRange.compareBoundaryPoints(
20246                  Range.START_TO_START, nodeStartRange) == 1;
20247         
20248          
20249     },
20250     rangeCompareNode : function(range, node)
20251     {
20252         var nodeRange = node.ownerDocument.createRange();
20253         try {
20254             nodeRange.selectNode(node);
20255         } catch (e) {
20256             nodeRange.selectNodeContents(node);
20257         }
20258         
20259         
20260         range.collapse(true);
20261     
20262         nodeRange.collapse(true);
20263      
20264         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20265         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20266          
20267         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20268         
20269         var nodeIsBefore   =  ss == 1;
20270         var nodeIsAfter    = ee == -1;
20271         
20272         if (nodeIsBefore && nodeIsAfter) {
20273             return 0; // outer
20274         }
20275         if (!nodeIsBefore && nodeIsAfter) {
20276             return 1; //right trailed.
20277         }
20278         
20279         if (nodeIsBefore && !nodeIsAfter) {
20280             return 2;  // left trailed.
20281         }
20282         // fully contined.
20283         return 3;
20284     },
20285
20286     // private? - in a new class?
20287     cleanUpPaste :  function()
20288     {
20289         // cleans up the whole document..
20290         Roo.log('cleanuppaste');
20291         
20292         this.cleanUpChildren(this.doc.body);
20293         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20294         if (clean != this.doc.body.innerHTML) {
20295             this.doc.body.innerHTML = clean;
20296         }
20297         
20298     },
20299     
20300     cleanWordChars : function(input) {// change the chars to hex code
20301         var he = Roo.HtmlEditorCore;
20302         
20303         var output = input;
20304         Roo.each(he.swapCodes, function(sw) { 
20305             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20306             
20307             output = output.replace(swapper, sw[1]);
20308         });
20309         
20310         return output;
20311     },
20312     
20313     
20314     cleanUpChildren : function (n)
20315     {
20316         if (!n.childNodes.length) {
20317             return;
20318         }
20319         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20320            this.cleanUpChild(n.childNodes[i]);
20321         }
20322     },
20323     
20324     
20325         
20326     
20327     cleanUpChild : function (node)
20328     {
20329         var ed = this;
20330         //console.log(node);
20331         if (node.nodeName == "#text") {
20332             // clean up silly Windows -- stuff?
20333             return; 
20334         }
20335         if (node.nodeName == "#comment") {
20336             node.parentNode.removeChild(node);
20337             // clean up silly Windows -- stuff?
20338             return; 
20339         }
20340         var lcname = node.tagName.toLowerCase();
20341         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20342         // whitelist of tags..
20343         
20344         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20345             // remove node.
20346             node.parentNode.removeChild(node);
20347             return;
20348             
20349         }
20350         
20351         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20352         
20353         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20354         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20355         
20356         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20357         //    remove_keep_children = true;
20358         //}
20359         
20360         if (remove_keep_children) {
20361             this.cleanUpChildren(node);
20362             // inserts everything just before this node...
20363             while (node.childNodes.length) {
20364                 var cn = node.childNodes[0];
20365                 node.removeChild(cn);
20366                 node.parentNode.insertBefore(cn, node);
20367             }
20368             node.parentNode.removeChild(node);
20369             return;
20370         }
20371         
20372         if (!node.attributes || !node.attributes.length) {
20373             this.cleanUpChildren(node);
20374             return;
20375         }
20376         
20377         function cleanAttr(n,v)
20378         {
20379             
20380             if (v.match(/^\./) || v.match(/^\//)) {
20381                 return;
20382             }
20383             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20384                 return;
20385             }
20386             if (v.match(/^#/)) {
20387                 return;
20388             }
20389 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20390             node.removeAttribute(n);
20391             
20392         }
20393         
20394         var cwhite = this.cwhite;
20395         var cblack = this.cblack;
20396             
20397         function cleanStyle(n,v)
20398         {
20399             if (v.match(/expression/)) { //XSS?? should we even bother..
20400                 node.removeAttribute(n);
20401                 return;
20402             }
20403             
20404             var parts = v.split(/;/);
20405             var clean = [];
20406             
20407             Roo.each(parts, function(p) {
20408                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20409                 if (!p.length) {
20410                     return true;
20411                 }
20412                 var l = p.split(':').shift().replace(/\s+/g,'');
20413                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20414                 
20415                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20416 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20417                     //node.removeAttribute(n);
20418                     return true;
20419                 }
20420                 //Roo.log()
20421                 // only allow 'c whitelisted system attributes'
20422                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20423 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20424                     //node.removeAttribute(n);
20425                     return true;
20426                 }
20427                 
20428                 
20429                  
20430                 
20431                 clean.push(p);
20432                 return true;
20433             });
20434             if (clean.length) { 
20435                 node.setAttribute(n, clean.join(';'));
20436             } else {
20437                 node.removeAttribute(n);
20438             }
20439             
20440         }
20441         
20442         
20443         for (var i = node.attributes.length-1; i > -1 ; i--) {
20444             var a = node.attributes[i];
20445             //console.log(a);
20446             
20447             if (a.name.toLowerCase().substr(0,2)=='on')  {
20448                 node.removeAttribute(a.name);
20449                 continue;
20450             }
20451             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20452                 node.removeAttribute(a.name);
20453                 continue;
20454             }
20455             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20456                 cleanAttr(a.name,a.value); // fixme..
20457                 continue;
20458             }
20459             if (a.name == 'style') {
20460                 cleanStyle(a.name,a.value);
20461                 continue;
20462             }
20463             /// clean up MS crap..
20464             // tecnically this should be a list of valid class'es..
20465             
20466             
20467             if (a.name == 'class') {
20468                 if (a.value.match(/^Mso/)) {
20469                     node.className = '';
20470                 }
20471                 
20472                 if (a.value.match(/body/)) {
20473                     node.className = '';
20474                 }
20475                 continue;
20476             }
20477             
20478             // style cleanup!?
20479             // class cleanup?
20480             
20481         }
20482         
20483         
20484         this.cleanUpChildren(node);
20485         
20486         
20487     },
20488     
20489     /**
20490      * Clean up MS wordisms...
20491      */
20492     cleanWord : function(node)
20493     {
20494         
20495         
20496         if (!node) {
20497             this.cleanWord(this.doc.body);
20498             return;
20499         }
20500         if (node.nodeName == "#text") {
20501             // clean up silly Windows -- stuff?
20502             return; 
20503         }
20504         if (node.nodeName == "#comment") {
20505             node.parentNode.removeChild(node);
20506             // clean up silly Windows -- stuff?
20507             return; 
20508         }
20509         
20510         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20511             node.parentNode.removeChild(node);
20512             return;
20513         }
20514         
20515         // remove - but keep children..
20516         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20517             while (node.childNodes.length) {
20518                 var cn = node.childNodes[0];
20519                 node.removeChild(cn);
20520                 node.parentNode.insertBefore(cn, node);
20521             }
20522             node.parentNode.removeChild(node);
20523             this.iterateChildren(node, this.cleanWord);
20524             return;
20525         }
20526         // clean styles
20527         if (node.className.length) {
20528             
20529             var cn = node.className.split(/\W+/);
20530             var cna = [];
20531             Roo.each(cn, function(cls) {
20532                 if (cls.match(/Mso[a-zA-Z]+/)) {
20533                     return;
20534                 }
20535                 cna.push(cls);
20536             });
20537             node.className = cna.length ? cna.join(' ') : '';
20538             if (!cna.length) {
20539                 node.removeAttribute("class");
20540             }
20541         }
20542         
20543         if (node.hasAttribute("lang")) {
20544             node.removeAttribute("lang");
20545         }
20546         
20547         if (node.hasAttribute("style")) {
20548             
20549             var styles = node.getAttribute("style").split(";");
20550             var nstyle = [];
20551             Roo.each(styles, function(s) {
20552                 if (!s.match(/:/)) {
20553                     return;
20554                 }
20555                 var kv = s.split(":");
20556                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20557                     return;
20558                 }
20559                 // what ever is left... we allow.
20560                 nstyle.push(s);
20561             });
20562             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20563             if (!nstyle.length) {
20564                 node.removeAttribute('style');
20565             }
20566         }
20567         this.iterateChildren(node, this.cleanWord);
20568         
20569         
20570         
20571     },
20572     /**
20573      * iterateChildren of a Node, calling fn each time, using this as the scole..
20574      * @param {DomNode} node node to iterate children of.
20575      * @param {Function} fn method of this class to call on each item.
20576      */
20577     iterateChildren : function(node, fn)
20578     {
20579         if (!node.childNodes.length) {
20580                 return;
20581         }
20582         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20583            fn.call(this, node.childNodes[i])
20584         }
20585     },
20586     
20587     
20588     /**
20589      * cleanTableWidths.
20590      *
20591      * Quite often pasting from word etc.. results in tables with column and widths.
20592      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20593      *
20594      */
20595     cleanTableWidths : function(node)
20596     {
20597          
20598          
20599         if (!node) {
20600             this.cleanTableWidths(this.doc.body);
20601             return;
20602         }
20603         
20604         // ignore list...
20605         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20606             return; 
20607         }
20608         Roo.log(node.tagName);
20609         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20610             this.iterateChildren(node, this.cleanTableWidths);
20611             return;
20612         }
20613         if (node.hasAttribute('width')) {
20614             node.removeAttribute('width');
20615         }
20616         
20617          
20618         if (node.hasAttribute("style")) {
20619             // pretty basic...
20620             
20621             var styles = node.getAttribute("style").split(";");
20622             var nstyle = [];
20623             Roo.each(styles, function(s) {
20624                 if (!s.match(/:/)) {
20625                     return;
20626                 }
20627                 var kv = s.split(":");
20628                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20629                     return;
20630                 }
20631                 // what ever is left... we allow.
20632                 nstyle.push(s);
20633             });
20634             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20635             if (!nstyle.length) {
20636                 node.removeAttribute('style');
20637             }
20638         }
20639         
20640         this.iterateChildren(node, this.cleanTableWidths);
20641         
20642         
20643     },
20644     
20645     
20646     
20647     
20648     domToHTML : function(currentElement, depth, nopadtext) {
20649         
20650         depth = depth || 0;
20651         nopadtext = nopadtext || false;
20652     
20653         if (!currentElement) {
20654             return this.domToHTML(this.doc.body);
20655         }
20656         
20657         //Roo.log(currentElement);
20658         var j;
20659         var allText = false;
20660         var nodeName = currentElement.nodeName;
20661         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20662         
20663         if  (nodeName == '#text') {
20664             
20665             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20666         }
20667         
20668         
20669         var ret = '';
20670         if (nodeName != 'BODY') {
20671              
20672             var i = 0;
20673             // Prints the node tagName, such as <A>, <IMG>, etc
20674             if (tagName) {
20675                 var attr = [];
20676                 for(i = 0; i < currentElement.attributes.length;i++) {
20677                     // quoting?
20678                     var aname = currentElement.attributes.item(i).name;
20679                     if (!currentElement.attributes.item(i).value.length) {
20680                         continue;
20681                     }
20682                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20683                 }
20684                 
20685                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20686             } 
20687             else {
20688                 
20689                 // eack
20690             }
20691         } else {
20692             tagName = false;
20693         }
20694         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20695             return ret;
20696         }
20697         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20698             nopadtext = true;
20699         }
20700         
20701         
20702         // Traverse the tree
20703         i = 0;
20704         var currentElementChild = currentElement.childNodes.item(i);
20705         var allText = true;
20706         var innerHTML  = '';
20707         lastnode = '';
20708         while (currentElementChild) {
20709             // Formatting code (indent the tree so it looks nice on the screen)
20710             var nopad = nopadtext;
20711             if (lastnode == 'SPAN') {
20712                 nopad  = true;
20713             }
20714             // text
20715             if  (currentElementChild.nodeName == '#text') {
20716                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20717                 toadd = nopadtext ? toadd : toadd.trim();
20718                 if (!nopad && toadd.length > 80) {
20719                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20720                 }
20721                 innerHTML  += toadd;
20722                 
20723                 i++;
20724                 currentElementChild = currentElement.childNodes.item(i);
20725                 lastNode = '';
20726                 continue;
20727             }
20728             allText = false;
20729             
20730             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20731                 
20732             // Recursively traverse the tree structure of the child node
20733             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20734             lastnode = currentElementChild.nodeName;
20735             i++;
20736             currentElementChild=currentElement.childNodes.item(i);
20737         }
20738         
20739         ret += innerHTML;
20740         
20741         if (!allText) {
20742                 // The remaining code is mostly for formatting the tree
20743             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20744         }
20745         
20746         
20747         if (tagName) {
20748             ret+= "</"+tagName+">";
20749         }
20750         return ret;
20751         
20752     },
20753         
20754     applyBlacklists : function()
20755     {
20756         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20757         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20758         
20759         this.white = [];
20760         this.black = [];
20761         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20762             if (b.indexOf(tag) > -1) {
20763                 return;
20764             }
20765             this.white.push(tag);
20766             
20767         }, this);
20768         
20769         Roo.each(w, function(tag) {
20770             if (b.indexOf(tag) > -1) {
20771                 return;
20772             }
20773             if (this.white.indexOf(tag) > -1) {
20774                 return;
20775             }
20776             this.white.push(tag);
20777             
20778         }, this);
20779         
20780         
20781         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20782             if (w.indexOf(tag) > -1) {
20783                 return;
20784             }
20785             this.black.push(tag);
20786             
20787         }, this);
20788         
20789         Roo.each(b, function(tag) {
20790             if (w.indexOf(tag) > -1) {
20791                 return;
20792             }
20793             if (this.black.indexOf(tag) > -1) {
20794                 return;
20795             }
20796             this.black.push(tag);
20797             
20798         }, this);
20799         
20800         
20801         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20802         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20803         
20804         this.cwhite = [];
20805         this.cblack = [];
20806         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20807             if (b.indexOf(tag) > -1) {
20808                 return;
20809             }
20810             this.cwhite.push(tag);
20811             
20812         }, this);
20813         
20814         Roo.each(w, function(tag) {
20815             if (b.indexOf(tag) > -1) {
20816                 return;
20817             }
20818             if (this.cwhite.indexOf(tag) > -1) {
20819                 return;
20820             }
20821             this.cwhite.push(tag);
20822             
20823         }, this);
20824         
20825         
20826         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20827             if (w.indexOf(tag) > -1) {
20828                 return;
20829             }
20830             this.cblack.push(tag);
20831             
20832         }, this);
20833         
20834         Roo.each(b, function(tag) {
20835             if (w.indexOf(tag) > -1) {
20836                 return;
20837             }
20838             if (this.cblack.indexOf(tag) > -1) {
20839                 return;
20840             }
20841             this.cblack.push(tag);
20842             
20843         }, this);
20844     },
20845     
20846     setStylesheets : function(stylesheets)
20847     {
20848         if(typeof(stylesheets) == 'string'){
20849             Roo.get(this.iframe.contentDocument.head).createChild({
20850                 tag : 'link',
20851                 rel : 'stylesheet',
20852                 type : 'text/css',
20853                 href : stylesheets
20854             });
20855             
20856             return;
20857         }
20858         var _this = this;
20859      
20860         Roo.each(stylesheets, function(s) {
20861             if(!s.length){
20862                 return;
20863             }
20864             
20865             Roo.get(_this.iframe.contentDocument.head).createChild({
20866                 tag : 'link',
20867                 rel : 'stylesheet',
20868                 type : 'text/css',
20869                 href : s
20870             });
20871         });
20872
20873         
20874     },
20875     
20876     removeStylesheets : function()
20877     {
20878         var _this = this;
20879         
20880         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20881             s.remove();
20882         });
20883     }
20884     
20885     // hide stuff that is not compatible
20886     /**
20887      * @event blur
20888      * @hide
20889      */
20890     /**
20891      * @event change
20892      * @hide
20893      */
20894     /**
20895      * @event focus
20896      * @hide
20897      */
20898     /**
20899      * @event specialkey
20900      * @hide
20901      */
20902     /**
20903      * @cfg {String} fieldClass @hide
20904      */
20905     /**
20906      * @cfg {String} focusClass @hide
20907      */
20908     /**
20909      * @cfg {String} autoCreate @hide
20910      */
20911     /**
20912      * @cfg {String} inputType @hide
20913      */
20914     /**
20915      * @cfg {String} invalidClass @hide
20916      */
20917     /**
20918      * @cfg {String} invalidText @hide
20919      */
20920     /**
20921      * @cfg {String} msgFx @hide
20922      */
20923     /**
20924      * @cfg {String} validateOnBlur @hide
20925      */
20926 });
20927
20928 Roo.HtmlEditorCore.white = [
20929         'area', 'br', 'img', 'input', 'hr', 'wbr',
20930         
20931        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20932        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20933        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20934        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20935        'table',   'ul',         'xmp', 
20936        
20937        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20938       'thead',   'tr', 
20939      
20940       'dir', 'menu', 'ol', 'ul', 'dl',
20941        
20942       'embed',  'object'
20943 ];
20944
20945
20946 Roo.HtmlEditorCore.black = [
20947     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20948         'applet', // 
20949         'base',   'basefont', 'bgsound', 'blink',  'body', 
20950         'frame',  'frameset', 'head',    'html',   'ilayer', 
20951         'iframe', 'layer',  'link',     'meta',    'object',   
20952         'script', 'style' ,'title',  'xml' // clean later..
20953 ];
20954 Roo.HtmlEditorCore.clean = [
20955     'script', 'style', 'title', 'xml'
20956 ];
20957 Roo.HtmlEditorCore.remove = [
20958     'font'
20959 ];
20960 // attributes..
20961
20962 Roo.HtmlEditorCore.ablack = [
20963     'on'
20964 ];
20965     
20966 Roo.HtmlEditorCore.aclean = [ 
20967     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20968 ];
20969
20970 // protocols..
20971 Roo.HtmlEditorCore.pwhite= [
20972         'http',  'https',  'mailto'
20973 ];
20974
20975 // white listed style attributes.
20976 Roo.HtmlEditorCore.cwhite= [
20977       //  'text-align', /// default is to allow most things..
20978       
20979          
20980 //        'font-size'//??
20981 ];
20982
20983 // black listed style attributes.
20984 Roo.HtmlEditorCore.cblack= [
20985       //  'font-size' -- this can be set by the project 
20986 ];
20987
20988
20989 Roo.HtmlEditorCore.swapCodes   =[ 
20990     [    8211, "--" ], 
20991     [    8212, "--" ], 
20992     [    8216,  "'" ],  
20993     [    8217, "'" ],  
20994     [    8220, '"' ],  
20995     [    8221, '"' ],  
20996     [    8226, "*" ],  
20997     [    8230, "..." ]
20998 ]; 
20999
21000     /*
21001  * - LGPL
21002  *
21003  * HtmlEditor
21004  * 
21005  */
21006
21007 /**
21008  * @class Roo.bootstrap.HtmlEditor
21009  * @extends Roo.bootstrap.TextArea
21010  * Bootstrap HtmlEditor class
21011
21012  * @constructor
21013  * Create a new HtmlEditor
21014  * @param {Object} config The config object
21015  */
21016
21017 Roo.bootstrap.HtmlEditor = function(config){
21018     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21019     if (!this.toolbars) {
21020         this.toolbars = [];
21021     }
21022     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21023     this.addEvents({
21024             /**
21025              * @event initialize
21026              * Fires when the editor is fully initialized (including the iframe)
21027              * @param {HtmlEditor} this
21028              */
21029             initialize: true,
21030             /**
21031              * @event activate
21032              * Fires when the editor is first receives the focus. Any insertion must wait
21033              * until after this event.
21034              * @param {HtmlEditor} this
21035              */
21036             activate: true,
21037              /**
21038              * @event beforesync
21039              * Fires before the textarea is updated with content from the editor iframe. Return false
21040              * to cancel the sync.
21041              * @param {HtmlEditor} this
21042              * @param {String} html
21043              */
21044             beforesync: true,
21045              /**
21046              * @event beforepush
21047              * Fires before the iframe editor is updated with content from the textarea. Return false
21048              * to cancel the push.
21049              * @param {HtmlEditor} this
21050              * @param {String} html
21051              */
21052             beforepush: true,
21053              /**
21054              * @event sync
21055              * Fires when the textarea is updated with content from the editor iframe.
21056              * @param {HtmlEditor} this
21057              * @param {String} html
21058              */
21059             sync: true,
21060              /**
21061              * @event push
21062              * Fires when the iframe editor is updated with content from the textarea.
21063              * @param {HtmlEditor} this
21064              * @param {String} html
21065              */
21066             push: true,
21067              /**
21068              * @event editmodechange
21069              * Fires when the editor switches edit modes
21070              * @param {HtmlEditor} this
21071              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21072              */
21073             editmodechange: true,
21074             /**
21075              * @event editorevent
21076              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21077              * @param {HtmlEditor} this
21078              */
21079             editorevent: true,
21080             /**
21081              * @event firstfocus
21082              * Fires when on first focus - needed by toolbars..
21083              * @param {HtmlEditor} this
21084              */
21085             firstfocus: true,
21086             /**
21087              * @event autosave
21088              * Auto save the htmlEditor value as a file into Events
21089              * @param {HtmlEditor} this
21090              */
21091             autosave: true,
21092             /**
21093              * @event savedpreview
21094              * preview the saved version of htmlEditor
21095              * @param {HtmlEditor} this
21096              */
21097             savedpreview: true
21098         });
21099 };
21100
21101
21102 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21103     
21104     
21105       /**
21106      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21107      */
21108     toolbars : false,
21109    
21110      /**
21111      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21112      *                        Roo.resizable.
21113      */
21114     resizable : false,
21115      /**
21116      * @cfg {Number} height (in pixels)
21117      */   
21118     height: 300,
21119    /**
21120      * @cfg {Number} width (in pixels)
21121      */   
21122     width: false,
21123     
21124     /**
21125      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21126      * 
21127      */
21128     stylesheets: false,
21129     
21130     // id of frame..
21131     frameId: false,
21132     
21133     // private properties
21134     validationEvent : false,
21135     deferHeight: true,
21136     initialized : false,
21137     activated : false,
21138     
21139     onFocus : Roo.emptyFn,
21140     iframePad:3,
21141     hideMode:'offsets',
21142     
21143     
21144     tbContainer : false,
21145     
21146     toolbarContainer :function() {
21147         return this.wrap.select('.x-html-editor-tb',true).first();
21148     },
21149
21150     /**
21151      * Protected method that will not generally be called directly. It
21152      * is called when the editor creates its toolbar. Override this method if you need to
21153      * add custom toolbar buttons.
21154      * @param {HtmlEditor} editor
21155      */
21156     createToolbar : function(){
21157         
21158         Roo.log("create toolbars");
21159         
21160         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21161         this.toolbars[0].render(this.toolbarContainer());
21162         
21163         return;
21164         
21165 //        if (!editor.toolbars || !editor.toolbars.length) {
21166 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21167 //        }
21168 //        
21169 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21170 //            editor.toolbars[i] = Roo.factory(
21171 //                    typeof(editor.toolbars[i]) == 'string' ?
21172 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21173 //                Roo.bootstrap.HtmlEditor);
21174 //            editor.toolbars[i].init(editor);
21175 //        }
21176     },
21177
21178      
21179     // private
21180     onRender : function(ct, position)
21181     {
21182        // Roo.log("Call onRender: " + this.xtype);
21183         var _t = this;
21184         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21185       
21186         this.wrap = this.inputEl().wrap({
21187             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21188         });
21189         
21190         this.editorcore.onRender(ct, position);
21191          
21192         if (this.resizable) {
21193             this.resizeEl = new Roo.Resizable(this.wrap, {
21194                 pinned : true,
21195                 wrap: true,
21196                 dynamic : true,
21197                 minHeight : this.height,
21198                 height: this.height,
21199                 handles : this.resizable,
21200                 width: this.width,
21201                 listeners : {
21202                     resize : function(r, w, h) {
21203                         _t.onResize(w,h); // -something
21204                     }
21205                 }
21206             });
21207             
21208         }
21209         this.createToolbar(this);
21210        
21211         
21212         if(!this.width && this.resizable){
21213             this.setSize(this.wrap.getSize());
21214         }
21215         if (this.resizeEl) {
21216             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21217             // should trigger onReize..
21218         }
21219         
21220     },
21221
21222     // private
21223     onResize : function(w, h)
21224     {
21225         Roo.log('resize: ' +w + ',' + h );
21226         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21227         var ew = false;
21228         var eh = false;
21229         
21230         if(this.inputEl() ){
21231             if(typeof w == 'number'){
21232                 var aw = w - this.wrap.getFrameWidth('lr');
21233                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21234                 ew = aw;
21235             }
21236             if(typeof h == 'number'){
21237                  var tbh = -11;  // fixme it needs to tool bar size!
21238                 for (var i =0; i < this.toolbars.length;i++) {
21239                     // fixme - ask toolbars for heights?
21240                     tbh += this.toolbars[i].el.getHeight();
21241                     //if (this.toolbars[i].footer) {
21242                     //    tbh += this.toolbars[i].footer.el.getHeight();
21243                     //}
21244                 }
21245               
21246                 
21247                 
21248                 
21249                 
21250                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21251                 ah -= 5; // knock a few pixes off for look..
21252                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21253                 var eh = ah;
21254             }
21255         }
21256         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21257         this.editorcore.onResize(ew,eh);
21258         
21259     },
21260
21261     /**
21262      * Toggles the editor between standard and source edit mode.
21263      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21264      */
21265     toggleSourceEdit : function(sourceEditMode)
21266     {
21267         this.editorcore.toggleSourceEdit(sourceEditMode);
21268         
21269         if(this.editorcore.sourceEditMode){
21270             Roo.log('editor - showing textarea');
21271             
21272 //            Roo.log('in');
21273 //            Roo.log(this.syncValue());
21274             this.syncValue();
21275             this.inputEl().removeClass(['hide', 'x-hidden']);
21276             this.inputEl().dom.removeAttribute('tabIndex');
21277             this.inputEl().focus();
21278         }else{
21279             Roo.log('editor - hiding textarea');
21280 //            Roo.log('out')
21281 //            Roo.log(this.pushValue()); 
21282             this.pushValue();
21283             
21284             this.inputEl().addClass(['hide', 'x-hidden']);
21285             this.inputEl().dom.setAttribute('tabIndex', -1);
21286             //this.deferFocus();
21287         }
21288          
21289         if(this.resizable){
21290             this.setSize(this.wrap.getSize());
21291         }
21292         
21293         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21294     },
21295  
21296     // private (for BoxComponent)
21297     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21298
21299     // private (for BoxComponent)
21300     getResizeEl : function(){
21301         return this.wrap;
21302     },
21303
21304     // private (for BoxComponent)
21305     getPositionEl : function(){
21306         return this.wrap;
21307     },
21308
21309     // private
21310     initEvents : function(){
21311         this.originalValue = this.getValue();
21312     },
21313
21314 //    /**
21315 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21316 //     * @method
21317 //     */
21318 //    markInvalid : Roo.emptyFn,
21319 //    /**
21320 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21321 //     * @method
21322 //     */
21323 //    clearInvalid : Roo.emptyFn,
21324
21325     setValue : function(v){
21326         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21327         this.editorcore.pushValue();
21328     },
21329
21330      
21331     // private
21332     deferFocus : function(){
21333         this.focus.defer(10, this);
21334     },
21335
21336     // doc'ed in Field
21337     focus : function(){
21338         this.editorcore.focus();
21339         
21340     },
21341       
21342
21343     // private
21344     onDestroy : function(){
21345         
21346         
21347         
21348         if(this.rendered){
21349             
21350             for (var i =0; i < this.toolbars.length;i++) {
21351                 // fixme - ask toolbars for heights?
21352                 this.toolbars[i].onDestroy();
21353             }
21354             
21355             this.wrap.dom.innerHTML = '';
21356             this.wrap.remove();
21357         }
21358     },
21359
21360     // private
21361     onFirstFocus : function(){
21362         //Roo.log("onFirstFocus");
21363         this.editorcore.onFirstFocus();
21364          for (var i =0; i < this.toolbars.length;i++) {
21365             this.toolbars[i].onFirstFocus();
21366         }
21367         
21368     },
21369     
21370     // private
21371     syncValue : function()
21372     {   
21373         this.editorcore.syncValue();
21374     },
21375     
21376     pushValue : function()
21377     {   
21378         this.editorcore.pushValue();
21379     }
21380      
21381     
21382     // hide stuff that is not compatible
21383     /**
21384      * @event blur
21385      * @hide
21386      */
21387     /**
21388      * @event change
21389      * @hide
21390      */
21391     /**
21392      * @event focus
21393      * @hide
21394      */
21395     /**
21396      * @event specialkey
21397      * @hide
21398      */
21399     /**
21400      * @cfg {String} fieldClass @hide
21401      */
21402     /**
21403      * @cfg {String} focusClass @hide
21404      */
21405     /**
21406      * @cfg {String} autoCreate @hide
21407      */
21408     /**
21409      * @cfg {String} inputType @hide
21410      */
21411     /**
21412      * @cfg {String} invalidClass @hide
21413      */
21414     /**
21415      * @cfg {String} invalidText @hide
21416      */
21417     /**
21418      * @cfg {String} msgFx @hide
21419      */
21420     /**
21421      * @cfg {String} validateOnBlur @hide
21422      */
21423 });
21424  
21425     
21426    
21427    
21428    
21429       
21430 Roo.namespace('Roo.bootstrap.htmleditor');
21431 /**
21432  * @class Roo.bootstrap.HtmlEditorToolbar1
21433  * Basic Toolbar
21434  * 
21435  * Usage:
21436  *
21437  new Roo.bootstrap.HtmlEditor({
21438     ....
21439     toolbars : [
21440         new Roo.bootstrap.HtmlEditorToolbar1({
21441             disable : { fonts: 1 , format: 1, ..., ... , ...],
21442             btns : [ .... ]
21443         })
21444     }
21445      
21446  * 
21447  * @cfg {Object} disable List of elements to disable..
21448  * @cfg {Array} btns List of additional buttons.
21449  * 
21450  * 
21451  * NEEDS Extra CSS? 
21452  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21453  */
21454  
21455 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21456 {
21457     
21458     Roo.apply(this, config);
21459     
21460     // default disabled, based on 'good practice'..
21461     this.disable = this.disable || {};
21462     Roo.applyIf(this.disable, {
21463         fontSize : true,
21464         colors : true,
21465         specialElements : true
21466     });
21467     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21468     
21469     this.editor = config.editor;
21470     this.editorcore = config.editor.editorcore;
21471     
21472     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21473     
21474     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21475     // dont call parent... till later.
21476 }
21477 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21478      
21479     bar : true,
21480     
21481     editor : false,
21482     editorcore : false,
21483     
21484     
21485     formats : [
21486         "p" ,  
21487         "h1","h2","h3","h4","h5","h6", 
21488         "pre", "code", 
21489         "abbr", "acronym", "address", "cite", "samp", "var",
21490         'div','span'
21491     ],
21492     
21493     onRender : function(ct, position)
21494     {
21495        // Roo.log("Call onRender: " + this.xtype);
21496         
21497        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21498        Roo.log(this.el);
21499        this.el.dom.style.marginBottom = '0';
21500        var _this = this;
21501        var editorcore = this.editorcore;
21502        var editor= this.editor;
21503        
21504        var children = [];
21505        var btn = function(id,cmd , toggle, handler){
21506        
21507             var  event = toggle ? 'toggle' : 'click';
21508        
21509             var a = {
21510                 size : 'sm',
21511                 xtype: 'Button',
21512                 xns: Roo.bootstrap,
21513                 glyphicon : id,
21514                 cmd : id || cmd,
21515                 enableToggle:toggle !== false,
21516                 //html : 'submit'
21517                 pressed : toggle ? false : null,
21518                 listeners : {}
21519             };
21520             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21521                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21522             };
21523             children.push(a);
21524             return a;
21525        }
21526         
21527         var style = {
21528                 xtype: 'Button',
21529                 size : 'sm',
21530                 xns: Roo.bootstrap,
21531                 glyphicon : 'font',
21532                 //html : 'submit'
21533                 menu : {
21534                     xtype: 'Menu',
21535                     xns: Roo.bootstrap,
21536                     items:  []
21537                 }
21538         };
21539         Roo.each(this.formats, function(f) {
21540             style.menu.items.push({
21541                 xtype :'MenuItem',
21542                 xns: Roo.bootstrap,
21543                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21544                 tagname : f,
21545                 listeners : {
21546                     click : function()
21547                     {
21548                         editorcore.insertTag(this.tagname);
21549                         editor.focus();
21550                     }
21551                 }
21552                 
21553             });
21554         });
21555          children.push(style);   
21556             
21557             
21558         btn('bold',false,true);
21559         btn('italic',false,true);
21560         btn('align-left', 'justifyleft',true);
21561         btn('align-center', 'justifycenter',true);
21562         btn('align-right' , 'justifyright',true);
21563         btn('link', false, false, function(btn) {
21564             //Roo.log("create link?");
21565             var url = prompt(this.createLinkText, this.defaultLinkValue);
21566             if(url && url != 'http:/'+'/'){
21567                 this.editorcore.relayCmd('createlink', url);
21568             }
21569         }),
21570         btn('list','insertunorderedlist',true);
21571         btn('pencil', false,true, function(btn){
21572                 Roo.log(this);
21573                 
21574                 this.toggleSourceEdit(btn.pressed);
21575         });
21576         /*
21577         var cog = {
21578                 xtype: 'Button',
21579                 size : 'sm',
21580                 xns: Roo.bootstrap,
21581                 glyphicon : 'cog',
21582                 //html : 'submit'
21583                 menu : {
21584                     xtype: 'Menu',
21585                     xns: Roo.bootstrap,
21586                     items:  []
21587                 }
21588         };
21589         
21590         cog.menu.items.push({
21591             xtype :'MenuItem',
21592             xns: Roo.bootstrap,
21593             html : Clean styles,
21594             tagname : f,
21595             listeners : {
21596                 click : function()
21597                 {
21598                     editorcore.insertTag(this.tagname);
21599                     editor.focus();
21600                 }
21601             }
21602             
21603         });
21604        */
21605         
21606          
21607        this.xtype = 'NavSimplebar';
21608         
21609         for(var i=0;i< children.length;i++) {
21610             
21611             this.buttons.add(this.addxtypeChild(children[i]));
21612             
21613         }
21614         
21615         editor.on('editorevent', this.updateToolbar, this);
21616     },
21617     onBtnClick : function(id)
21618     {
21619        this.editorcore.relayCmd(id);
21620        this.editorcore.focus();
21621     },
21622     
21623     /**
21624      * Protected method that will not generally be called directly. It triggers
21625      * a toolbar update by reading the markup state of the current selection in the editor.
21626      */
21627     updateToolbar: function(){
21628
21629         if(!this.editorcore.activated){
21630             this.editor.onFirstFocus(); // is this neeed?
21631             return;
21632         }
21633
21634         var btns = this.buttons; 
21635         var doc = this.editorcore.doc;
21636         btns.get('bold').setActive(doc.queryCommandState('bold'));
21637         btns.get('italic').setActive(doc.queryCommandState('italic'));
21638         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21639         
21640         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21641         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21642         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21643         
21644         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21645         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21646          /*
21647         
21648         var ans = this.editorcore.getAllAncestors();
21649         if (this.formatCombo) {
21650             
21651             
21652             var store = this.formatCombo.store;
21653             this.formatCombo.setValue("");
21654             for (var i =0; i < ans.length;i++) {
21655                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21656                     // select it..
21657                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21658                     break;
21659                 }
21660             }
21661         }
21662         
21663         
21664         
21665         // hides menus... - so this cant be on a menu...
21666         Roo.bootstrap.MenuMgr.hideAll();
21667         */
21668         Roo.bootstrap.MenuMgr.hideAll();
21669         //this.editorsyncValue();
21670     },
21671     onFirstFocus: function() {
21672         this.buttons.each(function(item){
21673            item.enable();
21674         });
21675     },
21676     toggleSourceEdit : function(sourceEditMode){
21677         
21678           
21679         if(sourceEditMode){
21680             Roo.log("disabling buttons");
21681            this.buttons.each( function(item){
21682                 if(item.cmd != 'pencil'){
21683                     item.disable();
21684                 }
21685             });
21686           
21687         }else{
21688             Roo.log("enabling buttons");
21689             if(this.editorcore.initialized){
21690                 this.buttons.each( function(item){
21691                     item.enable();
21692                 });
21693             }
21694             
21695         }
21696         Roo.log("calling toggole on editor");
21697         // tell the editor that it's been pressed..
21698         this.editor.toggleSourceEdit(sourceEditMode);
21699        
21700     }
21701 });
21702
21703
21704
21705
21706
21707 /**
21708  * @class Roo.bootstrap.Table.AbstractSelectionModel
21709  * @extends Roo.util.Observable
21710  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21711  * implemented by descendant classes.  This class should not be directly instantiated.
21712  * @constructor
21713  */
21714 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21715     this.locked = false;
21716     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21717 };
21718
21719
21720 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21721     /** @ignore Called by the grid automatically. Do not call directly. */
21722     init : function(grid){
21723         this.grid = grid;
21724         this.initEvents();
21725     },
21726
21727     /**
21728      * Locks the selections.
21729      */
21730     lock : function(){
21731         this.locked = true;
21732     },
21733
21734     /**
21735      * Unlocks the selections.
21736      */
21737     unlock : function(){
21738         this.locked = false;
21739     },
21740
21741     /**
21742      * Returns true if the selections are locked.
21743      * @return {Boolean}
21744      */
21745     isLocked : function(){
21746         return this.locked;
21747     }
21748 });
21749 /**
21750  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21751  * @class Roo.bootstrap.Table.RowSelectionModel
21752  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21753  * It supports multiple selections and keyboard selection/navigation. 
21754  * @constructor
21755  * @param {Object} config
21756  */
21757
21758 Roo.bootstrap.Table.RowSelectionModel = function(config){
21759     Roo.apply(this, config);
21760     this.selections = new Roo.util.MixedCollection(false, function(o){
21761         return o.id;
21762     });
21763
21764     this.last = false;
21765     this.lastActive = false;
21766
21767     this.addEvents({
21768         /**
21769              * @event selectionchange
21770              * Fires when the selection changes
21771              * @param {SelectionModel} this
21772              */
21773             "selectionchange" : true,
21774         /**
21775              * @event afterselectionchange
21776              * Fires after the selection changes (eg. by key press or clicking)
21777              * @param {SelectionModel} this
21778              */
21779             "afterselectionchange" : true,
21780         /**
21781              * @event beforerowselect
21782              * Fires when a row is selected being selected, return false to cancel.
21783              * @param {SelectionModel} this
21784              * @param {Number} rowIndex The selected index
21785              * @param {Boolean} keepExisting False if other selections will be cleared
21786              */
21787             "beforerowselect" : true,
21788         /**
21789              * @event rowselect
21790              * Fires when a row is selected.
21791              * @param {SelectionModel} this
21792              * @param {Number} rowIndex The selected index
21793              * @param {Roo.data.Record} r The record
21794              */
21795             "rowselect" : true,
21796         /**
21797              * @event rowdeselect
21798              * Fires when a row is deselected.
21799              * @param {SelectionModel} this
21800              * @param {Number} rowIndex The selected index
21801              */
21802         "rowdeselect" : true
21803     });
21804     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21805     this.locked = false;
21806 };
21807
21808 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21809     /**
21810      * @cfg {Boolean} singleSelect
21811      * True to allow selection of only one row at a time (defaults to false)
21812      */
21813     singleSelect : false,
21814
21815     // private
21816     initEvents : function(){
21817
21818         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21819             this.grid.on("mousedown", this.handleMouseDown, this);
21820         }else{ // allow click to work like normal
21821             this.grid.on("rowclick", this.handleDragableRowClick, this);
21822         }
21823
21824         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21825             "up" : function(e){
21826                 if(!e.shiftKey){
21827                     this.selectPrevious(e.shiftKey);
21828                 }else if(this.last !== false && this.lastActive !== false){
21829                     var last = this.last;
21830                     this.selectRange(this.last,  this.lastActive-1);
21831                     this.grid.getView().focusRow(this.lastActive);
21832                     if(last !== false){
21833                         this.last = last;
21834                     }
21835                 }else{
21836                     this.selectFirstRow();
21837                 }
21838                 this.fireEvent("afterselectionchange", this);
21839             },
21840             "down" : function(e){
21841                 if(!e.shiftKey){
21842                     this.selectNext(e.shiftKey);
21843                 }else if(this.last !== false && this.lastActive !== false){
21844                     var last = this.last;
21845                     this.selectRange(this.last,  this.lastActive+1);
21846                     this.grid.getView().focusRow(this.lastActive);
21847                     if(last !== false){
21848                         this.last = last;
21849                     }
21850                 }else{
21851                     this.selectFirstRow();
21852                 }
21853                 this.fireEvent("afterselectionchange", this);
21854             },
21855             scope: this
21856         });
21857
21858         var view = this.grid.view;
21859         view.on("refresh", this.onRefresh, this);
21860         view.on("rowupdated", this.onRowUpdated, this);
21861         view.on("rowremoved", this.onRemove, this);
21862     },
21863
21864     // private
21865     onRefresh : function(){
21866         var ds = this.grid.dataSource, i, v = this.grid.view;
21867         var s = this.selections;
21868         s.each(function(r){
21869             if((i = ds.indexOfId(r.id)) != -1){
21870                 v.onRowSelect(i);
21871             }else{
21872                 s.remove(r);
21873             }
21874         });
21875     },
21876
21877     // private
21878     onRemove : function(v, index, r){
21879         this.selections.remove(r);
21880     },
21881
21882     // private
21883     onRowUpdated : function(v, index, r){
21884         if(this.isSelected(r)){
21885             v.onRowSelect(index);
21886         }
21887     },
21888
21889     /**
21890      * Select records.
21891      * @param {Array} records The records to select
21892      * @param {Boolean} keepExisting (optional) True to keep existing selections
21893      */
21894     selectRecords : function(records, keepExisting){
21895         if(!keepExisting){
21896             this.clearSelections();
21897         }
21898         var ds = this.grid.dataSource;
21899         for(var i = 0, len = records.length; i < len; i++){
21900             this.selectRow(ds.indexOf(records[i]), true);
21901         }
21902     },
21903
21904     /**
21905      * Gets the number of selected rows.
21906      * @return {Number}
21907      */
21908     getCount : function(){
21909         return this.selections.length;
21910     },
21911
21912     /**
21913      * Selects the first row in the grid.
21914      */
21915     selectFirstRow : function(){
21916         this.selectRow(0);
21917     },
21918
21919     /**
21920      * Select the last row.
21921      * @param {Boolean} keepExisting (optional) True to keep existing selections
21922      */
21923     selectLastRow : function(keepExisting){
21924         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21925     },
21926
21927     /**
21928      * Selects the row immediately following the last selected row.
21929      * @param {Boolean} keepExisting (optional) True to keep existing selections
21930      */
21931     selectNext : function(keepExisting){
21932         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21933             this.selectRow(this.last+1, keepExisting);
21934             this.grid.getView().focusRow(this.last);
21935         }
21936     },
21937
21938     /**
21939      * Selects the row that precedes the last selected row.
21940      * @param {Boolean} keepExisting (optional) True to keep existing selections
21941      */
21942     selectPrevious : function(keepExisting){
21943         if(this.last){
21944             this.selectRow(this.last-1, keepExisting);
21945             this.grid.getView().focusRow(this.last);
21946         }
21947     },
21948
21949     /**
21950      * Returns the selected records
21951      * @return {Array} Array of selected records
21952      */
21953     getSelections : function(){
21954         return [].concat(this.selections.items);
21955     },
21956
21957     /**
21958      * Returns the first selected record.
21959      * @return {Record}
21960      */
21961     getSelected : function(){
21962         return this.selections.itemAt(0);
21963     },
21964
21965
21966     /**
21967      * Clears all selections.
21968      */
21969     clearSelections : function(fast){
21970         if(this.locked) {
21971             return;
21972         }
21973         if(fast !== true){
21974             var ds = this.grid.dataSource;
21975             var s = this.selections;
21976             s.each(function(r){
21977                 this.deselectRow(ds.indexOfId(r.id));
21978             }, this);
21979             s.clear();
21980         }else{
21981             this.selections.clear();
21982         }
21983         this.last = false;
21984     },
21985
21986
21987     /**
21988      * Selects all rows.
21989      */
21990     selectAll : function(){
21991         if(this.locked) {
21992             return;
21993         }
21994         this.selections.clear();
21995         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21996             this.selectRow(i, true);
21997         }
21998     },
21999
22000     /**
22001      * Returns True if there is a selection.
22002      * @return {Boolean}
22003      */
22004     hasSelection : function(){
22005         return this.selections.length > 0;
22006     },
22007
22008     /**
22009      * Returns True if the specified row is selected.
22010      * @param {Number/Record} record The record or index of the record to check
22011      * @return {Boolean}
22012      */
22013     isSelected : function(index){
22014         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22015         return (r && this.selections.key(r.id) ? true : false);
22016     },
22017
22018     /**
22019      * Returns True if the specified record id is selected.
22020      * @param {String} id The id of record to check
22021      * @return {Boolean}
22022      */
22023     isIdSelected : function(id){
22024         return (this.selections.key(id) ? true : false);
22025     },
22026
22027     // private
22028     handleMouseDown : function(e, t){
22029         var view = this.grid.getView(), rowIndex;
22030         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22031             return;
22032         };
22033         if(e.shiftKey && this.last !== false){
22034             var last = this.last;
22035             this.selectRange(last, rowIndex, e.ctrlKey);
22036             this.last = last; // reset the last
22037             view.focusRow(rowIndex);
22038         }else{
22039             var isSelected = this.isSelected(rowIndex);
22040             if(e.button !== 0 && isSelected){
22041                 view.focusRow(rowIndex);
22042             }else if(e.ctrlKey && isSelected){
22043                 this.deselectRow(rowIndex);
22044             }else if(!isSelected){
22045                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22046                 view.focusRow(rowIndex);
22047             }
22048         }
22049         this.fireEvent("afterselectionchange", this);
22050     },
22051     // private
22052     handleDragableRowClick :  function(grid, rowIndex, e) 
22053     {
22054         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22055             this.selectRow(rowIndex, false);
22056             grid.view.focusRow(rowIndex);
22057              this.fireEvent("afterselectionchange", this);
22058         }
22059     },
22060     
22061     /**
22062      * Selects multiple rows.
22063      * @param {Array} rows Array of the indexes of the row to select
22064      * @param {Boolean} keepExisting (optional) True to keep existing selections
22065      */
22066     selectRows : function(rows, keepExisting){
22067         if(!keepExisting){
22068             this.clearSelections();
22069         }
22070         for(var i = 0, len = rows.length; i < len; i++){
22071             this.selectRow(rows[i], true);
22072         }
22073     },
22074
22075     /**
22076      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22077      * @param {Number} startRow The index of the first row in the range
22078      * @param {Number} endRow The index of the last row in the range
22079      * @param {Boolean} keepExisting (optional) True to retain existing selections
22080      */
22081     selectRange : function(startRow, endRow, keepExisting){
22082         if(this.locked) {
22083             return;
22084         }
22085         if(!keepExisting){
22086             this.clearSelections();
22087         }
22088         if(startRow <= endRow){
22089             for(var i = startRow; i <= endRow; i++){
22090                 this.selectRow(i, true);
22091             }
22092         }else{
22093             for(var i = startRow; i >= endRow; i--){
22094                 this.selectRow(i, true);
22095             }
22096         }
22097     },
22098
22099     /**
22100      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22101      * @param {Number} startRow The index of the first row in the range
22102      * @param {Number} endRow The index of the last row in the range
22103      */
22104     deselectRange : function(startRow, endRow, preventViewNotify){
22105         if(this.locked) {
22106             return;
22107         }
22108         for(var i = startRow; i <= endRow; i++){
22109             this.deselectRow(i, preventViewNotify);
22110         }
22111     },
22112
22113     /**
22114      * Selects a row.
22115      * @param {Number} row The index of the row to select
22116      * @param {Boolean} keepExisting (optional) True to keep existing selections
22117      */
22118     selectRow : function(index, keepExisting, preventViewNotify){
22119         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22120             return;
22121         }
22122         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22123             if(!keepExisting || this.singleSelect){
22124                 this.clearSelections();
22125             }
22126             var r = this.grid.dataSource.getAt(index);
22127             this.selections.add(r);
22128             this.last = this.lastActive = index;
22129             if(!preventViewNotify){
22130                 this.grid.getView().onRowSelect(index);
22131             }
22132             this.fireEvent("rowselect", this, index, r);
22133             this.fireEvent("selectionchange", this);
22134         }
22135     },
22136
22137     /**
22138      * Deselects a row.
22139      * @param {Number} row The index of the row to deselect
22140      */
22141     deselectRow : function(index, preventViewNotify){
22142         if(this.locked) {
22143             return;
22144         }
22145         if(this.last == index){
22146             this.last = false;
22147         }
22148         if(this.lastActive == index){
22149             this.lastActive = false;
22150         }
22151         var r = this.grid.dataSource.getAt(index);
22152         this.selections.remove(r);
22153         if(!preventViewNotify){
22154             this.grid.getView().onRowDeselect(index);
22155         }
22156         this.fireEvent("rowdeselect", this, index);
22157         this.fireEvent("selectionchange", this);
22158     },
22159
22160     // private
22161     restoreLast : function(){
22162         if(this._last){
22163             this.last = this._last;
22164         }
22165     },
22166
22167     // private
22168     acceptsNav : function(row, col, cm){
22169         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22170     },
22171
22172     // private
22173     onEditorKey : function(field, e){
22174         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22175         if(k == e.TAB){
22176             e.stopEvent();
22177             ed.completeEdit();
22178             if(e.shiftKey){
22179                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22180             }else{
22181                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22182             }
22183         }else if(k == e.ENTER && !e.ctrlKey){
22184             e.stopEvent();
22185             ed.completeEdit();
22186             if(e.shiftKey){
22187                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22188             }else{
22189                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22190             }
22191         }else if(k == e.ESC){
22192             ed.cancelEdit();
22193         }
22194         if(newCell){
22195             g.startEditing(newCell[0], newCell[1]);
22196         }
22197     }
22198 });/*
22199  * Based on:
22200  * Ext JS Library 1.1.1
22201  * Copyright(c) 2006-2007, Ext JS, LLC.
22202  *
22203  * Originally Released Under LGPL - original licence link has changed is not relivant.
22204  *
22205  * Fork - LGPL
22206  * <script type="text/javascript">
22207  */
22208  
22209 /**
22210  * @class Roo.bootstrap.PagingToolbar
22211  * @extends Roo.bootstrap.NavSimplebar
22212  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22213  * @constructor
22214  * Create a new PagingToolbar
22215  * @param {Object} config The config object
22216  * @param {Roo.data.Store} store
22217  */
22218 Roo.bootstrap.PagingToolbar = function(config)
22219 {
22220     // old args format still supported... - xtype is prefered..
22221         // created from xtype...
22222     
22223     this.ds = config.dataSource;
22224     
22225     if (config.store && !this.ds) {
22226         this.store= Roo.factory(config.store, Roo.data);
22227         this.ds = this.store;
22228         this.ds.xmodule = this.xmodule || false;
22229     }
22230     
22231     this.toolbarItems = [];
22232     if (config.items) {
22233         this.toolbarItems = config.items;
22234     }
22235     
22236     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22237     
22238     this.cursor = 0;
22239     
22240     if (this.ds) { 
22241         this.bind(this.ds);
22242     }
22243     
22244     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22245     
22246 };
22247
22248 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22249     /**
22250      * @cfg {Roo.data.Store} dataSource
22251      * The underlying data store providing the paged data
22252      */
22253     /**
22254      * @cfg {String/HTMLElement/Element} container
22255      * container The id or element that will contain the toolbar
22256      */
22257     /**
22258      * @cfg {Boolean} displayInfo
22259      * True to display the displayMsg (defaults to false)
22260      */
22261     /**
22262      * @cfg {Number} pageSize
22263      * The number of records to display per page (defaults to 20)
22264      */
22265     pageSize: 20,
22266     /**
22267      * @cfg {String} displayMsg
22268      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22269      */
22270     displayMsg : 'Displaying {0} - {1} of {2}',
22271     /**
22272      * @cfg {String} emptyMsg
22273      * The message to display when no records are found (defaults to "No data to display")
22274      */
22275     emptyMsg : 'No data to display',
22276     /**
22277      * Customizable piece of the default paging text (defaults to "Page")
22278      * @type String
22279      */
22280     beforePageText : "Page",
22281     /**
22282      * Customizable piece of the default paging text (defaults to "of %0")
22283      * @type String
22284      */
22285     afterPageText : "of {0}",
22286     /**
22287      * Customizable piece of the default paging text (defaults to "First Page")
22288      * @type String
22289      */
22290     firstText : "First Page",
22291     /**
22292      * Customizable piece of the default paging text (defaults to "Previous Page")
22293      * @type String
22294      */
22295     prevText : "Previous Page",
22296     /**
22297      * Customizable piece of the default paging text (defaults to "Next Page")
22298      * @type String
22299      */
22300     nextText : "Next Page",
22301     /**
22302      * Customizable piece of the default paging text (defaults to "Last Page")
22303      * @type String
22304      */
22305     lastText : "Last Page",
22306     /**
22307      * Customizable piece of the default paging text (defaults to "Refresh")
22308      * @type String
22309      */
22310     refreshText : "Refresh",
22311
22312     buttons : false,
22313     // private
22314     onRender : function(ct, position) 
22315     {
22316         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22317         this.navgroup.parentId = this.id;
22318         this.navgroup.onRender(this.el, null);
22319         // add the buttons to the navgroup
22320         
22321         if(this.displayInfo){
22322             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22323             this.displayEl = this.el.select('.x-paging-info', true).first();
22324 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22325 //            this.displayEl = navel.el.select('span',true).first();
22326         }
22327         
22328         var _this = this;
22329         
22330         if(this.buttons){
22331             Roo.each(_this.buttons, function(e){ // this might need to use render????
22332                Roo.factory(e).onRender(_this.el, null);
22333             });
22334         }
22335             
22336         Roo.each(_this.toolbarItems, function(e) {
22337             _this.navgroup.addItem(e);
22338         });
22339         
22340         
22341         this.first = this.navgroup.addItem({
22342             tooltip: this.firstText,
22343             cls: "prev",
22344             icon : 'fa fa-backward',
22345             disabled: true,
22346             preventDefault: true,
22347             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22348         });
22349         
22350         this.prev =  this.navgroup.addItem({
22351             tooltip: this.prevText,
22352             cls: "prev",
22353             icon : 'fa fa-step-backward',
22354             disabled: true,
22355             preventDefault: true,
22356             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22357         });
22358     //this.addSeparator();
22359         
22360         
22361         var field = this.navgroup.addItem( {
22362             tagtype : 'span',
22363             cls : 'x-paging-position',
22364             
22365             html : this.beforePageText  +
22366                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22367                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22368          } ); //?? escaped?
22369         
22370         this.field = field.el.select('input', true).first();
22371         this.field.on("keydown", this.onPagingKeydown, this);
22372         this.field.on("focus", function(){this.dom.select();});
22373     
22374     
22375         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22376         //this.field.setHeight(18);
22377         //this.addSeparator();
22378         this.next = this.navgroup.addItem({
22379             tooltip: this.nextText,
22380             cls: "next",
22381             html : ' <i class="fa fa-step-forward">',
22382             disabled: true,
22383             preventDefault: true,
22384             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22385         });
22386         this.last = this.navgroup.addItem({
22387             tooltip: this.lastText,
22388             icon : 'fa fa-forward',
22389             cls: "next",
22390             disabled: true,
22391             preventDefault: true,
22392             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22393         });
22394     //this.addSeparator();
22395         this.loading = this.navgroup.addItem({
22396             tooltip: this.refreshText,
22397             icon: 'fa fa-refresh',
22398             preventDefault: true,
22399             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22400         });
22401         
22402     },
22403
22404     // private
22405     updateInfo : function(){
22406         if(this.displayEl){
22407             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22408             var msg = count == 0 ?
22409                 this.emptyMsg :
22410                 String.format(
22411                     this.displayMsg,
22412                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22413                 );
22414             this.displayEl.update(msg);
22415         }
22416     },
22417
22418     // private
22419     onLoad : function(ds, r, o){
22420        this.cursor = o.params ? o.params.start : 0;
22421        var d = this.getPageData(),
22422             ap = d.activePage,
22423             ps = d.pages;
22424         
22425        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22426        this.field.dom.value = ap;
22427        this.first.setDisabled(ap == 1);
22428        this.prev.setDisabled(ap == 1);
22429        this.next.setDisabled(ap == ps);
22430        this.last.setDisabled(ap == ps);
22431        this.loading.enable();
22432        this.updateInfo();
22433     },
22434
22435     // private
22436     getPageData : function(){
22437         var total = this.ds.getTotalCount();
22438         return {
22439             total : total,
22440             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22441             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22442         };
22443     },
22444
22445     // private
22446     onLoadError : function(){
22447         this.loading.enable();
22448     },
22449
22450     // private
22451     onPagingKeydown : function(e){
22452         var k = e.getKey();
22453         var d = this.getPageData();
22454         if(k == e.RETURN){
22455             var v = this.field.dom.value, pageNum;
22456             if(!v || isNaN(pageNum = parseInt(v, 10))){
22457                 this.field.dom.value = d.activePage;
22458                 return;
22459             }
22460             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22461             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22462             e.stopEvent();
22463         }
22464         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))
22465         {
22466           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22467           this.field.dom.value = pageNum;
22468           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22469           e.stopEvent();
22470         }
22471         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22472         {
22473           var v = this.field.dom.value, pageNum; 
22474           var increment = (e.shiftKey) ? 10 : 1;
22475           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22476                 increment *= -1;
22477           }
22478           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22479             this.field.dom.value = d.activePage;
22480             return;
22481           }
22482           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22483           {
22484             this.field.dom.value = parseInt(v, 10) + increment;
22485             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22486             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22487           }
22488           e.stopEvent();
22489         }
22490     },
22491
22492     // private
22493     beforeLoad : function(){
22494         if(this.loading){
22495             this.loading.disable();
22496         }
22497     },
22498
22499     // private
22500     onClick : function(which){
22501         
22502         var ds = this.ds;
22503         if (!ds) {
22504             return;
22505         }
22506         
22507         switch(which){
22508             case "first":
22509                 ds.load({params:{start: 0, limit: this.pageSize}});
22510             break;
22511             case "prev":
22512                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22513             break;
22514             case "next":
22515                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22516             break;
22517             case "last":
22518                 var total = ds.getTotalCount();
22519                 var extra = total % this.pageSize;
22520                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22521                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22522             break;
22523             case "refresh":
22524                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22525             break;
22526         }
22527     },
22528
22529     /**
22530      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22531      * @param {Roo.data.Store} store The data store to unbind
22532      */
22533     unbind : function(ds){
22534         ds.un("beforeload", this.beforeLoad, this);
22535         ds.un("load", this.onLoad, this);
22536         ds.un("loadexception", this.onLoadError, this);
22537         ds.un("remove", this.updateInfo, this);
22538         ds.un("add", this.updateInfo, this);
22539         this.ds = undefined;
22540     },
22541
22542     /**
22543      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22544      * @param {Roo.data.Store} store The data store to bind
22545      */
22546     bind : function(ds){
22547         ds.on("beforeload", this.beforeLoad, this);
22548         ds.on("load", this.onLoad, this);
22549         ds.on("loadexception", this.onLoadError, this);
22550         ds.on("remove", this.updateInfo, this);
22551         ds.on("add", this.updateInfo, this);
22552         this.ds = ds;
22553     }
22554 });/*
22555  * - LGPL
22556  *
22557  * element
22558  * 
22559  */
22560
22561 /**
22562  * @class Roo.bootstrap.MessageBar
22563  * @extends Roo.bootstrap.Component
22564  * Bootstrap MessageBar class
22565  * @cfg {String} html contents of the MessageBar
22566  * @cfg {String} weight (info | success | warning | danger) default info
22567  * @cfg {String} beforeClass insert the bar before the given class
22568  * @cfg {Boolean} closable (true | false) default false
22569  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22570  * 
22571  * @constructor
22572  * Create a new Element
22573  * @param {Object} config The config object
22574  */
22575
22576 Roo.bootstrap.MessageBar = function(config){
22577     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22578 };
22579
22580 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22581     
22582     html: '',
22583     weight: 'info',
22584     closable: false,
22585     fixed: false,
22586     beforeClass: 'bootstrap-sticky-wrap',
22587     
22588     getAutoCreate : function(){
22589         
22590         var cfg = {
22591             tag: 'div',
22592             cls: 'alert alert-dismissable alert-' + this.weight,
22593             cn: [
22594                 {
22595                     tag: 'span',
22596                     cls: 'message',
22597                     html: this.html || ''
22598                 }
22599             ]
22600         };
22601         
22602         if(this.fixed){
22603             cfg.cls += ' alert-messages-fixed';
22604         }
22605         
22606         if(this.closable){
22607             cfg.cn.push({
22608                 tag: 'button',
22609                 cls: 'close',
22610                 html: 'x'
22611             });
22612         }
22613         
22614         return cfg;
22615     },
22616     
22617     onRender : function(ct, position)
22618     {
22619         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22620         
22621         if(!this.el){
22622             var cfg = Roo.apply({},  this.getAutoCreate());
22623             cfg.id = Roo.id();
22624             
22625             if (this.cls) {
22626                 cfg.cls += ' ' + this.cls;
22627             }
22628             if (this.style) {
22629                 cfg.style = this.style;
22630             }
22631             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22632             
22633             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22634         }
22635         
22636         this.el.select('>button.close').on('click', this.hide, this);
22637         
22638     },
22639     
22640     show : function()
22641     {
22642         if (!this.rendered) {
22643             this.render();
22644         }
22645         
22646         this.el.show();
22647         
22648         this.fireEvent('show', this);
22649         
22650     },
22651     
22652     hide : function()
22653     {
22654         if (!this.rendered) {
22655             this.render();
22656         }
22657         
22658         this.el.hide();
22659         
22660         this.fireEvent('hide', this);
22661     },
22662     
22663     update : function()
22664     {
22665 //        var e = this.el.dom.firstChild;
22666 //        
22667 //        if(this.closable){
22668 //            e = e.nextSibling;
22669 //        }
22670 //        
22671 //        e.data = this.html || '';
22672
22673         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22674     }
22675    
22676 });
22677
22678  
22679
22680      /*
22681  * - LGPL
22682  *
22683  * Graph
22684  * 
22685  */
22686
22687
22688 /**
22689  * @class Roo.bootstrap.Graph
22690  * @extends Roo.bootstrap.Component
22691  * Bootstrap Graph class
22692 > Prameters
22693  -sm {number} sm 4
22694  -md {number} md 5
22695  @cfg {String} graphtype  bar | vbar | pie
22696  @cfg {number} g_x coodinator | centre x (pie)
22697  @cfg {number} g_y coodinator | centre y (pie)
22698  @cfg {number} g_r radius (pie)
22699  @cfg {number} g_height height of the chart (respected by all elements in the set)
22700  @cfg {number} g_width width of the chart (respected by all elements in the set)
22701  @cfg {Object} title The title of the chart
22702     
22703  -{Array}  values
22704  -opts (object) options for the chart 
22705      o {
22706      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22707      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22708      o vgutter (number)
22709      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.
22710      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22711      o to
22712      o stretch (boolean)
22713      o }
22714  -opts (object) options for the pie
22715      o{
22716      o cut
22717      o startAngle (number)
22718      o endAngle (number)
22719      } 
22720  *
22721  * @constructor
22722  * Create a new Input
22723  * @param {Object} config The config object
22724  */
22725
22726 Roo.bootstrap.Graph = function(config){
22727     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22728     
22729     this.addEvents({
22730         // img events
22731         /**
22732          * @event click
22733          * The img click event for the img.
22734          * @param {Roo.EventObject} e
22735          */
22736         "click" : true
22737     });
22738 };
22739
22740 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22741     
22742     sm: 4,
22743     md: 5,
22744     graphtype: 'bar',
22745     g_height: 250,
22746     g_width: 400,
22747     g_x: 50,
22748     g_y: 50,
22749     g_r: 30,
22750     opts:{
22751         //g_colors: this.colors,
22752         g_type: 'soft',
22753         g_gutter: '20%'
22754
22755     },
22756     title : false,
22757
22758     getAutoCreate : function(){
22759         
22760         var cfg = {
22761             tag: 'div',
22762             html : null
22763         };
22764         
22765         
22766         return  cfg;
22767     },
22768
22769     onRender : function(ct,position){
22770         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22771         this.raphael = Raphael(this.el.dom);
22772         
22773                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22774                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22775                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22776                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22777                 /*
22778                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22779                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22780                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22781                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22782                 
22783                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22784                 r.barchart(330, 10, 300, 220, data1);
22785                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22786                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22787                 */
22788                 
22789                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22790                 // r.barchart(30, 30, 560, 250,  xdata, {
22791                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22792                 //     axis : "0 0 1 1",
22793                 //     axisxlabels :  xdata
22794                 //     //yvalues : cols,
22795                    
22796                 // });
22797 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22798 //        
22799 //        this.load(null,xdata,{
22800 //                axis : "0 0 1 1",
22801 //                axisxlabels :  xdata
22802 //                });
22803
22804     },
22805
22806     load : function(graphtype,xdata,opts){
22807         this.raphael.clear();
22808         if(!graphtype) {
22809             graphtype = this.graphtype;
22810         }
22811         if(!opts){
22812             opts = this.opts;
22813         }
22814         var r = this.raphael,
22815             fin = function () {
22816                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22817             },
22818             fout = function () {
22819                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22820             },
22821             pfin = function() {
22822                 this.sector.stop();
22823                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22824
22825                 if (this.label) {
22826                     this.label[0].stop();
22827                     this.label[0].attr({ r: 7.5 });
22828                     this.label[1].attr({ "font-weight": 800 });
22829                 }
22830             },
22831             pfout = function() {
22832                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22833
22834                 if (this.label) {
22835                     this.label[0].animate({ r: 5 }, 500, "bounce");
22836                     this.label[1].attr({ "font-weight": 400 });
22837                 }
22838             };
22839
22840         switch(graphtype){
22841             case 'bar':
22842                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22843                 break;
22844             case 'hbar':
22845                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22846                 break;
22847             case 'pie':
22848 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22849 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22850 //            
22851                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22852                 
22853                 break;
22854
22855         }
22856         
22857         if(this.title){
22858             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22859         }
22860         
22861     },
22862     
22863     setTitle: function(o)
22864     {
22865         this.title = o;
22866     },
22867     
22868     initEvents: function() {
22869         
22870         if(!this.href){
22871             this.el.on('click', this.onClick, this);
22872         }
22873     },
22874     
22875     onClick : function(e)
22876     {
22877         Roo.log('img onclick');
22878         this.fireEvent('click', this, e);
22879     }
22880    
22881 });
22882
22883  
22884 /*
22885  * - LGPL
22886  *
22887  * numberBox
22888  * 
22889  */
22890 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22891
22892 /**
22893  * @class Roo.bootstrap.dash.NumberBox
22894  * @extends Roo.bootstrap.Component
22895  * Bootstrap NumberBox class
22896  * @cfg {String} headline Box headline
22897  * @cfg {String} content Box content
22898  * @cfg {String} icon Box icon
22899  * @cfg {String} footer Footer text
22900  * @cfg {String} fhref Footer href
22901  * 
22902  * @constructor
22903  * Create a new NumberBox
22904  * @param {Object} config The config object
22905  */
22906
22907
22908 Roo.bootstrap.dash.NumberBox = function(config){
22909     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22910     
22911 };
22912
22913 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22914     
22915     headline : '',
22916     content : '',
22917     icon : '',
22918     footer : '',
22919     fhref : '',
22920     ficon : '',
22921     
22922     getAutoCreate : function(){
22923         
22924         var cfg = {
22925             tag : 'div',
22926             cls : 'small-box ',
22927             cn : [
22928                 {
22929                     tag : 'div',
22930                     cls : 'inner',
22931                     cn :[
22932                         {
22933                             tag : 'h3',
22934                             cls : 'roo-headline',
22935                             html : this.headline
22936                         },
22937                         {
22938                             tag : 'p',
22939                             cls : 'roo-content',
22940                             html : this.content
22941                         }
22942                     ]
22943                 }
22944             ]
22945         };
22946         
22947         if(this.icon){
22948             cfg.cn.push({
22949                 tag : 'div',
22950                 cls : 'icon',
22951                 cn :[
22952                     {
22953                         tag : 'i',
22954                         cls : 'ion ' + this.icon
22955                     }
22956                 ]
22957             });
22958         }
22959         
22960         if(this.footer){
22961             var footer = {
22962                 tag : 'a',
22963                 cls : 'small-box-footer',
22964                 href : this.fhref || '#',
22965                 html : this.footer
22966             };
22967             
22968             cfg.cn.push(footer);
22969             
22970         }
22971         
22972         return  cfg;
22973     },
22974
22975     onRender : function(ct,position){
22976         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22977
22978
22979        
22980                 
22981     },
22982
22983     setHeadline: function (value)
22984     {
22985         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22986     },
22987     
22988     setFooter: function (value, href)
22989     {
22990         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22991         
22992         if(href){
22993             this.el.select('a.small-box-footer',true).first().attr('href', href);
22994         }
22995         
22996     },
22997
22998     setContent: function (value)
22999     {
23000         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23001     },
23002
23003     initEvents: function() 
23004     {   
23005         
23006     }
23007     
23008 });
23009
23010  
23011 /*
23012  * - LGPL
23013  *
23014  * TabBox
23015  * 
23016  */
23017 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23018
23019 /**
23020  * @class Roo.bootstrap.dash.TabBox
23021  * @extends Roo.bootstrap.Component
23022  * Bootstrap TabBox class
23023  * @cfg {String} title Title of the TabBox
23024  * @cfg {String} icon Icon of the TabBox
23025  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23026  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23027  * 
23028  * @constructor
23029  * Create a new TabBox
23030  * @param {Object} config The config object
23031  */
23032
23033
23034 Roo.bootstrap.dash.TabBox = function(config){
23035     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23036     this.addEvents({
23037         // raw events
23038         /**
23039          * @event addpane
23040          * When a pane is added
23041          * @param {Roo.bootstrap.dash.TabPane} pane
23042          */
23043         "addpane" : true,
23044         /**
23045          * @event activatepane
23046          * When a pane is activated
23047          * @param {Roo.bootstrap.dash.TabPane} pane
23048          */
23049         "activatepane" : true
23050         
23051          
23052     });
23053     
23054     this.panes = [];
23055 };
23056
23057 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23058
23059     title : '',
23060     icon : false,
23061     showtabs : true,
23062     tabScrollable : false,
23063     
23064     getChildContainer : function()
23065     {
23066         return this.el.select('.tab-content', true).first();
23067     },
23068     
23069     getAutoCreate : function(){
23070         
23071         var header = {
23072             tag: 'li',
23073             cls: 'pull-left header',
23074             html: this.title,
23075             cn : []
23076         };
23077         
23078         if(this.icon){
23079             header.cn.push({
23080                 tag: 'i',
23081                 cls: 'fa ' + this.icon
23082             });
23083         }
23084         
23085         var h = {
23086             tag: 'ul',
23087             cls: 'nav nav-tabs pull-right',
23088             cn: [
23089                 header
23090             ]
23091         };
23092         
23093         if(this.tabScrollable){
23094             h = {
23095                 tag: 'div',
23096                 cls: 'tab-header',
23097                 cn: [
23098                     {
23099                         tag: 'ul',
23100                         cls: 'nav nav-tabs pull-right',
23101                         cn: [
23102                             header
23103                         ]
23104                     }
23105                 ]
23106             };
23107         }
23108         
23109         var cfg = {
23110             tag: 'div',
23111             cls: 'nav-tabs-custom',
23112             cn: [
23113                 h,
23114                 {
23115                     tag: 'div',
23116                     cls: 'tab-content no-padding',
23117                     cn: []
23118                 }
23119             ]
23120         };
23121
23122         return  cfg;
23123     },
23124     initEvents : function()
23125     {
23126         //Roo.log('add add pane handler');
23127         this.on('addpane', this.onAddPane, this);
23128     },
23129      /**
23130      * Updates the box title
23131      * @param {String} html to set the title to.
23132      */
23133     setTitle : function(value)
23134     {
23135         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23136     },
23137     onAddPane : function(pane)
23138     {
23139         this.panes.push(pane);
23140         //Roo.log('addpane');
23141         //Roo.log(pane);
23142         // tabs are rendere left to right..
23143         if(!this.showtabs){
23144             return;
23145         }
23146         
23147         var ctr = this.el.select('.nav-tabs', true).first();
23148          
23149          
23150         var existing = ctr.select('.nav-tab',true);
23151         var qty = existing.getCount();;
23152         
23153         
23154         var tab = ctr.createChild({
23155             tag : 'li',
23156             cls : 'nav-tab' + (qty ? '' : ' active'),
23157             cn : [
23158                 {
23159                     tag : 'a',
23160                     href:'#',
23161                     html : pane.title
23162                 }
23163             ]
23164         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23165         pane.tab = tab;
23166         
23167         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23168         if (!qty) {
23169             pane.el.addClass('active');
23170         }
23171         
23172                 
23173     },
23174     onTabClick : function(ev,un,ob,pane)
23175     {
23176         //Roo.log('tab - prev default');
23177         ev.preventDefault();
23178         
23179         
23180         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23181         pane.tab.addClass('active');
23182         //Roo.log(pane.title);
23183         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23184         // technically we should have a deactivate event.. but maybe add later.
23185         // and it should not de-activate the selected tab...
23186         this.fireEvent('activatepane', pane);
23187         pane.el.addClass('active');
23188         pane.fireEvent('activate');
23189         
23190         
23191     },
23192     
23193     getActivePane : function()
23194     {
23195         var r = false;
23196         Roo.each(this.panes, function(p) {
23197             if(p.el.hasClass('active')){
23198                 r = p;
23199                 return false;
23200             }
23201             
23202             return;
23203         });
23204         
23205         return r;
23206     }
23207     
23208     
23209 });
23210
23211  
23212 /*
23213  * - LGPL
23214  *
23215  * Tab pane
23216  * 
23217  */
23218 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23219 /**
23220  * @class Roo.bootstrap.TabPane
23221  * @extends Roo.bootstrap.Component
23222  * Bootstrap TabPane class
23223  * @cfg {Boolean} active (false | true) Default false
23224  * @cfg {String} title title of panel
23225
23226  * 
23227  * @constructor
23228  * Create a new TabPane
23229  * @param {Object} config The config object
23230  */
23231
23232 Roo.bootstrap.dash.TabPane = function(config){
23233     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23234     
23235     this.addEvents({
23236         // raw events
23237         /**
23238          * @event activate
23239          * When a pane is activated
23240          * @param {Roo.bootstrap.dash.TabPane} pane
23241          */
23242         "activate" : true
23243          
23244     });
23245 };
23246
23247 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23248     
23249     active : false,
23250     title : '',
23251     
23252     // the tabBox that this is attached to.
23253     tab : false,
23254      
23255     getAutoCreate : function() 
23256     {
23257         var cfg = {
23258             tag: 'div',
23259             cls: 'tab-pane'
23260         };
23261         
23262         if(this.active){
23263             cfg.cls += ' active';
23264         }
23265         
23266         return cfg;
23267     },
23268     initEvents  : function()
23269     {
23270         //Roo.log('trigger add pane handler');
23271         this.parent().fireEvent('addpane', this)
23272     },
23273     
23274      /**
23275      * Updates the tab title 
23276      * @param {String} html to set the title to.
23277      */
23278     setTitle: function(str)
23279     {
23280         if (!this.tab) {
23281             return;
23282         }
23283         this.title = str;
23284         this.tab.select('a', true).first().dom.innerHTML = str;
23285         
23286     }
23287     
23288     
23289     
23290 });
23291
23292  
23293
23294
23295  /*
23296  * - LGPL
23297  *
23298  * menu
23299  * 
23300  */
23301 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23302
23303 /**
23304  * @class Roo.bootstrap.menu.Menu
23305  * @extends Roo.bootstrap.Component
23306  * Bootstrap Menu class - container for Menu
23307  * @cfg {String} html Text of the menu
23308  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23309  * @cfg {String} icon Font awesome icon
23310  * @cfg {String} pos Menu align to (top | bottom) default bottom
23311  * 
23312  * 
23313  * @constructor
23314  * Create a new Menu
23315  * @param {Object} config The config object
23316  */
23317
23318
23319 Roo.bootstrap.menu.Menu = function(config){
23320     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23321     
23322     this.addEvents({
23323         /**
23324          * @event beforeshow
23325          * Fires before this menu is displayed
23326          * @param {Roo.bootstrap.menu.Menu} this
23327          */
23328         beforeshow : true,
23329         /**
23330          * @event beforehide
23331          * Fires before this menu is hidden
23332          * @param {Roo.bootstrap.menu.Menu} this
23333          */
23334         beforehide : true,
23335         /**
23336          * @event show
23337          * Fires after this menu is displayed
23338          * @param {Roo.bootstrap.menu.Menu} this
23339          */
23340         show : true,
23341         /**
23342          * @event hide
23343          * Fires after this menu is hidden
23344          * @param {Roo.bootstrap.menu.Menu} this
23345          */
23346         hide : true,
23347         /**
23348          * @event click
23349          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23350          * @param {Roo.bootstrap.menu.Menu} this
23351          * @param {Roo.EventObject} e
23352          */
23353         click : true
23354     });
23355     
23356 };
23357
23358 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23359     
23360     submenu : false,
23361     html : '',
23362     weight : 'default',
23363     icon : false,
23364     pos : 'bottom',
23365     
23366     
23367     getChildContainer : function() {
23368         if(this.isSubMenu){
23369             return this.el;
23370         }
23371         
23372         return this.el.select('ul.dropdown-menu', true).first();  
23373     },
23374     
23375     getAutoCreate : function()
23376     {
23377         var text = [
23378             {
23379                 tag : 'span',
23380                 cls : 'roo-menu-text',
23381                 html : this.html
23382             }
23383         ];
23384         
23385         if(this.icon){
23386             text.unshift({
23387                 tag : 'i',
23388                 cls : 'fa ' + this.icon
23389             })
23390         }
23391         
23392         
23393         var cfg = {
23394             tag : 'div',
23395             cls : 'btn-group',
23396             cn : [
23397                 {
23398                     tag : 'button',
23399                     cls : 'dropdown-button btn btn-' + this.weight,
23400                     cn : text
23401                 },
23402                 {
23403                     tag : 'button',
23404                     cls : 'dropdown-toggle btn btn-' + this.weight,
23405                     cn : [
23406                         {
23407                             tag : 'span',
23408                             cls : 'caret'
23409                         }
23410                     ]
23411                 },
23412                 {
23413                     tag : 'ul',
23414                     cls : 'dropdown-menu'
23415                 }
23416             ]
23417             
23418         };
23419         
23420         if(this.pos == 'top'){
23421             cfg.cls += ' dropup';
23422         }
23423         
23424         if(this.isSubMenu){
23425             cfg = {
23426                 tag : 'ul',
23427                 cls : 'dropdown-menu'
23428             }
23429         }
23430         
23431         return cfg;
23432     },
23433     
23434     onRender : function(ct, position)
23435     {
23436         this.isSubMenu = ct.hasClass('dropdown-submenu');
23437         
23438         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23439     },
23440     
23441     initEvents : function() 
23442     {
23443         if(this.isSubMenu){
23444             return;
23445         }
23446         
23447         this.hidden = true;
23448         
23449         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23450         this.triggerEl.on('click', this.onTriggerPress, this);
23451         
23452         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23453         this.buttonEl.on('click', this.onClick, this);
23454         
23455     },
23456     
23457     list : function()
23458     {
23459         if(this.isSubMenu){
23460             return this.el;
23461         }
23462         
23463         return this.el.select('ul.dropdown-menu', true).first();
23464     },
23465     
23466     onClick : function(e)
23467     {
23468         this.fireEvent("click", this, e);
23469     },
23470     
23471     onTriggerPress  : function(e)
23472     {   
23473         if (this.isVisible()) {
23474             this.hide();
23475         } else {
23476             this.show();
23477         }
23478     },
23479     
23480     isVisible : function(){
23481         return !this.hidden;
23482     },
23483     
23484     show : function()
23485     {
23486         this.fireEvent("beforeshow", this);
23487         
23488         this.hidden = false;
23489         this.el.addClass('open');
23490         
23491         Roo.get(document).on("mouseup", this.onMouseUp, this);
23492         
23493         this.fireEvent("show", this);
23494         
23495         
23496     },
23497     
23498     hide : function()
23499     {
23500         this.fireEvent("beforehide", this);
23501         
23502         this.hidden = true;
23503         this.el.removeClass('open');
23504         
23505         Roo.get(document).un("mouseup", this.onMouseUp);
23506         
23507         this.fireEvent("hide", this);
23508     },
23509     
23510     onMouseUp : function()
23511     {
23512         this.hide();
23513     }
23514     
23515 });
23516
23517  
23518  /*
23519  * - LGPL
23520  *
23521  * menu item
23522  * 
23523  */
23524 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23525
23526 /**
23527  * @class Roo.bootstrap.menu.Item
23528  * @extends Roo.bootstrap.Component
23529  * Bootstrap MenuItem class
23530  * @cfg {Boolean} submenu (true | false) default false
23531  * @cfg {String} html text of the item
23532  * @cfg {String} href the link
23533  * @cfg {Boolean} disable (true | false) default false
23534  * @cfg {Boolean} preventDefault (true | false) default true
23535  * @cfg {String} icon Font awesome icon
23536  * @cfg {String} pos Submenu align to (left | right) default right 
23537  * 
23538  * 
23539  * @constructor
23540  * Create a new Item
23541  * @param {Object} config The config object
23542  */
23543
23544
23545 Roo.bootstrap.menu.Item = function(config){
23546     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23547     this.addEvents({
23548         /**
23549          * @event mouseover
23550          * Fires when the mouse is hovering over this menu
23551          * @param {Roo.bootstrap.menu.Item} this
23552          * @param {Roo.EventObject} e
23553          */
23554         mouseover : true,
23555         /**
23556          * @event mouseout
23557          * Fires when the mouse exits this menu
23558          * @param {Roo.bootstrap.menu.Item} this
23559          * @param {Roo.EventObject} e
23560          */
23561         mouseout : true,
23562         // raw events
23563         /**
23564          * @event click
23565          * The raw click event for the entire grid.
23566          * @param {Roo.EventObject} e
23567          */
23568         click : true
23569     });
23570 };
23571
23572 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23573     
23574     submenu : false,
23575     href : '',
23576     html : '',
23577     preventDefault: true,
23578     disable : false,
23579     icon : false,
23580     pos : 'right',
23581     
23582     getAutoCreate : function()
23583     {
23584         var text = [
23585             {
23586                 tag : 'span',
23587                 cls : 'roo-menu-item-text',
23588                 html : this.html
23589             }
23590         ];
23591         
23592         if(this.icon){
23593             text.unshift({
23594                 tag : 'i',
23595                 cls : 'fa ' + this.icon
23596             })
23597         }
23598         
23599         var cfg = {
23600             tag : 'li',
23601             cn : [
23602                 {
23603                     tag : 'a',
23604                     href : this.href || '#',
23605                     cn : text
23606                 }
23607             ]
23608         };
23609         
23610         if(this.disable){
23611             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23612         }
23613         
23614         if(this.submenu){
23615             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23616             
23617             if(this.pos == 'left'){
23618                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23619             }
23620         }
23621         
23622         return cfg;
23623     },
23624     
23625     initEvents : function() 
23626     {
23627         this.el.on('mouseover', this.onMouseOver, this);
23628         this.el.on('mouseout', this.onMouseOut, this);
23629         
23630         this.el.select('a', true).first().on('click', this.onClick, this);
23631         
23632     },
23633     
23634     onClick : function(e)
23635     {
23636         if(this.preventDefault){
23637             e.preventDefault();
23638         }
23639         
23640         this.fireEvent("click", this, e);
23641     },
23642     
23643     onMouseOver : function(e)
23644     {
23645         if(this.submenu && this.pos == 'left'){
23646             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23647         }
23648         
23649         this.fireEvent("mouseover", this, e);
23650     },
23651     
23652     onMouseOut : function(e)
23653     {
23654         this.fireEvent("mouseout", this, e);
23655     }
23656 });
23657
23658  
23659
23660  /*
23661  * - LGPL
23662  *
23663  * menu separator
23664  * 
23665  */
23666 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23667
23668 /**
23669  * @class Roo.bootstrap.menu.Separator
23670  * @extends Roo.bootstrap.Component
23671  * Bootstrap Separator class
23672  * 
23673  * @constructor
23674  * Create a new Separator
23675  * @param {Object} config The config object
23676  */
23677
23678
23679 Roo.bootstrap.menu.Separator = function(config){
23680     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23681 };
23682
23683 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23684     
23685     getAutoCreate : function(){
23686         var cfg = {
23687             tag : 'li',
23688             cls: 'divider'
23689         };
23690         
23691         return cfg;
23692     }
23693    
23694 });
23695
23696  
23697
23698  /*
23699  * - LGPL
23700  *
23701  * Tooltip
23702  * 
23703  */
23704
23705 /**
23706  * @class Roo.bootstrap.Tooltip
23707  * Bootstrap Tooltip class
23708  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23709  * to determine which dom element triggers the tooltip.
23710  * 
23711  * It needs to add support for additional attributes like tooltip-position
23712  * 
23713  * @constructor
23714  * Create a new Toolti
23715  * @param {Object} config The config object
23716  */
23717
23718 Roo.bootstrap.Tooltip = function(config){
23719     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23720 };
23721
23722 Roo.apply(Roo.bootstrap.Tooltip, {
23723     /**
23724      * @function init initialize tooltip monitoring.
23725      * @static
23726      */
23727     currentEl : false,
23728     currentTip : false,
23729     currentRegion : false,
23730     
23731     //  init : delay?
23732     
23733     init : function()
23734     {
23735         Roo.get(document).on('mouseover', this.enter ,this);
23736         Roo.get(document).on('mouseout', this.leave, this);
23737          
23738         
23739         this.currentTip = new Roo.bootstrap.Tooltip();
23740     },
23741     
23742     enter : function(ev)
23743     {
23744         var dom = ev.getTarget();
23745         
23746         //Roo.log(['enter',dom]);
23747         var el = Roo.fly(dom);
23748         if (this.currentEl) {
23749             //Roo.log(dom);
23750             //Roo.log(this.currentEl);
23751             //Roo.log(this.currentEl.contains(dom));
23752             if (this.currentEl == el) {
23753                 return;
23754             }
23755             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23756                 return;
23757             }
23758
23759         }
23760         
23761         if (this.currentTip.el) {
23762             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23763         }    
23764         //Roo.log(ev);
23765         var bindEl = el;
23766         
23767         // you can not look for children, as if el is the body.. then everythign is the child..
23768         if (!el.attr('tooltip')) { //
23769             if (!el.select("[tooltip]").elements.length) {
23770                 return;
23771             }
23772             // is the mouse over this child...?
23773             bindEl = el.select("[tooltip]").first();
23774             var xy = ev.getXY();
23775             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23776                 //Roo.log("not in region.");
23777                 return;
23778             }
23779             //Roo.log("child element over..");
23780             
23781         }
23782         this.currentEl = bindEl;
23783         this.currentTip.bind(bindEl);
23784         this.currentRegion = Roo.lib.Region.getRegion(dom);
23785         this.currentTip.enter();
23786         
23787     },
23788     leave : function(ev)
23789     {
23790         var dom = ev.getTarget();
23791         //Roo.log(['leave',dom]);
23792         if (!this.currentEl) {
23793             return;
23794         }
23795         
23796         
23797         if (dom != this.currentEl.dom) {
23798             return;
23799         }
23800         var xy = ev.getXY();
23801         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23802             return;
23803         }
23804         // only activate leave if mouse cursor is outside... bounding box..
23805         
23806         
23807         
23808         
23809         if (this.currentTip) {
23810             this.currentTip.leave();
23811         }
23812         //Roo.log('clear currentEl');
23813         this.currentEl = false;
23814         
23815         
23816     },
23817     alignment : {
23818         'left' : ['r-l', [-2,0], 'right'],
23819         'right' : ['l-r', [2,0], 'left'],
23820         'bottom' : ['t-b', [0,2], 'top'],
23821         'top' : [ 'b-t', [0,-2], 'bottom']
23822     }
23823     
23824 });
23825
23826
23827 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23828     
23829     
23830     bindEl : false,
23831     
23832     delay : null, // can be { show : 300 , hide: 500}
23833     
23834     timeout : null,
23835     
23836     hoverState : null, //???
23837     
23838     placement : 'bottom', 
23839     
23840     getAutoCreate : function(){
23841     
23842         var cfg = {
23843            cls : 'tooltip',
23844            role : 'tooltip',
23845            cn : [
23846                 {
23847                     cls : 'tooltip-arrow'
23848                 },
23849                 {
23850                     cls : 'tooltip-inner'
23851                 }
23852            ]
23853         };
23854         
23855         return cfg;
23856     },
23857     bind : function(el)
23858     {
23859         this.bindEl = el;
23860     },
23861       
23862     
23863     enter : function () {
23864        
23865         if (this.timeout != null) {
23866             clearTimeout(this.timeout);
23867         }
23868         
23869         this.hoverState = 'in';
23870          //Roo.log("enter - show");
23871         if (!this.delay || !this.delay.show) {
23872             this.show();
23873             return;
23874         }
23875         var _t = this;
23876         this.timeout = setTimeout(function () {
23877             if (_t.hoverState == 'in') {
23878                 _t.show();
23879             }
23880         }, this.delay.show);
23881     },
23882     leave : function()
23883     {
23884         clearTimeout(this.timeout);
23885     
23886         this.hoverState = 'out';
23887          if (!this.delay || !this.delay.hide) {
23888             this.hide();
23889             return;
23890         }
23891        
23892         var _t = this;
23893         this.timeout = setTimeout(function () {
23894             //Roo.log("leave - timeout");
23895             
23896             if (_t.hoverState == 'out') {
23897                 _t.hide();
23898                 Roo.bootstrap.Tooltip.currentEl = false;
23899             }
23900         }, delay);
23901     },
23902     
23903     show : function ()
23904     {
23905         if (!this.el) {
23906             this.render(document.body);
23907         }
23908         // set content.
23909         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23910         
23911         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23912         
23913         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23914         
23915         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23916         
23917         var placement = typeof this.placement == 'function' ?
23918             this.placement.call(this, this.el, on_el) :
23919             this.placement;
23920             
23921         var autoToken = /\s?auto?\s?/i;
23922         var autoPlace = autoToken.test(placement);
23923         if (autoPlace) {
23924             placement = placement.replace(autoToken, '') || 'top';
23925         }
23926         
23927         //this.el.detach()
23928         //this.el.setXY([0,0]);
23929         this.el.show();
23930         //this.el.dom.style.display='block';
23931         
23932         //this.el.appendTo(on_el);
23933         
23934         var p = this.getPosition();
23935         var box = this.el.getBox();
23936         
23937         if (autoPlace) {
23938             // fixme..
23939         }
23940         
23941         var align = Roo.bootstrap.Tooltip.alignment[placement];
23942         
23943         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23944         
23945         if(placement == 'top' || placement == 'bottom'){
23946             if(xy[0] < 0){
23947                 placement = 'right';
23948             }
23949             
23950             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23951                 placement = 'left';
23952             }
23953         }
23954         
23955         align = Roo.bootstrap.Tooltip.alignment[placement];
23956         
23957         this.el.alignTo(this.bindEl, align[0],align[1]);
23958         //var arrow = this.el.select('.arrow',true).first();
23959         //arrow.set(align[2], 
23960         
23961         this.el.addClass(placement);
23962         
23963         this.el.addClass('in fade');
23964         
23965         this.hoverState = null;
23966         
23967         if (this.el.hasClass('fade')) {
23968             // fade it?
23969         }
23970         
23971     },
23972     hide : function()
23973     {
23974          
23975         if (!this.el) {
23976             return;
23977         }
23978         //this.el.setXY([0,0]);
23979         this.el.removeClass('in');
23980         //this.el.hide();
23981         
23982     }
23983     
23984 });
23985  
23986
23987  /*
23988  * - LGPL
23989  *
23990  * Location Picker
23991  * 
23992  */
23993
23994 /**
23995  * @class Roo.bootstrap.LocationPicker
23996  * @extends Roo.bootstrap.Component
23997  * Bootstrap LocationPicker class
23998  * @cfg {Number} latitude Position when init default 0
23999  * @cfg {Number} longitude Position when init default 0
24000  * @cfg {Number} zoom default 15
24001  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24002  * @cfg {Boolean} mapTypeControl default false
24003  * @cfg {Boolean} disableDoubleClickZoom default false
24004  * @cfg {Boolean} scrollwheel default true
24005  * @cfg {Boolean} streetViewControl default false
24006  * @cfg {Number} radius default 0
24007  * @cfg {String} locationName
24008  * @cfg {Boolean} draggable default true
24009  * @cfg {Boolean} enableAutocomplete default false
24010  * @cfg {Boolean} enableReverseGeocode default true
24011  * @cfg {String} markerTitle
24012  * 
24013  * @constructor
24014  * Create a new LocationPicker
24015  * @param {Object} config The config object
24016  */
24017
24018
24019 Roo.bootstrap.LocationPicker = function(config){
24020     
24021     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24022     
24023     this.addEvents({
24024         /**
24025          * @event initial
24026          * Fires when the picker initialized.
24027          * @param {Roo.bootstrap.LocationPicker} this
24028          * @param {Google Location} location
24029          */
24030         initial : true,
24031         /**
24032          * @event positionchanged
24033          * Fires when the picker position changed.
24034          * @param {Roo.bootstrap.LocationPicker} this
24035          * @param {Google Location} location
24036          */
24037         positionchanged : true,
24038         /**
24039          * @event resize
24040          * Fires when the map resize.
24041          * @param {Roo.bootstrap.LocationPicker} this
24042          */
24043         resize : true,
24044         /**
24045          * @event show
24046          * Fires when the map show.
24047          * @param {Roo.bootstrap.LocationPicker} this
24048          */
24049         show : true,
24050         /**
24051          * @event hide
24052          * Fires when the map hide.
24053          * @param {Roo.bootstrap.LocationPicker} this
24054          */
24055         hide : true,
24056         /**
24057          * @event mapClick
24058          * Fires when click the map.
24059          * @param {Roo.bootstrap.LocationPicker} this
24060          * @param {Map event} e
24061          */
24062         mapClick : true,
24063         /**
24064          * @event mapRightClick
24065          * Fires when right click the map.
24066          * @param {Roo.bootstrap.LocationPicker} this
24067          * @param {Map event} e
24068          */
24069         mapRightClick : true,
24070         /**
24071          * @event markerClick
24072          * Fires when click the marker.
24073          * @param {Roo.bootstrap.LocationPicker} this
24074          * @param {Map event} e
24075          */
24076         markerClick : true,
24077         /**
24078          * @event markerRightClick
24079          * Fires when right click the marker.
24080          * @param {Roo.bootstrap.LocationPicker} this
24081          * @param {Map event} e
24082          */
24083         markerRightClick : true,
24084         /**
24085          * @event OverlayViewDraw
24086          * Fires when OverlayView Draw
24087          * @param {Roo.bootstrap.LocationPicker} this
24088          */
24089         OverlayViewDraw : true,
24090         /**
24091          * @event OverlayViewOnAdd
24092          * Fires when OverlayView Draw
24093          * @param {Roo.bootstrap.LocationPicker} this
24094          */
24095         OverlayViewOnAdd : true,
24096         /**
24097          * @event OverlayViewOnRemove
24098          * Fires when OverlayView Draw
24099          * @param {Roo.bootstrap.LocationPicker} this
24100          */
24101         OverlayViewOnRemove : true,
24102         /**
24103          * @event OverlayViewShow
24104          * Fires when OverlayView Draw
24105          * @param {Roo.bootstrap.LocationPicker} this
24106          * @param {Pixel} cpx
24107          */
24108         OverlayViewShow : true,
24109         /**
24110          * @event OverlayViewHide
24111          * Fires when OverlayView Draw
24112          * @param {Roo.bootstrap.LocationPicker} this
24113          */
24114         OverlayViewHide : true,
24115         /**
24116          * @event loadexception
24117          * Fires when load google lib failed.
24118          * @param {Roo.bootstrap.LocationPicker} this
24119          */
24120         loadexception : true
24121     });
24122         
24123 };
24124
24125 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24126     
24127     gMapContext: false,
24128     
24129     latitude: 0,
24130     longitude: 0,
24131     zoom: 15,
24132     mapTypeId: false,
24133     mapTypeControl: false,
24134     disableDoubleClickZoom: false,
24135     scrollwheel: true,
24136     streetViewControl: false,
24137     radius: 0,
24138     locationName: '',
24139     draggable: true,
24140     enableAutocomplete: false,
24141     enableReverseGeocode: true,
24142     markerTitle: '',
24143     
24144     getAutoCreate: function()
24145     {
24146
24147         var cfg = {
24148             tag: 'div',
24149             cls: 'roo-location-picker'
24150         };
24151         
24152         return cfg
24153     },
24154     
24155     initEvents: function(ct, position)
24156     {       
24157         if(!this.el.getWidth() || this.isApplied()){
24158             return;
24159         }
24160         
24161         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24162         
24163         this.initial();
24164     },
24165     
24166     initial: function()
24167     {
24168         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24169             this.fireEvent('loadexception', this);
24170             return;
24171         }
24172         
24173         if(!this.mapTypeId){
24174             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24175         }
24176         
24177         this.gMapContext = this.GMapContext();
24178         
24179         this.initOverlayView();
24180         
24181         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24182         
24183         var _this = this;
24184                 
24185         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24186             _this.setPosition(_this.gMapContext.marker.position);
24187         });
24188         
24189         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24190             _this.fireEvent('mapClick', this, event);
24191             
24192         });
24193
24194         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24195             _this.fireEvent('mapRightClick', this, event);
24196             
24197         });
24198         
24199         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24200             _this.fireEvent('markerClick', this, event);
24201             
24202         });
24203
24204         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24205             _this.fireEvent('markerRightClick', this, event);
24206             
24207         });
24208         
24209         this.setPosition(this.gMapContext.location);
24210         
24211         this.fireEvent('initial', this, this.gMapContext.location);
24212     },
24213     
24214     initOverlayView: function()
24215     {
24216         var _this = this;
24217         
24218         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24219             
24220             draw: function()
24221             {
24222                 _this.fireEvent('OverlayViewDraw', _this);
24223             },
24224             
24225             onAdd: function()
24226             {
24227                 _this.fireEvent('OverlayViewOnAdd', _this);
24228             },
24229             
24230             onRemove: function()
24231             {
24232                 _this.fireEvent('OverlayViewOnRemove', _this);
24233             },
24234             
24235             show: function(cpx)
24236             {
24237                 _this.fireEvent('OverlayViewShow', _this, cpx);
24238             },
24239             
24240             hide: function()
24241             {
24242                 _this.fireEvent('OverlayViewHide', _this);
24243             }
24244             
24245         });
24246     },
24247     
24248     fromLatLngToContainerPixel: function(event)
24249     {
24250         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24251     },
24252     
24253     isApplied: function() 
24254     {
24255         return this.getGmapContext() == false ? false : true;
24256     },
24257     
24258     getGmapContext: function() 
24259     {
24260         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24261     },
24262     
24263     GMapContext: function() 
24264     {
24265         var position = new google.maps.LatLng(this.latitude, this.longitude);
24266         
24267         var _map = new google.maps.Map(this.el.dom, {
24268             center: position,
24269             zoom: this.zoom,
24270             mapTypeId: this.mapTypeId,
24271             mapTypeControl: this.mapTypeControl,
24272             disableDoubleClickZoom: this.disableDoubleClickZoom,
24273             scrollwheel: this.scrollwheel,
24274             streetViewControl: this.streetViewControl,
24275             locationName: this.locationName,
24276             draggable: this.draggable,
24277             enableAutocomplete: this.enableAutocomplete,
24278             enableReverseGeocode: this.enableReverseGeocode
24279         });
24280         
24281         var _marker = new google.maps.Marker({
24282             position: position,
24283             map: _map,
24284             title: this.markerTitle,
24285             draggable: this.draggable
24286         });
24287         
24288         return {
24289             map: _map,
24290             marker: _marker,
24291             circle: null,
24292             location: position,
24293             radius: this.radius,
24294             locationName: this.locationName,
24295             addressComponents: {
24296                 formatted_address: null,
24297                 addressLine1: null,
24298                 addressLine2: null,
24299                 streetName: null,
24300                 streetNumber: null,
24301                 city: null,
24302                 district: null,
24303                 state: null,
24304                 stateOrProvince: null
24305             },
24306             settings: this,
24307             domContainer: this.el.dom,
24308             geodecoder: new google.maps.Geocoder()
24309         };
24310     },
24311     
24312     drawCircle: function(center, radius, options) 
24313     {
24314         if (this.gMapContext.circle != null) {
24315             this.gMapContext.circle.setMap(null);
24316         }
24317         if (radius > 0) {
24318             radius *= 1;
24319             options = Roo.apply({}, options, {
24320                 strokeColor: "#0000FF",
24321                 strokeOpacity: .35,
24322                 strokeWeight: 2,
24323                 fillColor: "#0000FF",
24324                 fillOpacity: .2
24325             });
24326             
24327             options.map = this.gMapContext.map;
24328             options.radius = radius;
24329             options.center = center;
24330             this.gMapContext.circle = new google.maps.Circle(options);
24331             return this.gMapContext.circle;
24332         }
24333         
24334         return null;
24335     },
24336     
24337     setPosition: function(location) 
24338     {
24339         this.gMapContext.location = location;
24340         this.gMapContext.marker.setPosition(location);
24341         this.gMapContext.map.panTo(location);
24342         this.drawCircle(location, this.gMapContext.radius, {});
24343         
24344         var _this = this;
24345         
24346         if (this.gMapContext.settings.enableReverseGeocode) {
24347             this.gMapContext.geodecoder.geocode({
24348                 latLng: this.gMapContext.location
24349             }, function(results, status) {
24350                 
24351                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24352                     _this.gMapContext.locationName = results[0].formatted_address;
24353                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24354                     
24355                     _this.fireEvent('positionchanged', this, location);
24356                 }
24357             });
24358             
24359             return;
24360         }
24361         
24362         this.fireEvent('positionchanged', this, location);
24363     },
24364     
24365     resize: function()
24366     {
24367         google.maps.event.trigger(this.gMapContext.map, "resize");
24368         
24369         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24370         
24371         this.fireEvent('resize', this);
24372     },
24373     
24374     setPositionByLatLng: function(latitude, longitude)
24375     {
24376         this.setPosition(new google.maps.LatLng(latitude, longitude));
24377     },
24378     
24379     getCurrentPosition: function() 
24380     {
24381         return {
24382             latitude: this.gMapContext.location.lat(),
24383             longitude: this.gMapContext.location.lng()
24384         };
24385     },
24386     
24387     getAddressName: function() 
24388     {
24389         return this.gMapContext.locationName;
24390     },
24391     
24392     getAddressComponents: function() 
24393     {
24394         return this.gMapContext.addressComponents;
24395     },
24396     
24397     address_component_from_google_geocode: function(address_components) 
24398     {
24399         var result = {};
24400         
24401         for (var i = 0; i < address_components.length; i++) {
24402             var component = address_components[i];
24403             if (component.types.indexOf("postal_code") >= 0) {
24404                 result.postalCode = component.short_name;
24405             } else if (component.types.indexOf("street_number") >= 0) {
24406                 result.streetNumber = component.short_name;
24407             } else if (component.types.indexOf("route") >= 0) {
24408                 result.streetName = component.short_name;
24409             } else if (component.types.indexOf("neighborhood") >= 0) {
24410                 result.city = component.short_name;
24411             } else if (component.types.indexOf("locality") >= 0) {
24412                 result.city = component.short_name;
24413             } else if (component.types.indexOf("sublocality") >= 0) {
24414                 result.district = component.short_name;
24415             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24416                 result.stateOrProvince = component.short_name;
24417             } else if (component.types.indexOf("country") >= 0) {
24418                 result.country = component.short_name;
24419             }
24420         }
24421         
24422         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24423         result.addressLine2 = "";
24424         return result;
24425     },
24426     
24427     setZoomLevel: function(zoom)
24428     {
24429         this.gMapContext.map.setZoom(zoom);
24430     },
24431     
24432     show: function()
24433     {
24434         if(!this.el){
24435             return;
24436         }
24437         
24438         this.el.show();
24439         
24440         this.resize();
24441         
24442         this.fireEvent('show', this);
24443     },
24444     
24445     hide: function()
24446     {
24447         if(!this.el){
24448             return;
24449         }
24450         
24451         this.el.hide();
24452         
24453         this.fireEvent('hide', this);
24454     }
24455     
24456 });
24457
24458 Roo.apply(Roo.bootstrap.LocationPicker, {
24459     
24460     OverlayView : function(map, options)
24461     {
24462         options = options || {};
24463         
24464         this.setMap(map);
24465     }
24466     
24467     
24468 });/*
24469  * - LGPL
24470  *
24471  * Alert
24472  * 
24473  */
24474
24475 /**
24476  * @class Roo.bootstrap.Alert
24477  * @extends Roo.bootstrap.Component
24478  * Bootstrap Alert class
24479  * @cfg {String} title The title of alert
24480  * @cfg {String} html The content of alert
24481  * @cfg {String} weight (  success | info | warning | danger )
24482  * @cfg {String} faicon font-awesomeicon
24483  * 
24484  * @constructor
24485  * Create a new alert
24486  * @param {Object} config The config object
24487  */
24488
24489
24490 Roo.bootstrap.Alert = function(config){
24491     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24492     
24493 };
24494
24495 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24496     
24497     title: '',
24498     html: '',
24499     weight: false,
24500     faicon: false,
24501     
24502     getAutoCreate : function()
24503     {
24504         
24505         var cfg = {
24506             tag : 'div',
24507             cls : 'alert',
24508             cn : [
24509                 {
24510                     tag : 'i',
24511                     cls : 'roo-alert-icon'
24512                     
24513                 },
24514                 {
24515                     tag : 'b',
24516                     cls : 'roo-alert-title',
24517                     html : this.title
24518                 },
24519                 {
24520                     tag : 'span',
24521                     cls : 'roo-alert-text',
24522                     html : this.html
24523                 }
24524             ]
24525         };
24526         
24527         if(this.faicon){
24528             cfg.cn[0].cls += ' fa ' + this.faicon;
24529         }
24530         
24531         if(this.weight){
24532             cfg.cls += ' alert-' + this.weight;
24533         }
24534         
24535         return cfg;
24536     },
24537     
24538     initEvents: function() 
24539     {
24540         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24541     },
24542     
24543     setTitle : function(str)
24544     {
24545         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24546     },
24547     
24548     setText : function(str)
24549     {
24550         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24551     },
24552     
24553     setWeight : function(weight)
24554     {
24555         if(this.weight){
24556             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24557         }
24558         
24559         this.weight = weight;
24560         
24561         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24562     },
24563     
24564     setIcon : function(icon)
24565     {
24566         if(this.faicon){
24567             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24568         }
24569         
24570         this.faicon = icon;
24571         
24572         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24573     },
24574     
24575     hide: function() 
24576     {
24577         this.el.hide();   
24578     },
24579     
24580     show: function() 
24581     {  
24582         this.el.show();   
24583     }
24584     
24585 });
24586
24587  
24588 /*
24589 * Licence: LGPL
24590 */
24591
24592 /**
24593  * @class Roo.bootstrap.UploadCropbox
24594  * @extends Roo.bootstrap.Component
24595  * Bootstrap UploadCropbox class
24596  * @cfg {String} emptyText show when image has been loaded
24597  * @cfg {String} rotateNotify show when image too small to rotate
24598  * @cfg {Number} errorTimeout default 3000
24599  * @cfg {Number} minWidth default 300
24600  * @cfg {Number} minHeight default 300
24601  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24602  * @cfg {Boolean} isDocument (true|false) default false
24603  * @cfg {String} url action url
24604  * @cfg {String} paramName default 'imageUpload'
24605  * @cfg {String} method default POST
24606  * @cfg {Boolean} loadMask (true|false) default true
24607  * @cfg {Boolean} loadingText default 'Loading...'
24608  * 
24609  * @constructor
24610  * Create a new UploadCropbox
24611  * @param {Object} config The config object
24612  */
24613
24614 Roo.bootstrap.UploadCropbox = function(config){
24615     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24616     
24617     this.addEvents({
24618         /**
24619          * @event beforeselectfile
24620          * Fire before select file
24621          * @param {Roo.bootstrap.UploadCropbox} this
24622          */
24623         "beforeselectfile" : true,
24624         /**
24625          * @event initial
24626          * Fire after initEvent
24627          * @param {Roo.bootstrap.UploadCropbox} this
24628          */
24629         "initial" : true,
24630         /**
24631          * @event crop
24632          * Fire after initEvent
24633          * @param {Roo.bootstrap.UploadCropbox} this
24634          * @param {String} data
24635          */
24636         "crop" : true,
24637         /**
24638          * @event prepare
24639          * Fire when preparing the file data
24640          * @param {Roo.bootstrap.UploadCropbox} this
24641          * @param {Object} file
24642          */
24643         "prepare" : true,
24644         /**
24645          * @event exception
24646          * Fire when get exception
24647          * @param {Roo.bootstrap.UploadCropbox} this
24648          * @param {XMLHttpRequest} xhr
24649          */
24650         "exception" : true,
24651         /**
24652          * @event beforeloadcanvas
24653          * Fire before load the canvas
24654          * @param {Roo.bootstrap.UploadCropbox} this
24655          * @param {String} src
24656          */
24657         "beforeloadcanvas" : true,
24658         /**
24659          * @event trash
24660          * Fire when trash image
24661          * @param {Roo.bootstrap.UploadCropbox} this
24662          */
24663         "trash" : true,
24664         /**
24665          * @event download
24666          * Fire when download the image
24667          * @param {Roo.bootstrap.UploadCropbox} this
24668          */
24669         "download" : true,
24670         /**
24671          * @event footerbuttonclick
24672          * Fire when footerbuttonclick
24673          * @param {Roo.bootstrap.UploadCropbox} this
24674          * @param {String} type
24675          */
24676         "footerbuttonclick" : true,
24677         /**
24678          * @event resize
24679          * Fire when resize
24680          * @param {Roo.bootstrap.UploadCropbox} this
24681          */
24682         "resize" : true,
24683         /**
24684          * @event rotate
24685          * Fire when rotate the image
24686          * @param {Roo.bootstrap.UploadCropbox} this
24687          * @param {String} pos
24688          */
24689         "rotate" : true,
24690         /**
24691          * @event inspect
24692          * Fire when inspect the file
24693          * @param {Roo.bootstrap.UploadCropbox} this
24694          * @param {Object} file
24695          */
24696         "inspect" : true,
24697         /**
24698          * @event upload
24699          * Fire when xhr upload the file
24700          * @param {Roo.bootstrap.UploadCropbox} this
24701          * @param {Object} data
24702          */
24703         "upload" : true,
24704         /**
24705          * @event arrange
24706          * Fire when arrange the file data
24707          * @param {Roo.bootstrap.UploadCropbox} this
24708          * @param {Object} formData
24709          */
24710         "arrange" : true
24711     });
24712     
24713     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24714 };
24715
24716 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24717     
24718     emptyText : 'Click to upload image',
24719     rotateNotify : 'Image is too small to rotate',
24720     errorTimeout : 3000,
24721     scale : 0,
24722     baseScale : 1,
24723     rotate : 0,
24724     dragable : false,
24725     pinching : false,
24726     mouseX : 0,
24727     mouseY : 0,
24728     cropData : false,
24729     minWidth : 300,
24730     minHeight : 300,
24731     file : false,
24732     exif : {},
24733     baseRotate : 1,
24734     cropType : 'image/jpeg',
24735     buttons : false,
24736     canvasLoaded : false,
24737     isDocument : false,
24738     method : 'POST',
24739     paramName : 'imageUpload',
24740     loadMask : true,
24741     loadingText : 'Loading...',
24742     maskEl : false,
24743     
24744     getAutoCreate : function()
24745     {
24746         var cfg = {
24747             tag : 'div',
24748             cls : 'roo-upload-cropbox',
24749             cn : [
24750                 {
24751                     tag : 'input',
24752                     cls : 'roo-upload-cropbox-selector',
24753                     type : 'file'
24754                 },
24755                 {
24756                     tag : 'div',
24757                     cls : 'roo-upload-cropbox-body',
24758                     style : 'cursor:pointer',
24759                     cn : [
24760                         {
24761                             tag : 'div',
24762                             cls : 'roo-upload-cropbox-preview'
24763                         },
24764                         {
24765                             tag : 'div',
24766                             cls : 'roo-upload-cropbox-thumb'
24767                         },
24768                         {
24769                             tag : 'div',
24770                             cls : 'roo-upload-cropbox-empty-notify',
24771                             html : this.emptyText
24772                         },
24773                         {
24774                             tag : 'div',
24775                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24776                             html : this.rotateNotify
24777                         }
24778                     ]
24779                 },
24780                 {
24781                     tag : 'div',
24782                     cls : 'roo-upload-cropbox-footer',
24783                     cn : {
24784                         tag : 'div',
24785                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24786                         cn : []
24787                     }
24788                 }
24789             ]
24790         };
24791         
24792         return cfg;
24793     },
24794     
24795     onRender : function(ct, position)
24796     {
24797         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24798         
24799         if (this.buttons.length) {
24800             
24801             Roo.each(this.buttons, function(bb) {
24802                 
24803                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24804                 
24805                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24806                 
24807             }, this);
24808         }
24809         
24810         if(this.loadMask){
24811             this.maskEl = this.el;
24812         }
24813     },
24814     
24815     initEvents : function()
24816     {
24817         this.urlAPI = (window.createObjectURL && window) || 
24818                                 (window.URL && URL.revokeObjectURL && URL) || 
24819                                 (window.webkitURL && webkitURL);
24820                         
24821         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24822         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24823         
24824         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24825         this.selectorEl.hide();
24826         
24827         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24828         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24829         
24830         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24831         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24832         this.thumbEl.hide();
24833         
24834         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24835         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24836         
24837         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24838         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24839         this.errorEl.hide();
24840         
24841         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24842         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24843         this.footerEl.hide();
24844         
24845         this.setThumbBoxSize();
24846         
24847         this.bind();
24848         
24849         this.resize();
24850         
24851         this.fireEvent('initial', this);
24852     },
24853
24854     bind : function()
24855     {
24856         var _this = this;
24857         
24858         window.addEventListener("resize", function() { _this.resize(); } );
24859         
24860         this.bodyEl.on('click', this.beforeSelectFile, this);
24861         
24862         if(Roo.isTouch){
24863             this.bodyEl.on('touchstart', this.onTouchStart, this);
24864             this.bodyEl.on('touchmove', this.onTouchMove, this);
24865             this.bodyEl.on('touchend', this.onTouchEnd, this);
24866         }
24867         
24868         if(!Roo.isTouch){
24869             this.bodyEl.on('mousedown', this.onMouseDown, this);
24870             this.bodyEl.on('mousemove', this.onMouseMove, this);
24871             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24872             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24873             Roo.get(document).on('mouseup', this.onMouseUp, this);
24874         }
24875         
24876         this.selectorEl.on('change', this.onFileSelected, this);
24877     },
24878     
24879     reset : function()
24880     {    
24881         this.scale = 0;
24882         this.baseScale = 1;
24883         this.rotate = 0;
24884         this.baseRotate = 1;
24885         this.dragable = false;
24886         this.pinching = false;
24887         this.mouseX = 0;
24888         this.mouseY = 0;
24889         this.cropData = false;
24890         this.notifyEl.dom.innerHTML = this.emptyText;
24891         
24892         this.selectorEl.dom.value = '';
24893         
24894     },
24895     
24896     resize : function()
24897     {
24898         if(this.fireEvent('resize', this) != false){
24899             this.setThumbBoxPosition();
24900             this.setCanvasPosition();
24901         }
24902     },
24903     
24904     onFooterButtonClick : function(e, el, o, type)
24905     {
24906         switch (type) {
24907             case 'rotate-left' :
24908                 this.onRotateLeft(e);
24909                 break;
24910             case 'rotate-right' :
24911                 this.onRotateRight(e);
24912                 break;
24913             case 'picture' :
24914                 this.beforeSelectFile(e);
24915                 break;
24916             case 'trash' :
24917                 this.trash(e);
24918                 break;
24919             case 'crop' :
24920                 this.crop(e);
24921                 break;
24922             case 'download' :
24923                 this.download(e);
24924                 break;
24925             default :
24926                 break;
24927         }
24928         
24929         this.fireEvent('footerbuttonclick', this, type);
24930     },
24931     
24932     beforeSelectFile : function(e)
24933     {
24934         e.preventDefault();
24935         
24936         if(this.fireEvent('beforeselectfile', this) != false){
24937             this.selectorEl.dom.click();
24938         }
24939     },
24940     
24941     onFileSelected : function(e)
24942     {
24943         e.preventDefault();
24944         
24945         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24946             return;
24947         }
24948         
24949         var file = this.selectorEl.dom.files[0];
24950         
24951         if(this.fireEvent('inspect', this, file) != false){
24952             this.prepare(file);
24953         }
24954         
24955     },
24956     
24957     trash : function(e)
24958     {
24959         this.fireEvent('trash', this);
24960     },
24961     
24962     download : function(e)
24963     {
24964         this.fireEvent('download', this);
24965     },
24966     
24967     loadCanvas : function(src)
24968     {   
24969         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24970             
24971             this.reset();
24972             
24973             this.imageEl = document.createElement('img');
24974             
24975             var _this = this;
24976             
24977             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24978             
24979             this.imageEl.src = src;
24980         }
24981     },
24982     
24983     onLoadCanvas : function()
24984     {   
24985         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24986         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24987         
24988         this.bodyEl.un('click', this.beforeSelectFile, this);
24989         
24990         this.notifyEl.hide();
24991         this.thumbEl.show();
24992         this.footerEl.show();
24993         
24994         this.baseRotateLevel();
24995         
24996         if(this.isDocument){
24997             this.setThumbBoxSize();
24998         }
24999         
25000         this.setThumbBoxPosition();
25001         
25002         this.baseScaleLevel();
25003         
25004         this.draw();
25005         
25006         this.resize();
25007         
25008         this.canvasLoaded = true;
25009         
25010         if(this.loadMask){
25011             this.maskEl.unmask();
25012         }
25013         
25014     },
25015     
25016     setCanvasPosition : function()
25017     {   
25018         if(!this.canvasEl){
25019             return;
25020         }
25021         
25022         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25023         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25024         
25025         this.previewEl.setLeft(pw);
25026         this.previewEl.setTop(ph);
25027         
25028     },
25029     
25030     onMouseDown : function(e)
25031     {   
25032         e.stopEvent();
25033         
25034         this.dragable = true;
25035         this.pinching = false;
25036         
25037         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25038             this.dragable = false;
25039             return;
25040         }
25041         
25042         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25043         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25044         
25045     },
25046     
25047     onMouseMove : function(e)
25048     {   
25049         e.stopEvent();
25050         
25051         if(!this.canvasLoaded){
25052             return;
25053         }
25054         
25055         if (!this.dragable){
25056             return;
25057         }
25058         
25059         var minX = Math.ceil(this.thumbEl.getLeft(true));
25060         var minY = Math.ceil(this.thumbEl.getTop(true));
25061         
25062         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25063         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25064         
25065         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25066         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25067         
25068         x = x - this.mouseX;
25069         y = y - this.mouseY;
25070         
25071         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25072         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25073         
25074         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25075         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25076         
25077         this.previewEl.setLeft(bgX);
25078         this.previewEl.setTop(bgY);
25079         
25080         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25081         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25082     },
25083     
25084     onMouseUp : function(e)
25085     {   
25086         e.stopEvent();
25087         
25088         this.dragable = false;
25089     },
25090     
25091     onMouseWheel : function(e)
25092     {   
25093         e.stopEvent();
25094         
25095         this.startScale = this.scale;
25096         
25097         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25098         
25099         if(!this.zoomable()){
25100             this.scale = this.startScale;
25101             return;
25102         }
25103         
25104         this.draw();
25105         
25106         return;
25107     },
25108     
25109     zoomable : function()
25110     {
25111         var minScale = this.thumbEl.getWidth() / this.minWidth;
25112         
25113         if(this.minWidth < this.minHeight){
25114             minScale = this.thumbEl.getHeight() / this.minHeight;
25115         }
25116         
25117         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25118         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25119         
25120         if(
25121                 this.isDocument &&
25122                 (this.rotate == 0 || this.rotate == 180) && 
25123                 (
25124                     width > this.imageEl.OriginWidth || 
25125                     height > this.imageEl.OriginHeight ||
25126                     (width < this.minWidth && height < this.minHeight)
25127                 )
25128         ){
25129             return false;
25130         }
25131         
25132         if(
25133                 this.isDocument &&
25134                 (this.rotate == 90 || this.rotate == 270) && 
25135                 (
25136                     width > this.imageEl.OriginWidth || 
25137                     height > this.imageEl.OriginHeight ||
25138                     (width < this.minHeight && height < this.minWidth)
25139                 )
25140         ){
25141             return false;
25142         }
25143         
25144         if(
25145                 !this.isDocument &&
25146                 (this.rotate == 0 || this.rotate == 180) && 
25147                 (
25148                     width < this.minWidth || 
25149                     width > this.imageEl.OriginWidth || 
25150                     height < this.minHeight || 
25151                     height > this.imageEl.OriginHeight
25152                 )
25153         ){
25154             return false;
25155         }
25156         
25157         if(
25158                 !this.isDocument &&
25159                 (this.rotate == 90 || this.rotate == 270) && 
25160                 (
25161                     width < this.minHeight || 
25162                     width > this.imageEl.OriginWidth || 
25163                     height < this.minWidth || 
25164                     height > this.imageEl.OriginHeight
25165                 )
25166         ){
25167             return false;
25168         }
25169         
25170         return true;
25171         
25172     },
25173     
25174     onRotateLeft : function(e)
25175     {   
25176         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25177             
25178             var minScale = this.thumbEl.getWidth() / this.minWidth;
25179             
25180             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25181             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25182             
25183             this.startScale = this.scale;
25184             
25185             while (this.getScaleLevel() < minScale){
25186             
25187                 this.scale = this.scale + 1;
25188                 
25189                 if(!this.zoomable()){
25190                     break;
25191                 }
25192                 
25193                 if(
25194                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25195                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25196                 ){
25197                     continue;
25198                 }
25199                 
25200                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25201
25202                 this.draw();
25203                 
25204                 return;
25205             }
25206             
25207             this.scale = this.startScale;
25208             
25209             this.onRotateFail();
25210             
25211             return false;
25212         }
25213         
25214         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25215
25216         if(this.isDocument){
25217             this.setThumbBoxSize();
25218             this.setThumbBoxPosition();
25219             this.setCanvasPosition();
25220         }
25221         
25222         this.draw();
25223         
25224         this.fireEvent('rotate', this, 'left');
25225         
25226     },
25227     
25228     onRotateRight : function(e)
25229     {
25230         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25231             
25232             var minScale = this.thumbEl.getWidth() / this.minWidth;
25233         
25234             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25235             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25236             
25237             this.startScale = this.scale;
25238             
25239             while (this.getScaleLevel() < minScale){
25240             
25241                 this.scale = this.scale + 1;
25242                 
25243                 if(!this.zoomable()){
25244                     break;
25245                 }
25246                 
25247                 if(
25248                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25249                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25250                 ){
25251                     continue;
25252                 }
25253                 
25254                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25255
25256                 this.draw();
25257                 
25258                 return;
25259             }
25260             
25261             this.scale = this.startScale;
25262             
25263             this.onRotateFail();
25264             
25265             return false;
25266         }
25267         
25268         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25269
25270         if(this.isDocument){
25271             this.setThumbBoxSize();
25272             this.setThumbBoxPosition();
25273             this.setCanvasPosition();
25274         }
25275         
25276         this.draw();
25277         
25278         this.fireEvent('rotate', this, 'right');
25279     },
25280     
25281     onRotateFail : function()
25282     {
25283         this.errorEl.show(true);
25284         
25285         var _this = this;
25286         
25287         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25288     },
25289     
25290     draw : function()
25291     {
25292         this.previewEl.dom.innerHTML = '';
25293         
25294         var canvasEl = document.createElement("canvas");
25295         
25296         var contextEl = canvasEl.getContext("2d");
25297         
25298         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25299         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25300         var center = this.imageEl.OriginWidth / 2;
25301         
25302         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25303             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25304             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25305             center = this.imageEl.OriginHeight / 2;
25306         }
25307         
25308         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25309         
25310         contextEl.translate(center, center);
25311         contextEl.rotate(this.rotate * Math.PI / 180);
25312
25313         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25314         
25315         this.canvasEl = document.createElement("canvas");
25316         
25317         this.contextEl = this.canvasEl.getContext("2d");
25318         
25319         switch (this.rotate) {
25320             case 0 :
25321                 
25322                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25323                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25324                 
25325                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25326                 
25327                 break;
25328             case 90 : 
25329                 
25330                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25331                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25332                 
25333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25334                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25335                     break;
25336                 }
25337                 
25338                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25339                 
25340                 break;
25341             case 180 :
25342                 
25343                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25344                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25345                 
25346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25347                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25348                     break;
25349                 }
25350                 
25351                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25352                 
25353                 break;
25354             case 270 :
25355                 
25356                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25357                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25358         
25359                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25360                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25361                     break;
25362                 }
25363                 
25364                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25365                 
25366                 break;
25367             default : 
25368                 break;
25369         }
25370         
25371         this.previewEl.appendChild(this.canvasEl);
25372         
25373         this.setCanvasPosition();
25374     },
25375     
25376     crop : function()
25377     {
25378         if(!this.canvasLoaded){
25379             return;
25380         }
25381         
25382         var imageCanvas = document.createElement("canvas");
25383         
25384         var imageContext = imageCanvas.getContext("2d");
25385         
25386         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25387         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25388         
25389         var center = imageCanvas.width / 2;
25390         
25391         imageContext.translate(center, center);
25392         
25393         imageContext.rotate(this.rotate * Math.PI / 180);
25394         
25395         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25396         
25397         var canvas = document.createElement("canvas");
25398         
25399         var context = canvas.getContext("2d");
25400                 
25401         canvas.width = this.minWidth;
25402         canvas.height = this.minHeight;
25403
25404         switch (this.rotate) {
25405             case 0 :
25406                 
25407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25409                 
25410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25412                 
25413                 var targetWidth = this.minWidth - 2 * x;
25414                 var targetHeight = this.minHeight - 2 * y;
25415                 
25416                 var scale = 1;
25417                 
25418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25419                     scale = targetWidth / width;
25420                 }
25421                 
25422                 if(x > 0 && y == 0){
25423                     scale = targetHeight / height;
25424                 }
25425                 
25426                 if(x > 0 && y > 0){
25427                     scale = targetWidth / width;
25428                     
25429                     if(width < height){
25430                         scale = targetHeight / height;
25431                     }
25432                 }
25433                 
25434                 context.scale(scale, scale);
25435                 
25436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25438
25439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25441
25442                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25443                 
25444                 break;
25445             case 90 : 
25446                 
25447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25449                 
25450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25452                 
25453                 var targetWidth = this.minWidth - 2 * x;
25454                 var targetHeight = this.minHeight - 2 * y;
25455                 
25456                 var scale = 1;
25457                 
25458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25459                     scale = targetWidth / width;
25460                 }
25461                 
25462                 if(x > 0 && y == 0){
25463                     scale = targetHeight / height;
25464                 }
25465                 
25466                 if(x > 0 && y > 0){
25467                     scale = targetWidth / width;
25468                     
25469                     if(width < height){
25470                         scale = targetHeight / height;
25471                     }
25472                 }
25473                 
25474                 context.scale(scale, scale);
25475                 
25476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25478
25479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25481                 
25482                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25483                 
25484                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25485                 
25486                 break;
25487             case 180 :
25488                 
25489                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25490                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25491                 
25492                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25493                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25494                 
25495                 var targetWidth = this.minWidth - 2 * x;
25496                 var targetHeight = this.minHeight - 2 * y;
25497                 
25498                 var scale = 1;
25499                 
25500                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25501                     scale = targetWidth / width;
25502                 }
25503                 
25504                 if(x > 0 && y == 0){
25505                     scale = targetHeight / height;
25506                 }
25507                 
25508                 if(x > 0 && y > 0){
25509                     scale = targetWidth / width;
25510                     
25511                     if(width < height){
25512                         scale = targetHeight / height;
25513                     }
25514                 }
25515                 
25516                 context.scale(scale, scale);
25517                 
25518                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25519                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25520
25521                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25522                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25523
25524                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25525                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25526                 
25527                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25528                 
25529                 break;
25530             case 270 :
25531                 
25532                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25533                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25534                 
25535                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25536                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25537                 
25538                 var targetWidth = this.minWidth - 2 * x;
25539                 var targetHeight = this.minHeight - 2 * y;
25540                 
25541                 var scale = 1;
25542                 
25543                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25544                     scale = targetWidth / width;
25545                 }
25546                 
25547                 if(x > 0 && y == 0){
25548                     scale = targetHeight / height;
25549                 }
25550                 
25551                 if(x > 0 && y > 0){
25552                     scale = targetWidth / width;
25553                     
25554                     if(width < height){
25555                         scale = targetHeight / height;
25556                     }
25557                 }
25558                 
25559                 context.scale(scale, scale);
25560                 
25561                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25562                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25563
25564                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25565                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25566                 
25567                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25568                 
25569                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25570                 
25571                 break;
25572             default : 
25573                 break;
25574         }
25575         
25576         this.cropData = canvas.toDataURL(this.cropType);
25577         
25578         if(this.fireEvent('crop', this, this.cropData) !== false){
25579             this.process(this.file, this.cropData);
25580         }
25581         
25582         return;
25583         
25584     },
25585     
25586     setThumbBoxSize : function()
25587     {
25588         var width, height;
25589         
25590         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25591             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25592             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25593             
25594             this.minWidth = width;
25595             this.minHeight = height;
25596             
25597             if(this.rotate == 90 || this.rotate == 270){
25598                 this.minWidth = height;
25599                 this.minHeight = width;
25600             }
25601         }
25602         
25603         height = 300;
25604         width = Math.ceil(this.minWidth * height / this.minHeight);
25605         
25606         if(this.minWidth > this.minHeight){
25607             width = 300;
25608             height = Math.ceil(this.minHeight * width / this.minWidth);
25609         }
25610         
25611         this.thumbEl.setStyle({
25612             width : width + 'px',
25613             height : height + 'px'
25614         });
25615
25616         return;
25617             
25618     },
25619     
25620     setThumbBoxPosition : function()
25621     {
25622         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25623         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25624         
25625         this.thumbEl.setLeft(x);
25626         this.thumbEl.setTop(y);
25627         
25628     },
25629     
25630     baseRotateLevel : function()
25631     {
25632         this.baseRotate = 1;
25633         
25634         if(
25635                 typeof(this.exif) != 'undefined' &&
25636                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25637                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25638         ){
25639             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25640         }
25641         
25642         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25643         
25644     },
25645     
25646     baseScaleLevel : function()
25647     {
25648         var width, height;
25649         
25650         if(this.isDocument){
25651             
25652             if(this.baseRotate == 6 || this.baseRotate == 8){
25653             
25654                 height = this.thumbEl.getHeight();
25655                 this.baseScale = height / this.imageEl.OriginWidth;
25656
25657                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25658                     width = this.thumbEl.getWidth();
25659                     this.baseScale = width / this.imageEl.OriginHeight;
25660                 }
25661
25662                 return;
25663             }
25664
25665             height = this.thumbEl.getHeight();
25666             this.baseScale = height / this.imageEl.OriginHeight;
25667
25668             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25669                 width = this.thumbEl.getWidth();
25670                 this.baseScale = width / this.imageEl.OriginWidth;
25671             }
25672
25673             return;
25674         }
25675         
25676         if(this.baseRotate == 6 || this.baseRotate == 8){
25677             
25678             width = this.thumbEl.getHeight();
25679             this.baseScale = width / this.imageEl.OriginHeight;
25680             
25681             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25682                 height = this.thumbEl.getWidth();
25683                 this.baseScale = height / this.imageEl.OriginHeight;
25684             }
25685             
25686             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25687                 height = this.thumbEl.getWidth();
25688                 this.baseScale = height / this.imageEl.OriginHeight;
25689                 
25690                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25691                     width = this.thumbEl.getHeight();
25692                     this.baseScale = width / this.imageEl.OriginWidth;
25693                 }
25694             }
25695             
25696             return;
25697         }
25698         
25699         width = this.thumbEl.getWidth();
25700         this.baseScale = width / this.imageEl.OriginWidth;
25701         
25702         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25703             height = this.thumbEl.getHeight();
25704             this.baseScale = height / this.imageEl.OriginHeight;
25705         }
25706         
25707         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25708             
25709             height = this.thumbEl.getHeight();
25710             this.baseScale = height / this.imageEl.OriginHeight;
25711             
25712             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25713                 width = this.thumbEl.getWidth();
25714                 this.baseScale = width / this.imageEl.OriginWidth;
25715             }
25716             
25717         }
25718         
25719         return;
25720     },
25721     
25722     getScaleLevel : function()
25723     {
25724         return this.baseScale * Math.pow(1.1, this.scale);
25725     },
25726     
25727     onTouchStart : function(e)
25728     {
25729         if(!this.canvasLoaded){
25730             this.beforeSelectFile(e);
25731             return;
25732         }
25733         
25734         var touches = e.browserEvent.touches;
25735         
25736         if(!touches){
25737             return;
25738         }
25739         
25740         if(touches.length == 1){
25741             this.onMouseDown(e);
25742             return;
25743         }
25744         
25745         if(touches.length != 2){
25746             return;
25747         }
25748         
25749         var coords = [];
25750         
25751         for(var i = 0, finger; finger = touches[i]; i++){
25752             coords.push(finger.pageX, finger.pageY);
25753         }
25754         
25755         var x = Math.pow(coords[0] - coords[2], 2);
25756         var y = Math.pow(coords[1] - coords[3], 2);
25757         
25758         this.startDistance = Math.sqrt(x + y);
25759         
25760         this.startScale = this.scale;
25761         
25762         this.pinching = true;
25763         this.dragable = false;
25764         
25765     },
25766     
25767     onTouchMove : function(e)
25768     {
25769         if(!this.pinching && !this.dragable){
25770             return;
25771         }
25772         
25773         var touches = e.browserEvent.touches;
25774         
25775         if(!touches){
25776             return;
25777         }
25778         
25779         if(this.dragable){
25780             this.onMouseMove(e);
25781             return;
25782         }
25783         
25784         var coords = [];
25785         
25786         for(var i = 0, finger; finger = touches[i]; i++){
25787             coords.push(finger.pageX, finger.pageY);
25788         }
25789         
25790         var x = Math.pow(coords[0] - coords[2], 2);
25791         var y = Math.pow(coords[1] - coords[3], 2);
25792         
25793         this.endDistance = Math.sqrt(x + y);
25794         
25795         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25796         
25797         if(!this.zoomable()){
25798             this.scale = this.startScale;
25799             return;
25800         }
25801         
25802         this.draw();
25803         
25804     },
25805     
25806     onTouchEnd : function(e)
25807     {
25808         this.pinching = false;
25809         this.dragable = false;
25810         
25811     },
25812     
25813     process : function(file, crop)
25814     {
25815         if(this.loadMask){
25816             this.maskEl.mask(this.loadingText);
25817         }
25818         
25819         this.xhr = new XMLHttpRequest();
25820         
25821         file.xhr = this.xhr;
25822
25823         this.xhr.open(this.method, this.url, true);
25824         
25825         var headers = {
25826             "Accept": "application/json",
25827             "Cache-Control": "no-cache",
25828             "X-Requested-With": "XMLHttpRequest"
25829         };
25830         
25831         for (var headerName in headers) {
25832             var headerValue = headers[headerName];
25833             if (headerValue) {
25834                 this.xhr.setRequestHeader(headerName, headerValue);
25835             }
25836         }
25837         
25838         var _this = this;
25839         
25840         this.xhr.onload = function()
25841         {
25842             _this.xhrOnLoad(_this.xhr);
25843         }
25844         
25845         this.xhr.onerror = function()
25846         {
25847             _this.xhrOnError(_this.xhr);
25848         }
25849         
25850         var formData = new FormData();
25851
25852         formData.append('returnHTML', 'NO');
25853         
25854         if(crop){
25855             formData.append('crop', crop);
25856         }
25857         
25858         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25859             formData.append(this.paramName, file, file.name);
25860         }
25861         
25862         if(typeof(file.filename) != 'undefined'){
25863             formData.append('filename', file.filename);
25864         }
25865         
25866         if(typeof(file.mimetype) != 'undefined'){
25867             formData.append('mimetype', file.mimetype);
25868         }
25869         
25870         if(this.fireEvent('arrange', this, formData) != false){
25871             this.xhr.send(formData);
25872         };
25873     },
25874     
25875     xhrOnLoad : function(xhr)
25876     {
25877         if(this.loadMask){
25878             this.maskEl.unmask();
25879         }
25880         
25881         if (xhr.readyState !== 4) {
25882             this.fireEvent('exception', this, xhr);
25883             return;
25884         }
25885
25886         var response = Roo.decode(xhr.responseText);
25887         
25888         if(!response.success){
25889             this.fireEvent('exception', this, xhr);
25890             return;
25891         }
25892         
25893         var response = Roo.decode(xhr.responseText);
25894         
25895         this.fireEvent('upload', this, response);
25896         
25897     },
25898     
25899     xhrOnError : function()
25900     {
25901         if(this.loadMask){
25902             this.maskEl.unmask();
25903         }
25904         
25905         Roo.log('xhr on error');
25906         
25907         var response = Roo.decode(xhr.responseText);
25908           
25909         Roo.log(response);
25910         
25911     },
25912     
25913     prepare : function(file)
25914     {   
25915         if(this.loadMask){
25916             this.maskEl.mask(this.loadingText);
25917         }
25918         
25919         this.file = false;
25920         this.exif = {};
25921         
25922         if(typeof(file) === 'string'){
25923             this.loadCanvas(file);
25924             return;
25925         }
25926         
25927         if(!file || !this.urlAPI){
25928             return;
25929         }
25930         
25931         this.file = file;
25932         this.cropType = file.type;
25933         
25934         var _this = this;
25935         
25936         if(this.fireEvent('prepare', this, this.file) != false){
25937             
25938             var reader = new FileReader();
25939             
25940             reader.onload = function (e) {
25941                 if (e.target.error) {
25942                     Roo.log(e.target.error);
25943                     return;
25944                 }
25945                 
25946                 var buffer = e.target.result,
25947                     dataView = new DataView(buffer),
25948                     offset = 2,
25949                     maxOffset = dataView.byteLength - 4,
25950                     markerBytes,
25951                     markerLength;
25952                 
25953                 if (dataView.getUint16(0) === 0xffd8) {
25954                     while (offset < maxOffset) {
25955                         markerBytes = dataView.getUint16(offset);
25956                         
25957                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25958                             markerLength = dataView.getUint16(offset + 2) + 2;
25959                             if (offset + markerLength > dataView.byteLength) {
25960                                 Roo.log('Invalid meta data: Invalid segment size.');
25961                                 break;
25962                             }
25963                             
25964                             if(markerBytes == 0xffe1){
25965                                 _this.parseExifData(
25966                                     dataView,
25967                                     offset,
25968                                     markerLength
25969                                 );
25970                             }
25971                             
25972                             offset += markerLength;
25973                             
25974                             continue;
25975                         }
25976                         
25977                         break;
25978                     }
25979                     
25980                 }
25981                 
25982                 var url = _this.urlAPI.createObjectURL(_this.file);
25983                 
25984                 _this.loadCanvas(url);
25985                 
25986                 return;
25987             }
25988             
25989             reader.readAsArrayBuffer(this.file);
25990             
25991         }
25992         
25993     },
25994     
25995     parseExifData : function(dataView, offset, length)
25996     {
25997         var tiffOffset = offset + 10,
25998             littleEndian,
25999             dirOffset;
26000     
26001         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26002             // No Exif data, might be XMP data instead
26003             return;
26004         }
26005         
26006         // Check for the ASCII code for "Exif" (0x45786966):
26007         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26008             // No Exif data, might be XMP data instead
26009             return;
26010         }
26011         if (tiffOffset + 8 > dataView.byteLength) {
26012             Roo.log('Invalid Exif data: Invalid segment size.');
26013             return;
26014         }
26015         // Check for the two null bytes:
26016         if (dataView.getUint16(offset + 8) !== 0x0000) {
26017             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26018             return;
26019         }
26020         // Check the byte alignment:
26021         switch (dataView.getUint16(tiffOffset)) {
26022         case 0x4949:
26023             littleEndian = true;
26024             break;
26025         case 0x4D4D:
26026             littleEndian = false;
26027             break;
26028         default:
26029             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26030             return;
26031         }
26032         // Check for the TIFF tag marker (0x002A):
26033         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26034             Roo.log('Invalid Exif data: Missing TIFF marker.');
26035             return;
26036         }
26037         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26038         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26039         
26040         this.parseExifTags(
26041             dataView,
26042             tiffOffset,
26043             tiffOffset + dirOffset,
26044             littleEndian
26045         );
26046     },
26047     
26048     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26049     {
26050         var tagsNumber,
26051             dirEndOffset,
26052             i;
26053         if (dirOffset + 6 > dataView.byteLength) {
26054             Roo.log('Invalid Exif data: Invalid directory offset.');
26055             return;
26056         }
26057         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26058         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26059         if (dirEndOffset + 4 > dataView.byteLength) {
26060             Roo.log('Invalid Exif data: Invalid directory size.');
26061             return;
26062         }
26063         for (i = 0; i < tagsNumber; i += 1) {
26064             this.parseExifTag(
26065                 dataView,
26066                 tiffOffset,
26067                 dirOffset + 2 + 12 * i, // tag offset
26068                 littleEndian
26069             );
26070         }
26071         // Return the offset to the next directory:
26072         return dataView.getUint32(dirEndOffset, littleEndian);
26073     },
26074     
26075     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26076     {
26077         var tag = dataView.getUint16(offset, littleEndian);
26078         
26079         this.exif[tag] = this.getExifValue(
26080             dataView,
26081             tiffOffset,
26082             offset,
26083             dataView.getUint16(offset + 2, littleEndian), // tag type
26084             dataView.getUint32(offset + 4, littleEndian), // tag length
26085             littleEndian
26086         );
26087     },
26088     
26089     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26090     {
26091         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26092             tagSize,
26093             dataOffset,
26094             values,
26095             i,
26096             str,
26097             c;
26098     
26099         if (!tagType) {
26100             Roo.log('Invalid Exif data: Invalid tag type.');
26101             return;
26102         }
26103         
26104         tagSize = tagType.size * length;
26105         // Determine if the value is contained in the dataOffset bytes,
26106         // or if the value at the dataOffset is a pointer to the actual data:
26107         dataOffset = tagSize > 4 ?
26108                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26109         if (dataOffset + tagSize > dataView.byteLength) {
26110             Roo.log('Invalid Exif data: Invalid data offset.');
26111             return;
26112         }
26113         if (length === 1) {
26114             return tagType.getValue(dataView, dataOffset, littleEndian);
26115         }
26116         values = [];
26117         for (i = 0; i < length; i += 1) {
26118             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26119         }
26120         
26121         if (tagType.ascii) {
26122             str = '';
26123             // Concatenate the chars:
26124             for (i = 0; i < values.length; i += 1) {
26125                 c = values[i];
26126                 // Ignore the terminating NULL byte(s):
26127                 if (c === '\u0000') {
26128                     break;
26129                 }
26130                 str += c;
26131             }
26132             return str;
26133         }
26134         return values;
26135     }
26136     
26137 });
26138
26139 Roo.apply(Roo.bootstrap.UploadCropbox, {
26140     tags : {
26141         'Orientation': 0x0112
26142     },
26143     
26144     Orientation: {
26145             1: 0, //'top-left',
26146 //            2: 'top-right',
26147             3: 180, //'bottom-right',
26148 //            4: 'bottom-left',
26149 //            5: 'left-top',
26150             6: 90, //'right-top',
26151 //            7: 'right-bottom',
26152             8: 270 //'left-bottom'
26153     },
26154     
26155     exifTagTypes : {
26156         // byte, 8-bit unsigned int:
26157         1: {
26158             getValue: function (dataView, dataOffset) {
26159                 return dataView.getUint8(dataOffset);
26160             },
26161             size: 1
26162         },
26163         // ascii, 8-bit byte:
26164         2: {
26165             getValue: function (dataView, dataOffset) {
26166                 return String.fromCharCode(dataView.getUint8(dataOffset));
26167             },
26168             size: 1,
26169             ascii: true
26170         },
26171         // short, 16 bit int:
26172         3: {
26173             getValue: function (dataView, dataOffset, littleEndian) {
26174                 return dataView.getUint16(dataOffset, littleEndian);
26175             },
26176             size: 2
26177         },
26178         // long, 32 bit int:
26179         4: {
26180             getValue: function (dataView, dataOffset, littleEndian) {
26181                 return dataView.getUint32(dataOffset, littleEndian);
26182             },
26183             size: 4
26184         },
26185         // rational = two long values, first is numerator, second is denominator:
26186         5: {
26187             getValue: function (dataView, dataOffset, littleEndian) {
26188                 return dataView.getUint32(dataOffset, littleEndian) /
26189                     dataView.getUint32(dataOffset + 4, littleEndian);
26190             },
26191             size: 8
26192         },
26193         // slong, 32 bit signed int:
26194         9: {
26195             getValue: function (dataView, dataOffset, littleEndian) {
26196                 return dataView.getInt32(dataOffset, littleEndian);
26197             },
26198             size: 4
26199         },
26200         // srational, two slongs, first is numerator, second is denominator:
26201         10: {
26202             getValue: function (dataView, dataOffset, littleEndian) {
26203                 return dataView.getInt32(dataOffset, littleEndian) /
26204                     dataView.getInt32(dataOffset + 4, littleEndian);
26205             },
26206             size: 8
26207         }
26208     },
26209     
26210     footer : {
26211         STANDARD : [
26212             {
26213                 tag : 'div',
26214                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26215                 action : 'rotate-left',
26216                 cn : [
26217                     {
26218                         tag : 'button',
26219                         cls : 'btn btn-default',
26220                         html : '<i class="fa fa-undo"></i>'
26221                     }
26222                 ]
26223             },
26224             {
26225                 tag : 'div',
26226                 cls : 'btn-group roo-upload-cropbox-picture',
26227                 action : 'picture',
26228                 cn : [
26229                     {
26230                         tag : 'button',
26231                         cls : 'btn btn-default',
26232                         html : '<i class="fa fa-picture-o"></i>'
26233                     }
26234                 ]
26235             },
26236             {
26237                 tag : 'div',
26238                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26239                 action : 'rotate-right',
26240                 cn : [
26241                     {
26242                         tag : 'button',
26243                         cls : 'btn btn-default',
26244                         html : '<i class="fa fa-repeat"></i>'
26245                     }
26246                 ]
26247             }
26248         ],
26249         DOCUMENT : [
26250             {
26251                 tag : 'div',
26252                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26253                 action : 'rotate-left',
26254                 cn : [
26255                     {
26256                         tag : 'button',
26257                         cls : 'btn btn-default',
26258                         html : '<i class="fa fa-undo"></i>'
26259                     }
26260                 ]
26261             },
26262             {
26263                 tag : 'div',
26264                 cls : 'btn-group roo-upload-cropbox-download',
26265                 action : 'download',
26266                 cn : [
26267                     {
26268                         tag : 'button',
26269                         cls : 'btn btn-default',
26270                         html : '<i class="fa fa-download"></i>'
26271                     }
26272                 ]
26273             },
26274             {
26275                 tag : 'div',
26276                 cls : 'btn-group roo-upload-cropbox-crop',
26277                 action : 'crop',
26278                 cn : [
26279                     {
26280                         tag : 'button',
26281                         cls : 'btn btn-default',
26282                         html : '<i class="fa fa-crop"></i>'
26283                     }
26284                 ]
26285             },
26286             {
26287                 tag : 'div',
26288                 cls : 'btn-group roo-upload-cropbox-trash',
26289                 action : 'trash',
26290                 cn : [
26291                     {
26292                         tag : 'button',
26293                         cls : 'btn btn-default',
26294                         html : '<i class="fa fa-trash"></i>'
26295                     }
26296                 ]
26297             },
26298             {
26299                 tag : 'div',
26300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26301                 action : 'rotate-right',
26302                 cn : [
26303                     {
26304                         tag : 'button',
26305                         cls : 'btn btn-default',
26306                         html : '<i class="fa fa-repeat"></i>'
26307                     }
26308                 ]
26309             }
26310         ],
26311         ROTATOR : [
26312             {
26313                 tag : 'div',
26314                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26315                 action : 'rotate-left',
26316                 cn : [
26317                     {
26318                         tag : 'button',
26319                         cls : 'btn btn-default',
26320                         html : '<i class="fa fa-undo"></i>'
26321                     }
26322                 ]
26323             },
26324             {
26325                 tag : 'div',
26326                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26327                 action : 'rotate-right',
26328                 cn : [
26329                     {
26330                         tag : 'button',
26331                         cls : 'btn btn-default',
26332                         html : '<i class="fa fa-repeat"></i>'
26333                     }
26334                 ]
26335             }
26336         ]
26337     }
26338 });
26339
26340 /*
26341 * Licence: LGPL
26342 */
26343
26344 /**
26345  * @class Roo.bootstrap.DocumentManager
26346  * @extends Roo.bootstrap.Component
26347  * Bootstrap DocumentManager class
26348  * @cfg {String} paramName default 'imageUpload'
26349  * @cfg {String} method default POST
26350  * @cfg {String} url action url
26351  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26352  * @cfg {Boolean} multiple multiple upload default true
26353  * @cfg {Number} thumbSize default 300
26354  * @cfg {String} fieldLabel
26355  * @cfg {Number} labelWidth default 4
26356  * @cfg {String} labelAlign (left|top) default left
26357  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26358  * 
26359  * @constructor
26360  * Create a new DocumentManager
26361  * @param {Object} config The config object
26362  */
26363
26364 Roo.bootstrap.DocumentManager = function(config){
26365     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26366     
26367     this.addEvents({
26368         /**
26369          * @event initial
26370          * Fire when initial the DocumentManager
26371          * @param {Roo.bootstrap.DocumentManager} this
26372          */
26373         "initial" : true,
26374         /**
26375          * @event inspect
26376          * inspect selected file
26377          * @param {Roo.bootstrap.DocumentManager} this
26378          * @param {File} file
26379          */
26380         "inspect" : true,
26381         /**
26382          * @event exception
26383          * Fire when xhr load exception
26384          * @param {Roo.bootstrap.DocumentManager} this
26385          * @param {XMLHttpRequest} xhr
26386          */
26387         "exception" : true,
26388         /**
26389          * @event prepare
26390          * prepare the form data
26391          * @param {Roo.bootstrap.DocumentManager} this
26392          * @param {Object} formData
26393          */
26394         "prepare" : true,
26395         /**
26396          * @event remove
26397          * Fire when remove the file
26398          * @param {Roo.bootstrap.DocumentManager} this
26399          * @param {Object} file
26400          */
26401         "remove" : true,
26402         /**
26403          * @event refresh
26404          * Fire after refresh the file
26405          * @param {Roo.bootstrap.DocumentManager} this
26406          */
26407         "refresh" : true,
26408         /**
26409          * @event click
26410          * Fire after click the image
26411          * @param {Roo.bootstrap.DocumentManager} this
26412          * @param {Object} file
26413          */
26414         "click" : true,
26415         /**
26416          * @event edit
26417          * Fire when upload a image and editable set to true
26418          * @param {Roo.bootstrap.DocumentManager} this
26419          * @param {Object} file
26420          */
26421         "edit" : true,
26422         /**
26423          * @event beforeselectfile
26424          * Fire before select file
26425          * @param {Roo.bootstrap.DocumentManager} this
26426          */
26427         "beforeselectfile" : true,
26428         /**
26429          * @event process
26430          * Fire before process file
26431          * @param {Roo.bootstrap.DocumentManager} this
26432          * @param {Object} file
26433          */
26434         "process" : true
26435         
26436     });
26437 };
26438
26439 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26440     
26441     boxes : 0,
26442     inputName : '',
26443     thumbSize : 300,
26444     multiple : true,
26445     files : [],
26446     method : 'POST',
26447     url : '',
26448     paramName : 'imageUpload',
26449     fieldLabel : '',
26450     labelWidth : 4,
26451     labelAlign : 'left',
26452     editable : true,
26453     delegates : [],
26454     
26455     
26456     xhr : false, 
26457     
26458     getAutoCreate : function()
26459     {   
26460         var managerWidget = {
26461             tag : 'div',
26462             cls : 'roo-document-manager',
26463             cn : [
26464                 {
26465                     tag : 'input',
26466                     cls : 'roo-document-manager-selector',
26467                     type : 'file'
26468                 },
26469                 {
26470                     tag : 'div',
26471                     cls : 'roo-document-manager-uploader',
26472                     cn : [
26473                         {
26474                             tag : 'div',
26475                             cls : 'roo-document-manager-upload-btn',
26476                             html : '<i class="fa fa-plus"></i>'
26477                         }
26478                     ]
26479                     
26480                 }
26481             ]
26482         };
26483         
26484         var content = [
26485             {
26486                 tag : 'div',
26487                 cls : 'column col-md-12',
26488                 cn : managerWidget
26489             }
26490         ];
26491         
26492         if(this.fieldLabel.length){
26493             
26494             content = [
26495                 {
26496                     tag : 'div',
26497                     cls : 'column col-md-12',
26498                     html : this.fieldLabel
26499                 },
26500                 {
26501                     tag : 'div',
26502                     cls : 'column col-md-12',
26503                     cn : managerWidget
26504                 }
26505             ];
26506
26507             if(this.labelAlign == 'left'){
26508                 content = [
26509                     {
26510                         tag : 'div',
26511                         cls : 'column col-md-' + this.labelWidth,
26512                         html : this.fieldLabel
26513                     },
26514                     {
26515                         tag : 'div',
26516                         cls : 'column col-md-' + (12 - this.labelWidth),
26517                         cn : managerWidget
26518                     }
26519                 ];
26520                 
26521             }
26522         }
26523         
26524         var cfg = {
26525             tag : 'div',
26526             cls : 'row clearfix',
26527             cn : content
26528         };
26529         
26530         return cfg;
26531         
26532     },
26533     
26534     initEvents : function()
26535     {
26536         this.managerEl = this.el.select('.roo-document-manager', true).first();
26537         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26538         
26539         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26540         this.selectorEl.hide();
26541         
26542         if(this.multiple){
26543             this.selectorEl.attr('multiple', 'multiple');
26544         }
26545         
26546         this.selectorEl.on('change', this.onFileSelected, this);
26547         
26548         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26549         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26550         
26551         this.uploader.on('click', this.onUploaderClick, this);
26552         
26553         this.renderProgressDialog();
26554         
26555         var _this = this;
26556         
26557         window.addEventListener("resize", function() { _this.refresh(); } );
26558         
26559         this.fireEvent('initial', this);
26560     },
26561     
26562     renderProgressDialog : function()
26563     {
26564         var _this = this;
26565         
26566         this.progressDialog = new Roo.bootstrap.Modal({
26567             cls : 'roo-document-manager-progress-dialog',
26568             allow_close : false,
26569             title : '',
26570             buttons : [
26571                 {
26572                     name  :'cancel',
26573                     weight : 'danger',
26574                     html : 'Cancel'
26575                 }
26576             ], 
26577             listeners : { 
26578                 btnclick : function() {
26579                     _this.uploadCancel();
26580                     this.hide();
26581                 }
26582             }
26583         });
26584          
26585         this.progressDialog.render(Roo.get(document.body));
26586          
26587         this.progress = new Roo.bootstrap.Progress({
26588             cls : 'roo-document-manager-progress',
26589             active : true,
26590             striped : true
26591         });
26592         
26593         this.progress.render(this.progressDialog.getChildContainer());
26594         
26595         this.progressBar = new Roo.bootstrap.ProgressBar({
26596             cls : 'roo-document-manager-progress-bar',
26597             aria_valuenow : 0,
26598             aria_valuemin : 0,
26599             aria_valuemax : 12,
26600             panel : 'success'
26601         });
26602         
26603         this.progressBar.render(this.progress.getChildContainer());
26604     },
26605     
26606     onUploaderClick : function(e)
26607     {
26608         e.preventDefault();
26609      
26610         if(this.fireEvent('beforeselectfile', this) != false){
26611             this.selectorEl.dom.click();
26612         }
26613         
26614     },
26615     
26616     onFileSelected : function(e)
26617     {
26618         e.preventDefault();
26619         
26620         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26621             return;
26622         }
26623         
26624         Roo.each(this.selectorEl.dom.files, function(file){
26625             if(this.fireEvent('inspect', this, file) != false){
26626                 this.files.push(file);
26627             }
26628         }, this);
26629         
26630         this.queue();
26631         
26632     },
26633     
26634     queue : function()
26635     {
26636         this.selectorEl.dom.value = '';
26637         
26638         if(!this.files.length){
26639             return;
26640         }
26641         
26642         if(this.boxes > 0 && this.files.length > this.boxes){
26643             this.files = this.files.slice(0, this.boxes);
26644         }
26645         
26646         this.uploader.show();
26647         
26648         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26649             this.uploader.hide();
26650         }
26651         
26652         var _this = this;
26653         
26654         var files = [];
26655         
26656         var docs = [];
26657         
26658         Roo.each(this.files, function(file){
26659             
26660             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26661                 var f = this.renderPreview(file);
26662                 files.push(f);
26663                 return;
26664             }
26665             
26666             if(file.type.indexOf('image') != -1){
26667                 this.delegates.push(
26668                     (function(){
26669                         _this.process(file);
26670                     }).createDelegate(this)
26671                 );
26672         
26673                 return;
26674             }
26675             
26676             docs.push(
26677                 (function(){
26678                     _this.process(file);
26679                 }).createDelegate(this)
26680             );
26681             
26682         }, this);
26683         
26684         this.files = files;
26685         
26686         this.delegates = this.delegates.concat(docs);
26687         
26688         if(!this.delegates.length){
26689             this.refresh();
26690             return;
26691         }
26692         
26693         this.progressBar.aria_valuemax = this.delegates.length;
26694         
26695         this.arrange();
26696         
26697         return;
26698     },
26699     
26700     arrange : function()
26701     {
26702         if(!this.delegates.length){
26703             this.progressDialog.hide();
26704             this.refresh();
26705             return;
26706         }
26707         
26708         var delegate = this.delegates.shift();
26709         
26710         this.progressDialog.show();
26711         
26712         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26713         
26714         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26715         
26716         delegate();
26717     },
26718     
26719     refresh : function()
26720     {
26721         this.uploader.show();
26722         
26723         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26724             this.uploader.hide();
26725         }
26726         
26727         Roo.isTouch ? this.closable(false) : this.closable(true);
26728         
26729         this.fireEvent('refresh', this);
26730     },
26731     
26732     onRemove : function(e, el, o)
26733     {
26734         e.preventDefault();
26735         
26736         this.fireEvent('remove', this, o);
26737         
26738     },
26739     
26740     remove : function(o)
26741     {
26742         var files = [];
26743         
26744         Roo.each(this.files, function(file){
26745             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26746                 files.push(file);
26747                 return;
26748             }
26749
26750             o.target.remove();
26751
26752         }, this);
26753         
26754         this.files = files;
26755         
26756         this.refresh();
26757     },
26758     
26759     clear : function()
26760     {
26761         Roo.each(this.files, function(file){
26762             if(!file.target){
26763                 return;
26764             }
26765             
26766             file.target.remove();
26767
26768         }, this);
26769         
26770         this.files = [];
26771         
26772         this.refresh();
26773     },
26774     
26775     onClick : function(e, el, o)
26776     {
26777         e.preventDefault();
26778         
26779         this.fireEvent('click', this, o);
26780         
26781     },
26782     
26783     closable : function(closable)
26784     {
26785         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26786             
26787             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26788             
26789             if(closable){
26790                 el.show();
26791                 return;
26792             }
26793             
26794             el.hide();
26795             
26796         }, this);
26797     },
26798     
26799     xhrOnLoad : function(xhr)
26800     {
26801         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26802             el.remove();
26803         }, this);
26804         
26805         if (xhr.readyState !== 4) {
26806             this.arrange();
26807             this.fireEvent('exception', this, xhr);
26808             return;
26809         }
26810
26811         var response = Roo.decode(xhr.responseText);
26812         
26813         if(!response.success){
26814             this.arrange();
26815             this.fireEvent('exception', this, xhr);
26816             return;
26817         }
26818         
26819         var file = this.renderPreview(response.data);
26820         
26821         this.files.push(file);
26822         
26823         this.arrange();
26824         
26825     },
26826     
26827     xhrOnError : function(xhr)
26828     {
26829         Roo.log('xhr on error');
26830         
26831         var response = Roo.decode(xhr.responseText);
26832           
26833         Roo.log(response);
26834         
26835         this.arrange();
26836     },
26837     
26838     process : function(file)
26839     {
26840         if(this.fireEvent('process', this, file) !== false){
26841             if(this.editable && file.type.indexOf('image') != -1){
26842                 this.fireEvent('edit', this, file);
26843                 return;
26844             }
26845
26846             this.uploadStart(file, false);
26847
26848             return;
26849         }
26850         
26851     },
26852     
26853     uploadStart : function(file, crop)
26854     {
26855         this.xhr = new XMLHttpRequest();
26856         
26857         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26858             this.arrange();
26859             return;
26860         }
26861         
26862         file.xhr = this.xhr;
26863             
26864         this.managerEl.createChild({
26865             tag : 'div',
26866             cls : 'roo-document-manager-loading',
26867             cn : [
26868                 {
26869                     tag : 'div',
26870                     tooltip : file.name,
26871                     cls : 'roo-document-manager-thumb',
26872                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26873                 }
26874             ]
26875
26876         });
26877
26878         this.xhr.open(this.method, this.url, true);
26879         
26880         var headers = {
26881             "Accept": "application/json",
26882             "Cache-Control": "no-cache",
26883             "X-Requested-With": "XMLHttpRequest"
26884         };
26885         
26886         for (var headerName in headers) {
26887             var headerValue = headers[headerName];
26888             if (headerValue) {
26889                 this.xhr.setRequestHeader(headerName, headerValue);
26890             }
26891         }
26892         
26893         var _this = this;
26894         
26895         this.xhr.onload = function()
26896         {
26897             _this.xhrOnLoad(_this.xhr);
26898         }
26899         
26900         this.xhr.onerror = function()
26901         {
26902             _this.xhrOnError(_this.xhr);
26903         }
26904         
26905         var formData = new FormData();
26906
26907         formData.append('returnHTML', 'NO');
26908         
26909         if(crop){
26910             formData.append('crop', crop);
26911         }
26912         
26913         formData.append(this.paramName, file, file.name);
26914         
26915         if(this.fireEvent('prepare', this, formData) != false){
26916             this.xhr.send(formData);
26917         };
26918     },
26919     
26920     uploadCancel : function()
26921     {
26922         if (this.xhr) {
26923             this.xhr.abort();
26924         }
26925         
26926         
26927         this.delegates = [];
26928         
26929         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26930             el.remove();
26931         }, this);
26932         
26933         this.arrange();
26934     },
26935     
26936     renderPreview : function(file)
26937     {
26938         if(typeof(file.target) != 'undefined' && file.target){
26939             return file;
26940         }
26941         
26942         var previewEl = this.managerEl.createChild({
26943             tag : 'div',
26944             cls : 'roo-document-manager-preview',
26945             cn : [
26946                 {
26947                     tag : 'div',
26948                     tooltip : file.filename,
26949                     cls : 'roo-document-manager-thumb',
26950                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26951                 },
26952                 {
26953                     tag : 'button',
26954                     cls : 'close',
26955                     html : '<i class="fa fa-times-circle"></i>'
26956                 }
26957             ]
26958         });
26959
26960         var close = previewEl.select('button.close', true).first();
26961
26962         close.on('click', this.onRemove, this, file);
26963
26964         file.target = previewEl;
26965
26966         var image = previewEl.select('img', true).first();
26967         
26968         var _this = this;
26969         
26970         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26971         
26972         image.on('click', this.onClick, this, file);
26973         
26974         return file;
26975         
26976     },
26977     
26978     onPreviewLoad : function(file, image)
26979     {
26980         if(typeof(file.target) == 'undefined' || !file.target){
26981             return;
26982         }
26983         
26984         var width = image.dom.naturalWidth || image.dom.width;
26985         var height = image.dom.naturalHeight || image.dom.height;
26986         
26987         if(width > height){
26988             file.target.addClass('wide');
26989             return;
26990         }
26991         
26992         file.target.addClass('tall');
26993         return;
26994         
26995     },
26996     
26997     uploadFromSource : function(file, crop)
26998     {
26999         this.xhr = new XMLHttpRequest();
27000         
27001         this.managerEl.createChild({
27002             tag : 'div',
27003             cls : 'roo-document-manager-loading',
27004             cn : [
27005                 {
27006                     tag : 'div',
27007                     tooltip : file.name,
27008                     cls : 'roo-document-manager-thumb',
27009                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27010                 }
27011             ]
27012
27013         });
27014
27015         this.xhr.open(this.method, this.url, true);
27016         
27017         var headers = {
27018             "Accept": "application/json",
27019             "Cache-Control": "no-cache",
27020             "X-Requested-With": "XMLHttpRequest"
27021         };
27022         
27023         for (var headerName in headers) {
27024             var headerValue = headers[headerName];
27025             if (headerValue) {
27026                 this.xhr.setRequestHeader(headerName, headerValue);
27027             }
27028         }
27029         
27030         var _this = this;
27031         
27032         this.xhr.onload = function()
27033         {
27034             _this.xhrOnLoad(_this.xhr);
27035         }
27036         
27037         this.xhr.onerror = function()
27038         {
27039             _this.xhrOnError(_this.xhr);
27040         }
27041         
27042         var formData = new FormData();
27043
27044         formData.append('returnHTML', 'NO');
27045         
27046         formData.append('crop', crop);
27047         
27048         if(typeof(file.filename) != 'undefined'){
27049             formData.append('filename', file.filename);
27050         }
27051         
27052         if(typeof(file.mimetype) != 'undefined'){
27053             formData.append('mimetype', file.mimetype);
27054         }
27055         
27056         if(this.fireEvent('prepare', this, formData) != false){
27057             this.xhr.send(formData);
27058         };
27059     }
27060 });
27061
27062 /*
27063 * Licence: LGPL
27064 */
27065
27066 /**
27067  * @class Roo.bootstrap.DocumentViewer
27068  * @extends Roo.bootstrap.Component
27069  * Bootstrap DocumentViewer class
27070  * 
27071  * @constructor
27072  * Create a new DocumentViewer
27073  * @param {Object} config The config object
27074  */
27075
27076 Roo.bootstrap.DocumentViewer = function(config){
27077     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27078     
27079     this.addEvents({
27080         /**
27081          * @event initial
27082          * Fire after initEvent
27083          * @param {Roo.bootstrap.DocumentViewer} this
27084          */
27085         "initial" : true,
27086         /**
27087          * @event click
27088          * Fire after click
27089          * @param {Roo.bootstrap.DocumentViewer} this
27090          */
27091         "click" : true,
27092         /**
27093          * @event trash
27094          * Fire after trash button
27095          * @param {Roo.bootstrap.DocumentViewer} this
27096          */
27097         "trash" : true
27098         
27099     });
27100 };
27101
27102 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27103     
27104     getAutoCreate : function()
27105     {
27106         var cfg = {
27107             tag : 'div',
27108             cls : 'roo-document-viewer',
27109             cn : [
27110                 {
27111                     tag : 'div',
27112                     cls : 'roo-document-viewer-body',
27113                     cn : [
27114                         {
27115                             tag : 'div',
27116                             cls : 'roo-document-viewer-thumb',
27117                             cn : [
27118                                 {
27119                                     tag : 'img',
27120                                     cls : 'roo-document-viewer-image'
27121                                 }
27122                             ]
27123                         }
27124                     ]
27125                 },
27126                 {
27127                     tag : 'div',
27128                     cls : 'roo-document-viewer-footer',
27129                     cn : {
27130                         tag : 'div',
27131                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27132                         cn : [
27133                             {
27134                                 tag : 'div',
27135                                 cls : 'btn-group',
27136                                 cn : [
27137                                     {
27138                                         tag : 'button',
27139                                         cls : 'btn btn-default roo-document-viewer-trash',
27140                                         html : '<i class="fa fa-trash"></i>'
27141                                     }
27142                                 ]
27143                             }
27144                         ]
27145                     }
27146                 }
27147             ]
27148         };
27149         
27150         return cfg;
27151     },
27152     
27153     initEvents : function()
27154     {
27155         
27156         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27157         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27158         
27159         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27160         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27161         
27162         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27163         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27164         
27165         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27166         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27167         
27168         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27169         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27170         
27171         this.bodyEl.on('click', this.onClick, this);
27172         
27173         this.trashBtn.on('click', this.onTrash, this);
27174         
27175     },
27176     
27177     initial : function()
27178     {
27179 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27180         
27181         
27182         this.fireEvent('initial', this);
27183         
27184     },
27185     
27186     onClick : function(e)
27187     {
27188         e.preventDefault();
27189         
27190         this.fireEvent('click', this);
27191     },
27192     
27193     onTrash : function(e)
27194     {
27195         e.preventDefault();
27196         
27197         this.fireEvent('trash', this);
27198     }
27199     
27200 });
27201 /*
27202  * - LGPL
27203  *
27204  * nav progress bar
27205  * 
27206  */
27207
27208 /**
27209  * @class Roo.bootstrap.NavProgressBar
27210  * @extends Roo.bootstrap.Component
27211  * Bootstrap NavProgressBar class
27212  * 
27213  * @constructor
27214  * Create a new nav progress bar
27215  * @param {Object} config The config object
27216  */
27217
27218 Roo.bootstrap.NavProgressBar = function(config){
27219     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27220
27221     this.bullets = this.bullets || [];
27222    
27223 //    Roo.bootstrap.NavProgressBar.register(this);
27224      this.addEvents({
27225         /**
27226              * @event changed
27227              * Fires when the active item changes
27228              * @param {Roo.bootstrap.NavProgressBar} this
27229              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27230              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27231          */
27232         'changed': true
27233      });
27234     
27235 };
27236
27237 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27238     
27239     bullets : [],
27240     barItems : [],
27241     
27242     getAutoCreate : function()
27243     {
27244         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27245         
27246         cfg = {
27247             tag : 'div',
27248             cls : 'roo-navigation-bar-group',
27249             cn : [
27250                 {
27251                     tag : 'div',
27252                     cls : 'roo-navigation-top-bar'
27253                 },
27254                 {
27255                     tag : 'div',
27256                     cls : 'roo-navigation-bullets-bar',
27257                     cn : [
27258                         {
27259                             tag : 'ul',
27260                             cls : 'roo-navigation-bar'
27261                         }
27262                     ]
27263                 },
27264                 
27265                 {
27266                     tag : 'div',
27267                     cls : 'roo-navigation-bottom-bar'
27268                 }
27269             ]
27270             
27271         };
27272         
27273         return cfg;
27274         
27275     },
27276     
27277     initEvents: function() 
27278     {
27279         
27280     },
27281     
27282     onRender : function(ct, position) 
27283     {
27284         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27285         
27286         if(this.bullets.length){
27287             Roo.each(this.bullets, function(b){
27288                this.addItem(b);
27289             }, this);
27290         }
27291         
27292         this.format();
27293         
27294     },
27295     
27296     addItem : function(cfg)
27297     {
27298         var item = new Roo.bootstrap.NavProgressItem(cfg);
27299         
27300         item.parentId = this.id;
27301         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27302         
27303         if(cfg.html){
27304             var top = new Roo.bootstrap.Element({
27305                 tag : 'div',
27306                 cls : 'roo-navigation-bar-text'
27307             });
27308             
27309             var bottom = new Roo.bootstrap.Element({
27310                 tag : 'div',
27311                 cls : 'roo-navigation-bar-text'
27312             });
27313             
27314             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27315             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27316             
27317             var topText = new Roo.bootstrap.Element({
27318                 tag : 'span',
27319                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27320             });
27321             
27322             var bottomText = new Roo.bootstrap.Element({
27323                 tag : 'span',
27324                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27325             });
27326             
27327             topText.onRender(top.el, null);
27328             bottomText.onRender(bottom.el, null);
27329             
27330             item.topEl = top;
27331             item.bottomEl = bottom;
27332         }
27333         
27334         this.barItems.push(item);
27335         
27336         return item;
27337     },
27338     
27339     getActive : function()
27340     {
27341         var active = false;
27342         
27343         Roo.each(this.barItems, function(v){
27344             
27345             if (!v.isActive()) {
27346                 return;
27347             }
27348             
27349             active = v;
27350             return false;
27351             
27352         });
27353         
27354         return active;
27355     },
27356     
27357     setActiveItem : function(item)
27358     {
27359         var prev = false;
27360         
27361         Roo.each(this.barItems, function(v){
27362             if (v.rid == item.rid) {
27363                 return ;
27364             }
27365             
27366             if (v.isActive()) {
27367                 v.setActive(false);
27368                 prev = v;
27369             }
27370         });
27371
27372         item.setActive(true);
27373         
27374         this.fireEvent('changed', this, item, prev);
27375     },
27376     
27377     getBarItem: function(rid)
27378     {
27379         var ret = false;
27380         
27381         Roo.each(this.barItems, function(e) {
27382             if (e.rid != rid) {
27383                 return;
27384             }
27385             
27386             ret =  e;
27387             return false;
27388         });
27389         
27390         return ret;
27391     },
27392     
27393     indexOfItem : function(item)
27394     {
27395         var index = false;
27396         
27397         Roo.each(this.barItems, function(v, i){
27398             
27399             if (v.rid != item.rid) {
27400                 return;
27401             }
27402             
27403             index = i;
27404             return false
27405         });
27406         
27407         return index;
27408     },
27409     
27410     setActiveNext : function()
27411     {
27412         var i = this.indexOfItem(this.getActive());
27413         
27414         if (i > this.barItems.length) {
27415             return;
27416         }
27417         
27418         this.setActiveItem(this.barItems[i+1]);
27419     },
27420     
27421     setActivePrev : function()
27422     {
27423         var i = this.indexOfItem(this.getActive());
27424         
27425         if (i  < 1) {
27426             return;
27427         }
27428         
27429         this.setActiveItem(this.barItems[i-1]);
27430     },
27431     
27432     format : function()
27433     {
27434         if(!this.barItems.length){
27435             return;
27436         }
27437      
27438         var width = 100 / this.barItems.length;
27439         
27440         Roo.each(this.barItems, function(i){
27441             i.el.setStyle('width', width + '%');
27442             i.topEl.el.setStyle('width', width + '%');
27443             i.bottomEl.el.setStyle('width', width + '%');
27444         }, this);
27445         
27446     }
27447     
27448 });
27449 /*
27450  * - LGPL
27451  *
27452  * Nav Progress Item
27453  * 
27454  */
27455
27456 /**
27457  * @class Roo.bootstrap.NavProgressItem
27458  * @extends Roo.bootstrap.Component
27459  * Bootstrap NavProgressItem class
27460  * @cfg {String} rid the reference id
27461  * @cfg {Boolean} active (true|false) Is item active default false
27462  * @cfg {Boolean} disabled (true|false) Is item active default false
27463  * @cfg {String} html
27464  * @cfg {String} position (top|bottom) text position default bottom
27465  * @cfg {String} icon show icon instead of number
27466  * 
27467  * @constructor
27468  * Create a new NavProgressItem
27469  * @param {Object} config The config object
27470  */
27471 Roo.bootstrap.NavProgressItem = function(config){
27472     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27473     this.addEvents({
27474         // raw events
27475         /**
27476          * @event click
27477          * The raw click event for the entire grid.
27478          * @param {Roo.bootstrap.NavProgressItem} this
27479          * @param {Roo.EventObject} e
27480          */
27481         "click" : true
27482     });
27483    
27484 };
27485
27486 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27487     
27488     rid : '',
27489     active : false,
27490     disabled : false,
27491     html : '',
27492     position : 'bottom',
27493     icon : false,
27494     
27495     getAutoCreate : function()
27496     {
27497         var iconCls = 'roo-navigation-bar-item-icon';
27498         
27499         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27500         
27501         var cfg = {
27502             tag: 'li',
27503             cls: 'roo-navigation-bar-item',
27504             cn : [
27505                 {
27506                     tag : 'i',
27507                     cls : iconCls
27508                 }
27509             ]
27510         };
27511         
27512         if(this.active){
27513             cfg.cls += ' active';
27514         }
27515         if(this.disabled){
27516             cfg.cls += ' disabled';
27517         }
27518         
27519         return cfg;
27520     },
27521     
27522     disable : function()
27523     {
27524         this.setDisabled(true);
27525     },
27526     
27527     enable : function()
27528     {
27529         this.setDisabled(false);
27530     },
27531     
27532     initEvents: function() 
27533     {
27534         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27535         
27536         this.iconEl.on('click', this.onClick, this);
27537     },
27538     
27539     onClick : function(e)
27540     {
27541         e.preventDefault();
27542         
27543         if(this.disabled){
27544             return;
27545         }
27546         
27547         if(this.fireEvent('click', this, e) === false){
27548             return;
27549         };
27550         
27551         this.parent().setActiveItem(this);
27552     },
27553     
27554     isActive: function () 
27555     {
27556         return this.active;
27557     },
27558     
27559     setActive : function(state)
27560     {
27561         if(this.active == state){
27562             return;
27563         }
27564         
27565         this.active = state;
27566         
27567         if (state) {
27568             this.el.addClass('active');
27569             return;
27570         }
27571         
27572         this.el.removeClass('active');
27573         
27574         return;
27575     },
27576     
27577     setDisabled : function(state)
27578     {
27579         if(this.disabled == state){
27580             return;
27581         }
27582         
27583         this.disabled = state;
27584         
27585         if (state) {
27586             this.el.addClass('disabled');
27587             return;
27588         }
27589         
27590         this.el.removeClass('disabled');
27591     },
27592     
27593     tooltipEl : function()
27594     {
27595         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27596     }
27597 });
27598  
27599
27600  /*
27601  * - LGPL
27602  *
27603  * FieldLabel
27604  * 
27605  */
27606
27607 /**
27608  * @class Roo.bootstrap.FieldLabel
27609  * @extends Roo.bootstrap.Component
27610  * Bootstrap FieldLabel class
27611  * @cfg {String} html contents of the element
27612  * @cfg {String} tag tag of the element default label
27613  * @cfg {String} cls class of the element
27614  * @cfg {String} target label target 
27615  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27616  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27617  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27618  * @cfg {String} iconTooltip default "This field is required"
27619  * 
27620  * @constructor
27621  * Create a new FieldLabel
27622  * @param {Object} config The config object
27623  */
27624
27625 Roo.bootstrap.FieldLabel = function(config){
27626     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27627     
27628     this.addEvents({
27629             /**
27630              * @event invalid
27631              * Fires after the field has been marked as invalid.
27632              * @param {Roo.form.FieldLabel} this
27633              * @param {String} msg The validation message
27634              */
27635             invalid : true,
27636             /**
27637              * @event valid
27638              * Fires after the field has been validated with no errors.
27639              * @param {Roo.form.FieldLabel} this
27640              */
27641             valid : true
27642         });
27643 };
27644
27645 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27646     
27647     tag: 'label',
27648     cls: '',
27649     html: '',
27650     target: '',
27651     allowBlank : true,
27652     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27653     validClass : 'text-success fa fa-lg fa-check',
27654     iconTooltip : 'This field is required',
27655     
27656     getAutoCreate : function(){
27657         
27658         var cfg = {
27659             tag : this.tag,
27660             cls : 'roo-bootstrap-field-label ' + this.cls,
27661             for : this.target,
27662             cn : [
27663                 {
27664                     tag : 'i',
27665                     cls : '',
27666                     tooltip : this.iconTooltip
27667                 },
27668                 {
27669                     tag : 'span',
27670                     html : this.html
27671                 }
27672             ] 
27673         };
27674         
27675         return cfg;
27676     },
27677     
27678     initEvents: function() 
27679     {
27680         Roo.bootstrap.Element.superclass.initEvents.call(this);
27681         
27682         this.iconEl = this.el.select('i', true).first();
27683         
27684         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27685         
27686         Roo.bootstrap.FieldLabel.register(this);
27687     },
27688     
27689     /**
27690      * Mark this field as valid
27691      */
27692     markValid : function()
27693     {
27694         this.iconEl.show();
27695         
27696         this.iconEl.removeClass(this.invalidClass);
27697         
27698         this.iconEl.addClass(this.validClass);
27699         
27700         this.fireEvent('valid', this);
27701     },
27702     
27703     /**
27704      * Mark this field as invalid
27705      * @param {String} msg The validation message
27706      */
27707     markInvalid : function(msg)
27708     {
27709         this.iconEl.show();
27710         
27711         this.iconEl.removeClass(this.validClass);
27712         
27713         this.iconEl.addClass(this.invalidClass);
27714         
27715         this.fireEvent('invalid', this, msg);
27716     }
27717     
27718    
27719 });
27720
27721 Roo.apply(Roo.bootstrap.FieldLabel, {
27722     
27723     groups: {},
27724     
27725      /**
27726     * register a FieldLabel Group
27727     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27728     */
27729     register : function(label)
27730     {
27731         if(this.groups.hasOwnProperty(label.target)){
27732             return;
27733         }
27734      
27735         this.groups[label.target] = label;
27736         
27737     },
27738     /**
27739     * fetch a FieldLabel Group based on the target
27740     * @param {string} target
27741     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27742     */
27743     get: function(target) {
27744         if (typeof(this.groups[target]) == 'undefined') {
27745             return false;
27746         }
27747         
27748         return this.groups[target] ;
27749     }
27750 });
27751
27752  
27753
27754  /*
27755  * - LGPL
27756  *
27757  * page DateSplitField.
27758  * 
27759  */
27760
27761
27762 /**
27763  * @class Roo.bootstrap.DateSplitField
27764  * @extends Roo.bootstrap.Component
27765  * Bootstrap DateSplitField class
27766  * @cfg {string} fieldLabel - the label associated
27767  * @cfg {Number} labelWidth set the width of label (0-12)
27768  * @cfg {String} labelAlign (top|left)
27769  * @cfg {Boolean} dayAllowBlank (true|false) default false
27770  * @cfg {Boolean} monthAllowBlank (true|false) default false
27771  * @cfg {Boolean} yearAllowBlank (true|false) default false
27772  * @cfg {string} dayPlaceholder 
27773  * @cfg {string} monthPlaceholder
27774  * @cfg {string} yearPlaceholder
27775  * @cfg {string} dayFormat default 'd'
27776  * @cfg {string} monthFormat default 'm'
27777  * @cfg {string} yearFormat default 'Y'
27778
27779  *     
27780  * @constructor
27781  * Create a new DateSplitField
27782  * @param {Object} config The config object
27783  */
27784
27785 Roo.bootstrap.DateSplitField = function(config){
27786     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27787     
27788     this.addEvents({
27789         // raw events
27790          /**
27791          * @event years
27792          * getting the data of years
27793          * @param {Roo.bootstrap.DateSplitField} this
27794          * @param {Object} years
27795          */
27796         "years" : true,
27797         /**
27798          * @event days
27799          * getting the data of days
27800          * @param {Roo.bootstrap.DateSplitField} this
27801          * @param {Object} days
27802          */
27803         "days" : true,
27804         /**
27805          * @event invalid
27806          * Fires after the field has been marked as invalid.
27807          * @param {Roo.form.Field} this
27808          * @param {String} msg The validation message
27809          */
27810         invalid : true,
27811        /**
27812          * @event valid
27813          * Fires after the field has been validated with no errors.
27814          * @param {Roo.form.Field} this
27815          */
27816         valid : true
27817     });
27818 };
27819
27820 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27821     
27822     fieldLabel : '',
27823     labelAlign : 'top',
27824     labelWidth : 3,
27825     dayAllowBlank : false,
27826     monthAllowBlank : false,
27827     yearAllowBlank : false,
27828     dayPlaceholder : '',
27829     monthPlaceholder : '',
27830     yearPlaceholder : '',
27831     dayFormat : 'd',
27832     monthFormat : 'm',
27833     yearFormat : 'Y',
27834     isFormField : true,
27835     
27836     getAutoCreate : function()
27837     {
27838         var cfg = {
27839             tag : 'div',
27840             cls : 'row roo-date-split-field-group',
27841             cn : [
27842                 {
27843                     tag : 'input',
27844                     type : 'hidden',
27845                     cls : 'form-hidden-field roo-date-split-field-group-value',
27846                     name : this.name
27847                 }
27848             ]
27849         };
27850         
27851         if(this.fieldLabel){
27852             cfg.cn.push({
27853                 tag : 'div',
27854                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27855                 cn : [
27856                     {
27857                         tag : 'label',
27858                         html : this.fieldLabel
27859                     }
27860                 ]
27861             });
27862         }
27863         
27864         Roo.each(['day', 'month', 'year'], function(t){
27865             cfg.cn.push({
27866                 tag : 'div',
27867                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27868             });
27869         }, this);
27870         
27871         return cfg;
27872     },
27873     
27874     inputEl: function ()
27875     {
27876         return this.el.select('.roo-date-split-field-group-value', true).first();
27877     },
27878     
27879     onRender : function(ct, position) 
27880     {
27881         var _this = this;
27882         
27883         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27884         
27885         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27886         
27887         this.dayField = new Roo.bootstrap.ComboBox({
27888             allowBlank : this.dayAllowBlank,
27889             alwaysQuery : true,
27890             displayField : 'value',
27891             editable : false,
27892             fieldLabel : '',
27893             forceSelection : true,
27894             mode : 'local',
27895             placeholder : this.dayPlaceholder,
27896             selectOnFocus : true,
27897             tpl : '<div class="select2-result"><b>{value}</b></div>',
27898             triggerAction : 'all',
27899             typeAhead : true,
27900             valueField : 'value',
27901             store : new Roo.data.SimpleStore({
27902                 data : (function() {    
27903                     var days = [];
27904                     _this.fireEvent('days', _this, days);
27905                     return days;
27906                 })(),
27907                 fields : [ 'value' ]
27908             }),
27909             listeners : {
27910                 select : function (_self, record, index)
27911                 {
27912                     _this.setValue(_this.getValue());
27913                 }
27914             }
27915         });
27916
27917         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27918         
27919         this.monthField = new Roo.bootstrap.MonthField({
27920             after : '<i class=\"fa fa-calendar\"></i>',
27921             allowBlank : this.monthAllowBlank,
27922             placeholder : this.monthPlaceholder,
27923             readOnly : true,
27924             listeners : {
27925                 render : function (_self)
27926                 {
27927                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27928                         e.preventDefault();
27929                         _self.focus();
27930                     });
27931                 },
27932                 select : function (_self, oldvalue, newvalue)
27933                 {
27934                     _this.setValue(_this.getValue());
27935                 }
27936             }
27937         });
27938         
27939         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27940         
27941         this.yearField = new Roo.bootstrap.ComboBox({
27942             allowBlank : this.yearAllowBlank,
27943             alwaysQuery : true,
27944             displayField : 'value',
27945             editable : false,
27946             fieldLabel : '',
27947             forceSelection : true,
27948             mode : 'local',
27949             placeholder : this.yearPlaceholder,
27950             selectOnFocus : true,
27951             tpl : '<div class="select2-result"><b>{value}</b></div>',
27952             triggerAction : 'all',
27953             typeAhead : true,
27954             valueField : 'value',
27955             store : new Roo.data.SimpleStore({
27956                 data : (function() {
27957                     var years = [];
27958                     _this.fireEvent('years', _this, years);
27959                     return years;
27960                 })(),
27961                 fields : [ 'value' ]
27962             }),
27963             listeners : {
27964                 select : function (_self, record, index)
27965                 {
27966                     _this.setValue(_this.getValue());
27967                 }
27968             }
27969         });
27970
27971         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27972     },
27973     
27974     setValue : function(v, format)
27975     {
27976         this.inputEl.dom.value = v;
27977         
27978         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27979         
27980         var d = Date.parseDate(v, f);
27981         
27982         if(!d){
27983             this.validate();
27984             return;
27985         }
27986         
27987         this.setDay(d.format(this.dayFormat));
27988         this.setMonth(d.format(this.monthFormat));
27989         this.setYear(d.format(this.yearFormat));
27990         
27991         this.validate();
27992         
27993         return;
27994     },
27995     
27996     setDay : function(v)
27997     {
27998         this.dayField.setValue(v);
27999         this.inputEl.dom.value = this.getValue();
28000         this.validate();
28001         return;
28002     },
28003     
28004     setMonth : function(v)
28005     {
28006         this.monthField.setValue(v, true);
28007         this.inputEl.dom.value = this.getValue();
28008         this.validate();
28009         return;
28010     },
28011     
28012     setYear : function(v)
28013     {
28014         this.yearField.setValue(v);
28015         this.inputEl.dom.value = this.getValue();
28016         this.validate();
28017         return;
28018     },
28019     
28020     getDay : function()
28021     {
28022         return this.dayField.getValue();
28023     },
28024     
28025     getMonth : function()
28026     {
28027         return this.monthField.getValue();
28028     },
28029     
28030     getYear : function()
28031     {
28032         return this.yearField.getValue();
28033     },
28034     
28035     getValue : function()
28036     {
28037         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28038         
28039         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28040         
28041         return date;
28042     },
28043     
28044     reset : function()
28045     {
28046         this.setDay('');
28047         this.setMonth('');
28048         this.setYear('');
28049         this.inputEl.dom.value = '';
28050         this.validate();
28051         return;
28052     },
28053     
28054     validate : function()
28055     {
28056         var d = this.dayField.validate();
28057         var m = this.monthField.validate();
28058         var y = this.yearField.validate();
28059         
28060         var valid = true;
28061         
28062         if(
28063                 (!this.dayAllowBlank && !d) ||
28064                 (!this.monthAllowBlank && !m) ||
28065                 (!this.yearAllowBlank && !y)
28066         ){
28067             valid = false;
28068         }
28069         
28070         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28071             return valid;
28072         }
28073         
28074         if(valid){
28075             this.markValid();
28076             return valid;
28077         }
28078         
28079         this.markInvalid();
28080         
28081         return valid;
28082     },
28083     
28084     markValid : function()
28085     {
28086         
28087         var label = this.el.select('label', true).first();
28088         var icon = this.el.select('i.fa-star', true).first();
28089
28090         if(label && icon){
28091             icon.remove();
28092         }
28093         
28094         this.fireEvent('valid', this);
28095     },
28096     
28097      /**
28098      * Mark this field as invalid
28099      * @param {String} msg The validation message
28100      */
28101     markInvalid : function(msg)
28102     {
28103         
28104         var label = this.el.select('label', true).first();
28105         var icon = this.el.select('i.fa-star', true).first();
28106
28107         if(label && !icon){
28108             this.el.select('.roo-date-split-field-label', true).createChild({
28109                 tag : 'i',
28110                 cls : 'text-danger fa fa-lg fa-star',
28111                 tooltip : 'This field is required',
28112                 style : 'margin-right:5px;'
28113             }, label, true);
28114         }
28115         
28116         this.fireEvent('invalid', this, msg);
28117     },
28118     
28119     clearInvalid : function()
28120     {
28121         var label = this.el.select('label', true).first();
28122         var icon = this.el.select('i.fa-star', true).first();
28123
28124         if(label && icon){
28125             icon.remove();
28126         }
28127         
28128         this.fireEvent('valid', this);
28129     },
28130     
28131     getName: function()
28132     {
28133         return this.name;
28134     }
28135     
28136 });
28137
28138